diff --git a/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml b/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml index 51205f4d..ca41355a 100644 --- a/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml +++ b/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml @@ -262,6 +262,17 @@ Singleton { return "profile_" + Date.now() + "_" + Math.random().toString(36).slice(2, 9); } + function generateAutoProfileId(outputIdentifiers) { + const fp = outputSetFingerprint(outputIdentifiers); + let hash = 0; + for (let i = 0; i < fp.length; i++) { + const char = fp.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + } + const hashStr = (hash >>> 0).toString(16); + return "auto_" + hashStr; + } + function configFingerprint(configEntry) { return Object.keys(configEntry.outputs || {}).sort().join("+"); } @@ -282,39 +293,20 @@ Singleton { return null; } - function findConfigEntryByFingerprint(data, outputIdentifiers) { + function findConfigEntryByFingerprint(data, outputIdentifiers, autoOnly) { const targetKey = outputSetFingerprint(outputIdentifiers); const configs = data.configurations || []; for (let i = 0; i < configs.length; i++) { - if (configFingerprint(configs[i]) === targetKey) + if (configFingerprint(configs[i]) === targetKey) { + if (autoOnly && configs[i].name) + continue; return { entry: configs[i], index: i }; - } - return null; - } - - function findPartialConfigEntry(data, outputIdentifiers) { - const currentSet = new Set(outputIdentifiers); - const configs = data.configurations || []; - let bestEntry = null; - let bestCount = 0; - for (let i = 0; i < configs.length; i++) { - const cfgKeys = Object.keys(configs[i].outputs || {}); - if (cfgKeys.length === 0) - continue; - if (!cfgKeys.every(k => currentSet.has(k))) - continue; - if (cfgKeys.length > bestCount) { - bestCount = cfgKeys.length; - bestEntry = { - entry: configs[i], - index: i - }; } } - return bestEntry; + return null; } function getProfileMonitorInclusion(profileId) { @@ -503,15 +495,13 @@ Singleton { } }; const onWriteSuccess = () => { - SettingsData.setActiveDisplayProfile(CompositorService.compositor, configEntry.name ? configId : ""); + SettingsData.setActiveDisplayProfile(CompositorService.compositor, configId); if (isManual) { - WlrOutputService.requestState(); profilesLoading = false; profileActivated(configId, profileName); manualActivationTimer.restart(); - } else { - saveConfigEntry(configEntry); } + WlrOutputService.requestState(); }; switch (CompositorService.compositor) { @@ -591,6 +581,8 @@ Singleton { const currentKey = currentOutputSet.join("+"); for (const id in validatedProfiles) { const p = validatedProfiles[id]; + if (p.name === "") + continue; if (Object.keys(p.outputs || {}).sort().join("+") === currentKey) return id; } @@ -696,42 +688,57 @@ Singleton { onTriggered: root.manualActivation = false } + Timer { + id: autoSelectDebounceTimer + interval: 400 + onTriggered: { + if (root.hasPendingChanges) + return; + root.applyAutoConfig(); + } + } + function applyAutoConfig() { if (!profilesReady || !SettingsData.displayProfileAutoSelect || manualActivation || !currentOutputSet.length) return; readMonitorsJson(data => { - const match = findConfigEntryByFingerprint(data, currentOutputSet); + const match = findConfigEntryByFingerprint(data, currentOutputSet, true); if (match) { - applyConfigEntry(match.entry, match.entry.id, match.entry.name || "", false); + applyConfigEntry(match.entry, match.entry.id, "", false); return; } - const partial = findPartialConfigEntry(data, currentOutputSet); - const niriSettings = buildMergedNiriSettings(); - const hyprlandSettings = buildMergedHyprlandSettings(); - const mergedOutputs = buildOutputsWithPendingChanges(); - - const outputConfigs = partial ? JSON.parse(JSON.stringify(partial.entry.outputs || {})) : {}; - - for (const name in outputs) { - const outputId = getOutputIdentifier(outputs[name], name); - const alreadyCovered = Object.keys(outputConfigs).some(k => k === outputId); - if (!alreadyCovered) { - const od = mergedOutputs[name]; - if (od) - outputConfigs[outputId] = extractOutputNeutralConfig(name, od, niriSettings, hyprlandSettings); - } - } - - if (Object.keys(outputConfigs).length === 0) - return; - - const syntheticEntry = { - name: "", - outputs: outputConfigs - }; - applyConfigEntry(syntheticEntry, "", "", false); + const outputConfigs = buildCurrentOutputConfigs(); + const id = generateAutoProfileId(currentOutputSet); + const existingIdx = data.configurations.findIndex(c => c.id === id); + if (existingIdx >= 0) + data.configurations[existingIdx] = { + "id": id, + "name": "", + "outputs": outputConfigs + }; + else + data.configurations.push({ + "id": id, + "name": "", + "outputs": outputConfigs + }); + writeMonitorsJson(data, success => { + if (!success) + return; + const updated = JSON.parse(JSON.stringify(validatedProfiles)); + updated[id] = { + id: id, + name: "", + outputs: outputConfigs + }; + validatedProfiles = updated; + matchedProfile = ""; + const match = findConfigEntryById(data, id); + if (match) + applyConfigEntry(match.entry, id, "", false); + }); }); } @@ -748,22 +755,6 @@ Singleton { return outputConfigs; } - function saveConfigEntry(configEntry) { - if (!configEntry.id || !configEntry.name) - return; - readMonitorsJson(data => { - const match = findConfigEntryById(data, configEntry.id); - if (!match) - return; - data.configurations[match.index] = { - "id": configEntry.id, - "name": configEntry.name, - "outputs": configEntry.outputs - }; - writeMonitorsJson(data, null); - }); - } - function deleteDisconnectedOutput(outputName) { if (outputs[outputName]?.connected) return; @@ -831,7 +822,7 @@ Singleton { if (hasPendingChanges) clearPendingChanges(); currentOutputSet = newOutputSet; - applyAutoConfig(); + autoSelectDebounceTimer.restart(); } onSavedOutputsChanged: allOutputs = buildAllOutputsMap() onLastAppliedEntryChanged: allOutputs = buildAllOutputsMap() @@ -875,9 +866,12 @@ Singleton { if (CompositorService.isHyprland) { initHyprlandSettingsFromConfig(parsed); syncHyprlandVrrFromConfig(parsed); + syncHyprlandDisabledFromConfig(parsed); } - if (CompositorService.isNiri) + if (CompositorService.isNiri) { syncNiriVrrFromConfig(parsed); + syncNiriDisabledFromConfig(parsed); + } }); } @@ -954,6 +948,44 @@ Singleton { SettingsData.saveSettings(); } + function syncHyprlandDisabledFromConfig(parsedOutputs) { + const current = JSON.parse(JSON.stringify(SettingsData.hyprlandOutputSettings)); + let changed = false; + for (const outputName in parsedOutputs) { + const settings = parsedOutputs[outputName]?.hyprlandSettings; + const fromConfig = settings?.disabled ?? false; + const stored = current[outputName]?.disabled ?? false; + if (fromConfig === stored) + continue; + if (!current[outputName]) + current[outputName] = {}; + if (fromConfig) + current[outputName].disabled = true; + else + delete current[outputName].disabled; + changed = true; + } + if (changed) { + SettingsData.hyprlandOutputSettings = current; + SettingsData.saveSettings(); + } + } + + function syncNiriDisabledFromConfig(parsedOutputs) { + let changed = false; + for (const outputName in parsedOutputs) { + const output = parsedOutputs[outputName]; + const fromConfig = output.disabled ?? false; + const current = SettingsData.getNiriOutputSetting(outputName, "disabled", false); + if (current === fromConfig) + continue; + SettingsData.setNiriOutputSetting(outputName, "disabled", fromConfig || undefined); + changed = true; + } + if (changed) + SettingsData.saveSettings(); + } + function filterDisconnectedOnly(parsedOutputs) { const result = {}; const liveNames = Object.keys(outputs); @@ -998,6 +1030,15 @@ Singleton { const name = match[1]; const body = match[2]; + if (body.trim() === "off") { + result[name] = { + "name": name, + "disabled": true, + "logical": { "x": 0, "y": 0, "scale": 1.0, "transform": "Normal" } + }; + continue; + } + const modeMatch = body.match(/mode\s+"(\d+)x(\d+)@([\d.]+)"/); const posMatch = body.match(/position\s+x=(-?\d+)\s+y=(-?\d+)/); const scaleMatch = body.match(/scale\s+([\d.]+)/); @@ -1979,23 +2020,23 @@ Singleton { return block; } - function confirmChanges() { + function confirmChanges(profileId) { const outputConfigs = buildCurrentOutputConfigs(); - lastAppliedEntry = { - outputs: outputConfigs - }; + lastAppliedEntry = { outputs: outputConfigs }; - readMonitorsJson(data => { - const match = findConfigEntryByFingerprint(data, Object.keys(outputConfigs)); - if (!match || !match.entry.name) - return; - data.configurations[match.index] = { - "id": match.entry.id, - "name": match.entry.name, - "outputs": outputConfigs - }; - writeMonitorsJson(data, null); - }); + if (profileId) { + readMonitorsJson(data => { + const match = findConfigEntryById(data, profileId); + if (match) { + data.configurations[match.index] = { + "id": match.entry.id, + "name": match.entry.name || "", + "outputs": outputConfigs + }; + writeMonitorsJson(data, null); + } + }); + } clearPendingChanges(); changesConfirmed(); @@ -2148,10 +2189,22 @@ Singleton { }; } + function isOutputDisabled(outputName) { + if (!outputs[outputName]) + return false; + if (CompositorService.isHyprland) + return getHyprlandSetting(outputs[outputName], outputName, "disabled", false); + if (CompositorService.isNiri) + return getNiriSetting(outputs[outputName], outputName, "disabled", false); + return false; + } + function checkOverlap(testName, testX, testY, testW, testH) { for (const name in outputs) { if (name === testName) continue; + if (isOutputDisabled(name)) + continue; const output = outputs[name]; if (!output.logical) continue; @@ -2174,6 +2227,8 @@ Singleton { for (const name in outputs) { if (name === testName) continue; + if (isOutputDisabled(name)) + continue; const output = outputs[name]; if (!output.logical) continue; @@ -2250,7 +2305,7 @@ Singleton { } function findBestSnapPosition(testName, posX, posY, testW, testH) { - const outputNames = Object.keys(outputs).filter(n => n !== testName); + const outputNames = Object.keys(outputs).filter(n => n !== testName && !isOutputDisabled(n)); if (outputNames.length === 0) return Qt.point(posX, posY); diff --git a/quickshell/Modules/Settings/DisplayConfig/HyprlandOutputSettings.qml b/quickshell/Modules/Settings/DisplayConfig/HyprlandOutputSettings.qml index e8fdedf0..13cbc32f 100644 --- a/quickshell/Modules/Settings/DisplayConfig/HyprlandOutputSettings.qml +++ b/quickshell/Modules/Settings/DisplayConfig/HyprlandOutputSettings.qml @@ -70,6 +70,10 @@ Column { return DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "colorManagement", "auto"); } property bool isHdrMode: currentCm === "hdr" || currentCm === "hdredid" + property bool isDisabled: { + void (DisplayConfigState.pendingHyprlandChanges); + return DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "disabled", false); + } DankToggle { width: parent.width @@ -83,6 +87,7 @@ Column { DankDropdown { width: parent.width text: I18n.tr("Mirror Display") + enabled: !settingsColumn.isDisabled addHorizontalPadding: true property var otherOutputs: { @@ -112,6 +117,7 @@ Column { width: parent.width text: I18n.tr("10-bit Color") description: I18n.tr("Enable 10-bit color depth for wider color gamut and HDR support") + enabled: !settingsColumn.isDisabled checked: settingsColumn.is10Bit onToggled: checked => { if (checked) { @@ -139,6 +145,7 @@ Column { width: parent.width text: I18n.tr("Color Gamut") addHorizontalPadding: true + enabled: !settingsColumn.isDisabled currentValue: { DisplayConfigState.pendingHyprlandChanges; const val = DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "colorManagement", "auto"); @@ -254,6 +261,7 @@ Column { width: parent.width height: 40 placeholderText: "1.0 - 2.0" + enabled: !settingsColumn.isDisabled text: { DisplayConfigState.pendingHyprlandChanges; const val = DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "sdrBrightness", null); @@ -287,6 +295,7 @@ Column { width: parent.width height: 40 placeholderText: "0.5 - 1.5" + enabled: !settingsColumn.isDisabled text: { DisplayConfigState.pendingHyprlandChanges; const val = DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "sdrSaturation", null); diff --git a/quickshell/Modules/Settings/DisplayConfig/MonitorCanvas.qml b/quickshell/Modules/Settings/DisplayConfig/MonitorCanvas.qml index accdfd7b..54dd01f1 100644 --- a/quickshell/Modules/Settings/DisplayConfig/MonitorCanvas.qml +++ b/quickshell/Modules/Settings/DisplayConfig/MonitorCanvas.qml @@ -1,15 +1,26 @@ import QtQuick import qs.Common +import qs.Services Rectangle { id: root property var filteredOutputs: { + void (DisplayConfigState.pendingHyprlandChanges); + void (DisplayConfigState.pendingNiriChanges); const all = DisplayConfigState.allOutputs || {}; const keys = Object.keys(all); - if (SettingsData.displayShowDisconnected) - return keys; - return keys.filter(k => all[k]?.connected); + return keys.filter(k => { + const od = all[k]; + const isConnected = od?.connected ?? false; + if (!isConnected) + return SettingsData.displayShowDisconnected; + if (CompositorService.isHyprland && DisplayConfigState.getHyprlandSetting(od, k, "disabled", false)) + return false; + if (CompositorService.isNiri && DisplayConfigState.getNiriSetting(od, k, "disabled", false)) + return false; + return true; + }); } property var filteredBounds: { diff --git a/quickshell/Modules/Settings/DisplayConfig/NiriOutputSettings.qml b/quickshell/Modules/Settings/DisplayConfig/NiriOutputSettings.qml index bd80b639..c0d462e6 100644 --- a/quickshell/Modules/Settings/DisplayConfig/NiriOutputSettings.qml +++ b/quickshell/Modules/Settings/DisplayConfig/NiriOutputSettings.qml @@ -53,10 +53,15 @@ Column { } Column { + id: settingsColumn width: parent.width spacing: Theme.spacingS visible: root.expanded topPadding: Theme.spacingS + property bool isDisabled: { + void (DisplayConfigState.pendingNiriChanges); + return DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "disabled", false); + } DankToggle { width: parent.width @@ -70,6 +75,7 @@ Column { DankToggle { width: parent.width text: I18n.tr("Focus at Startup") + enabled: !settingsColumn.isDisabled checked: DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "focusAtStartup", false) onToggled: checked => DisplayConfigState.setNiriSetting(root.outputData, root.outputName, "focusAtStartup", checked) } @@ -78,6 +84,7 @@ Column { width: parent.width text: I18n.tr("Hot Corners") addHorizontalPadding: true + enabled: !settingsColumn.isDisabled property var hotCornersData: { void (DisplayConfigState.pendingNiriChanges); @@ -139,6 +146,7 @@ Column { anchors.horizontalCenter: parent.horizontalCenter selectionMode: "multi" checkEnabled: false + enabled: !settingsColumn.isDisabled buttonHeight: 32 buttonPadding: parent.width < 400 ? Theme.spacingXS : Theme.spacingM minButtonWidth: parent.width < 400 ? 28 : 56 @@ -225,6 +233,7 @@ Column { width: parent.width height: 40 placeholderText: I18n.tr("Inherit") + enabled: !settingsColumn.isDisabled text: { const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null); if (layout?.gaps === undefined) @@ -262,6 +271,7 @@ Column { width: parent.width height: 40 placeholderText: I18n.tr("Inherit") + enabled: !settingsColumn.isDisabled text: { const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null); if (!layout?.defaultColumnWidth) @@ -312,6 +322,7 @@ Column { width: parent.width height: 40 placeholderText: I18n.tr("Inherit") + enabled: !settingsColumn.isDisabled text: { const layout = DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null); const presets = layout?.presetColumnWidths || []; @@ -354,6 +365,7 @@ Column { DankToggle { width: parent.width text: I18n.tr("Center Single Column") + enabled: !settingsColumn.isDisabled property var layoutData: DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "layout", null) checked: layoutData?.alwaysCenterSingleColumn ?? false onToggled: checked => { diff --git a/quickshell/Modules/Settings/DisplayConfig/OutputCard.qml b/quickshell/Modules/Settings/DisplayConfig/OutputCard.qml index 6d14449c..96367d81 100644 --- a/quickshell/Modules/Settings/DisplayConfig/OutputCard.qml +++ b/quickshell/Modules/Settings/DisplayConfig/OutputCard.qml @@ -12,6 +12,17 @@ StyledRect { required property string outputName required property var outputData property bool isConnected: outputData?.connected ?? false + property bool isDisabled: { + void (DisplayConfigState.pendingHyprlandChanges); + void (DisplayConfigState.pendingNiriChanges); + if (!root.isConnected) + return false; + if (CompositorService.isHyprland) + return DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "disabled", false); + if (CompositorService.isNiri) + return DisplayConfigState.getNiriSetting(root.outputData, root.outputName, "disabled", false); + return false; + } width: parent.width height: settingsColumn.implicitHeight + Theme.spacingM * 2 @@ -19,7 +30,7 @@ StyledRect { color: Theme.withAlpha(Theme.surfaceContainerHigh, isConnected ? 0.5 : 0.3) border.color: Theme.withAlpha(Theme.outline, 0.3) border.width: 1 - opacity: isConnected ? 1.0 : 0.7 + opacity: isConnected ? (isDisabled ? 0.5 : 1.0) : 0.7 Column { id: settingsColumn @@ -32,14 +43,14 @@ StyledRect { spacing: Theme.spacingM DankIcon { - name: root.isConnected ? "desktop_windows" : "desktop_access_disabled" + name: root.isConnected && !root.isDisabled ? "desktop_windows" : "desktop_access_disabled" size: Theme.iconSize - 4 - color: root.isConnected ? Theme.primary : Theme.surfaceVariantText + color: root.isConnected && !root.isDisabled ? Theme.primary : Theme.surfaceVariantText anchors.verticalCenter: parent.verticalCenter } Column { - width: parent.width - Theme.iconSize - Theme.spacingM - (disconnectedBadge.visible ? disconnectedBadge.width + deleteButton.width + Theme.spacingS * 2 : 0) + width: parent.width - Theme.iconSize - Theme.spacingM - (disconnectedBadge.visible ? disconnectedBadge.width + deleteButton.width + Theme.spacingS * 2 : disabledBadge.visible ? disabledBadge.width + Theme.spacingS : 0) spacing: 2 StyledText { @@ -102,12 +113,30 @@ StyledRect { onClicked: DisplayConfigState.deleteDisconnectedOutput(root.outputName) } } + + Rectangle { + id: disabledBadge + visible: root.isDisabled + width: disabledText.implicitWidth + Theme.spacingM + height: disabledText.implicitHeight + Theme.spacingXS + radius: height / 2 + color: Theme.withAlpha(Theme.outline, 0.3) + anchors.verticalCenter: parent.verticalCenter + + StyledText { + id: disabledText + text: I18n.tr("Disabled") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + anchors.centerIn: parent + } + } } DankDropdown { width: parent.width text: I18n.tr("Resolution & Refresh") - visible: root.isConnected + visible: root.isConnected && !root.isDisabled currentValue: { const pendingMode = DisplayConfigState.getPendingValue(root.outputName, "mode"); if (pendingMode) @@ -141,10 +170,20 @@ StyledRect { horizontalAlignment: Text.AlignLeft } + StyledText { + visible: root.isDisabled + text: I18n.tr("This output is disabled in the current profile") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + wrapMode: Text.WordWrap + width: parent.width + horizontalAlignment: Text.AlignLeft + } + Row { width: parent.width spacing: Theme.spacingM - visible: root.isConnected + visible: root.isConnected && !root.isDisabled Column { width: (parent.width - Theme.spacingM) / 2 @@ -262,7 +301,7 @@ StyledRect { DankToggle { width: parent.width text: I18n.tr("Variable Refresh Rate") - visible: root.isConnected && !CompositorService.isDwl && !CompositorService.isHyprland && !CompositorService.isNiri && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false) + visible: root.isConnected && !root.isDisabled && !CompositorService.isDwl && !CompositorService.isHyprland && !CompositorService.isNiri && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false) checked: { const pendingVrr = DisplayConfigState.getPendingValue(root.outputName, "vrr"); if (pendingVrr !== undefined) @@ -275,7 +314,7 @@ StyledRect { DankDropdown { width: parent.width text: I18n.tr("Variable Refresh Rate") - visible: root.isConnected && CompositorService.isHyprland && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false) + visible: root.isConnected && !root.isDisabled && CompositorService.isHyprland && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false) options: [I18n.tr("Off"), I18n.tr("On"), I18n.tr("Fullscreen Only")] currentValue: { DisplayConfigState.pendingHyprlandChanges; @@ -298,7 +337,7 @@ StyledRect { DankDropdown { width: parent.width text: I18n.tr("Variable Refresh Rate") - visible: root.isConnected && CompositorService.isNiri && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false) + visible: root.isConnected && !root.isDisabled && CompositorService.isNiri && (DisplayConfigState.outputs[root.outputName]?.vrr_supported ?? false) options: [I18n.tr("Off"), I18n.tr("On"), I18n.tr("On-Demand")] currentValue: { DisplayConfigState.pendingNiriChanges; diff --git a/quickshell/Modules/Settings/DisplayConfigTab.qml b/quickshell/Modules/Settings/DisplayConfigTab.qml index f7977f44..925680dd 100644 --- a/quickshell/Modules/Settings/DisplayConfigTab.qml +++ b/quickshell/Modules/Settings/DisplayConfigTab.qml @@ -11,7 +11,15 @@ Item { LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true - property string selectedProfileId: SettingsData.getActiveDisplayProfile(CompositorService.compositor) + property string selectedProfileId: { + const id = SettingsData.activeDisplayProfile[CompositorService.compositor] || ""; + if (!SettingsData.displayProfileAutoSelect) { + const profile = DisplayConfigState.validatedProfiles[id]; + if (profile && profile.name === "") + return ""; + } + return id; + } property bool showNewProfileDialog: false property bool showDeleteConfirmDialog: false property bool showRenameDialog: false @@ -60,15 +68,12 @@ Item { function onChangesReverted() { } function onProfileActivated(profileId, profileName) { - root.selectedProfileId = profileId; ToastService.showInfo(I18n.tr("Profile activated: %1").arg(profileName)); } function onProfileSaved(profileId, profileName) { - root.selectedProfileId = profileId; ToastService.showInfo(I18n.tr("Profile saved: %1").arg(profileName)); } function onProfileDeleted(profileId) { - root.selectedProfileId = SettingsData.getActiveDisplayProfile(CompositorService.compositor); ToastService.showInfo(I18n.tr("Profile deleted")); } function onProfileError(message) { @@ -163,6 +168,8 @@ Item { checked: SettingsData.displayProfileAutoSelect onToggled: checked => { SettingsData.displayProfileAutoSelect = checked; + if (!checked) + SettingsData.setActiveDisplayProfile(CompositorService.compositor, ""); SettingsData.saveSettings(); if (checked) DisplayConfigState.applyAutoConfig(); @@ -624,7 +631,7 @@ Item { DisplayConfirmationModal { id: confirmationModal - onConfirmed: DisplayConfigState.confirmChanges() + onConfirmed: DisplayConfigState.confirmChanges(root.selectedProfileId) onReverted: DisplayConfigState.revertChanges() } } diff --git a/quickshell/Services/NiriService.qml b/quickshell/Services/NiriService.qml index 1963be0d..da0f5668 100644 --- a/quickshell/Services/NiriService.qml +++ b/quickshell/Services/NiriService.qml @@ -1317,6 +1317,8 @@ Singleton { if (niriSettings.disabled) { kdlContent += ` off\n`; + kdlContent += `}\n\n`; + continue; } if (output.current_mode !== undefined && output.modes && output.modes[output.current_mode]) { diff --git a/quickshell/translations/en.json b/quickshell/translations/en.json index 7c9bc195..5bf4600d 100644 --- a/quickshell/translations/en.json +++ b/quickshell/translations/en.json @@ -2759,6 +2759,12 @@ "reference": "Modules/Settings/DisplayConfig/OutputCard.qml:136", "comment": "" }, + { + "term": "This output is disabled in the current profile", + "context": "This output is disabled in the current profile", + "reference": "Modules/Settings/DisplayConfig/OutputCard.qml", + "comment": "" + }, { "term": "Configure", "context": "Configure", diff --git a/quickshell/translations/template.json b/quickshell/translations/template.json index 75de473c..b8762f85 100644 --- a/quickshell/translations/template.json +++ b/quickshell/translations/template.json @@ -17638,5 +17638,12 @@ "context": "Keyboard hints when enter-to-paste is enabled", "reference": "", "comment": "" + }, + { + "term": "This output is disabled in the current profile", + "translation": "", + "context": "This output is disabled in the current profile", + "reference": "Modules/Settings/DisplayConfig/OutputCard.qml", + "comment": "" } ]