1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-27 05:25:19 -04:00
Files
DankMaterialShell/quickshell/Modules/Settings/WorkspaceAppearanceCard.qml
T
Maddison Cohodas 48f6a0c632 feat(bar): separate workspace appearance for unfocused displays (#2687)
* feat(bar): separate workspace appearance for unfocused displays

Add a toggle in the Workspace Appearance card to style workspace
indicators independently on displays that aren't currently focused.

The card is split into Focused Display and Unfocused Display(s) tabs.
When the new master toggle is off (default), unfocused displays inherit
the focused settings, preserving existing behavior. When on, unfocused
displays use their own colors (focused/occupied/unfocused/urgent) and
focused border (enable, color, thickness).

WorkspaceSwitcher resolves the focused monitor per compositor and routes
color/border resolution through isFocusedMonitor/useUnfocusedAppearance.

* refactor(bar): address review feedback for unfocused workspace appearance

- Consolidate focused-monitor lookup into BarWidgetService.getFocusedScreenName(),
  adding Mango support, and drop the duplicate compositor switches in
  WorkspaceSwitcher (effectiveScreenName + isFocusedMonitor now use the helper).
- Extract the shared color and border options into reusable
  WorkspaceAppearanceColorOptions and WorkspaceAppearanceBorderFields components
  so the focused and unfocused tabs no longer duplicate the layout.
- Gate the unfocused-display options on BarWidgetService.focusedScreenDetectionSupported
  and show an explanatory note on compositors without focus detection (e.g. labwc),
  instead of silently no-opping.
2026-06-26 23:34:24 -04:00

368 lines
15 KiB
QML

import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
SettingsCard {
id: root
iconName: "palette"
title: I18n.tr("Workspace Appearance")
settingKey: "workspaceAppearance"
tags: ["workspace", "focused", "color", "custom"]
collapsible: true
expanded: false
readonly property var focusedColorOptions: [({
"value": "default",
"label": I18n.tr("Primary", "workspace color option")
}), ({
"value": "primaryContainer",
"label": I18n.tr("Primary Container", "workspace color option")
}), ({
"value": "secondary",
"label": I18n.tr("Secondary", "workspace color option")
}), ({
"value": "secondaryContainer",
"label": I18n.tr("Secondary Container", "workspace color option")
}), ({
"value": "tertiary",
"label": I18n.tr("Tertiary", "workspace color option")
}), ({
"value": "tertiaryContainer",
"label": I18n.tr("Tertiary Container", "workspace color option")
}), ({
"value": "s",
"label": I18n.tr("Surface", "workspace color option")
}), ({
"value": "sc",
"label": I18n.tr("Surface Container", "workspace color option")
}), ({
"value": "sch",
"label": I18n.tr("Surface High", "workspace color option")
}), ({
"value": "schh",
"label": I18n.tr("Surface Highest", "workspace color option")
}), ({
"value": "none",
"label": I18n.tr("None", "workspace color option")
}), ({
"value": "custom",
"label": I18n.tr("Custom", "workspace color option")
})]
readonly property var occupiedColorOptions: [({
"value": "none",
"label": I18n.tr("None", "workspace color option")
}), ({
"value": "primary",
"label": I18n.tr("Primary", "workspace color option")
}), ({
"value": "primaryContainer",
"label": I18n.tr("Primary Container", "workspace color option")
}), ({
"value": "sec",
"label": I18n.tr("Secondary", "workspace color option")
}), ({
"value": "secondaryContainer",
"label": I18n.tr("Secondary Container", "workspace color option")
}), ({
"value": "tertiary",
"label": I18n.tr("Tertiary", "workspace color option")
}), ({
"value": "tertiaryContainer",
"label": I18n.tr("Tertiary Container", "workspace color option")
}), ({
"value": "s",
"label": I18n.tr("Surface", "workspace color option")
}), ({
"value": "sc",
"label": I18n.tr("Surface Container", "workspace color option")
}), ({
"value": "sch",
"label": I18n.tr("Surface High", "workspace color option")
}), ({
"value": "schh",
"label": I18n.tr("Surface Highest", "workspace color option")
}), ({
"value": "custom",
"label": I18n.tr("Custom", "workspace color option")
})]
readonly property var unfocusedColorOptions: [({
"value": "default",
"label": I18n.tr("Default", "workspace color option")
}), ({
"value": "surfaceText",
"label": I18n.tr("Surface Text", "workspace color option")
}), ({
"value": "primary",
"label": I18n.tr("Primary", "workspace color option")
}), ({
"value": "secondary",
"label": I18n.tr("Secondary", "workspace color option")
}), ({
"value": "tertiary",
"label": I18n.tr("Tertiary", "workspace color option")
}), ({
"value": "s",
"label": I18n.tr("Surface", "workspace color option")
}), ({
"value": "sc",
"label": I18n.tr("Surface Container", "workspace color option")
}), ({
"value": "sch",
"label": I18n.tr("Surface High", "workspace color option")
}), ({
"value": "schh",
"label": I18n.tr("Surface Highest", "workspace color option")
}), ({
"value": "custom",
"label": I18n.tr("Custom", "workspace color option")
})]
readonly property var urgentColorOptions: [({
"value": "default",
"label": I18n.tr("Error", "workspace color option")
}), ({
"value": "primary",
"label": I18n.tr("Primary", "workspace color option")
}), ({
"value": "primaryContainer",
"label": I18n.tr("Primary Container", "workspace color option")
}), ({
"value": "secondary",
"label": I18n.tr("Secondary", "workspace color option")
}), ({
"value": "secondaryContainer",
"label": I18n.tr("Secondary Container", "workspace color option")
}), ({
"value": "tertiary",
"label": I18n.tr("Tertiary", "workspace color option")
}), ({
"value": "tertiaryContainer",
"label": I18n.tr("Tertiary Container", "workspace color option")
}), ({
"value": "s",
"label": I18n.tr("Surface", "workspace color option")
}), ({
"value": "sc",
"label": I18n.tr("Surface Container", "workspace color option")
}), ({
"value": "sch",
"label": I18n.tr("Surface High", "workspace color option")
}), ({
"value": "custom",
"label": I18n.tr("Custom", "workspace color option")
})]
readonly property var borderColorOptions: [({
"value": "surfaceText",
"label": I18n.tr("Surface Text", "workspace color option")
}), ({
"value": "primary",
"label": I18n.tr("Primary", "workspace color option")
}), ({
"value": "primaryContainer",
"label": I18n.tr("Primary Container", "workspace color option")
}), ({
"value": "secondary",
"label": I18n.tr("Secondary", "workspace color option")
}), ({
"value": "secondaryContainer",
"label": I18n.tr("Secondary Container", "workspace color option")
}), ({
"value": "tertiary",
"label": I18n.tr("Tertiary", "workspace color option")
}), ({
"value": "tertiaryContainer",
"label": I18n.tr("Tertiary Container", "workspace color option")
}), ({
"value": "custom",
"label": I18n.tr("Custom", "workspace color option")
})]
readonly property bool workspaceStateColorsVisible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango
readonly property bool urgentWorkspaceColorsVisible: workspaceStateColorsVisible || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
function isFocusedAppearanceSection(section) {
return ["workspaceAppearance", "workspaceColorMode", "workspaceOccupiedColorMode", "workspaceUnfocusedColorMode", "workspaceUrgentColorMode", "workspaceFocusedBorderEnabled", "workspaceFocusedBorderColor", "workspaceFocusedBorderThickness"].includes(section);
}
Item {
width: parent.width
height: workspaceTabBar.height + Theme.spacingM
DankTabBar {
id: workspaceTabBar
width: parent.width
tabHeight: 44
showIcons: false
model: [({
"text": I18n.tr("Focused Display", "workspace appearance tab")
}), ({
"text": I18n.tr("Unfocused Display(s)", "workspace appearance tab")
})]
onTabClicked: index => currentIndex = index
Component.onCompleted: Qt.callLater(updateIndicator)
Connections {
target: SettingsSearchService
function onTargetSectionChanged() {
const section = SettingsSearchService.targetSection;
if (!section)
return;
if (section.startsWith("workspaceUnfocusedMonitor")) {
root.expanded = true;
workspaceTabBar.currentIndex = 1;
} else if (root.isFocusedAppearanceSection(section)) {
root.expanded = true;
workspaceTabBar.currentIndex = 0;
} else {
return;
}
Qt.callLater(workspaceTabBar.updateIndicator);
}
}
}
}
Column {
id: focusedTab
width: parent.width
spacing: Theme.spacingM
visible: workspaceTabBar.currentIndex === 0
WorkspaceAppearanceColorOptions {
focusedColorOptions: root.focusedColorOptions
occupiedColorOptions: root.occupiedColorOptions
unfocusedColorOptions: root.unfocusedColorOptions
urgentColorOptions: root.urgentColorOptions
occupiedColorVisible: root.workspaceStateColorsVisible
urgentColorVisible: root.urgentWorkspaceColorsVisible
focusedColorModeKey: "workspaceColorMode"
focusedCustomColorKey: "workspaceFocusedCustomColor"
occupiedColorModeKey: "workspaceOccupiedColorMode"
occupiedCustomColorKey: "workspaceOccupiedCustomColor"
unfocusedColorModeKey: "workspaceUnfocusedColorMode"
unfocusedCustomColorKey: "workspaceUnfocusedCustomColor"
urgentColorModeKey: "workspaceUrgentColorMode"
urgentCustomColorKey: "workspaceUrgentCustomColor"
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
SettingsToggleRow {
settingKey: "workspaceFocusedBorderEnabled"
tags: ["workspace", "border", "outline", "focused", "ring"]
text: I18n.tr("Focused Border")
description: I18n.tr("Show an outline ring around the focused workspace indicator")
checked: SettingsData.workspaceFocusedBorderEnabled
onToggled: checked => SettingsData.set("workspaceFocusedBorderEnabled", checked)
}
WorkspaceAppearanceBorderFields {
visible: SettingsData.workspaceFocusedBorderEnabled
borderColorOptions: root.borderColorOptions
borderColorKey: "workspaceFocusedBorderColor"
borderCustomColorKey: "workspaceFocusedBorderCustomColor"
borderThicknessKey: "workspaceFocusedBorderThickness"
}
}
Column {
id: unfocusedTab
width: parent.width
spacing: Theme.spacingM
visible: workspaceTabBar.currentIndex === 1
StyledText {
width: parent.width
visible: !BarWidgetService.focusedScreenDetectionSupported
text: I18n.tr("Separate appearance for unfocused displays is not supported on this compositor.")
wrapMode: Text.WordWrap
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeMedium
}
SettingsToggleRow {
visible: BarWidgetService.focusedScreenDetectionSupported
settingKey: "workspaceUnfocusedMonitorSeparateAppearance"
tags: ["workspace", "unfocused", "monitor", "display", "separate", "color"]
text: I18n.tr("Separate Appearance for Unfocused Display(s)")
description: I18n.tr("Use different workspace colors on displays that are not focused")
checked: SettingsData.workspaceUnfocusedMonitorSeparateAppearance
onToggled: checked => SettingsData.set("workspaceUnfocusedMonitorSeparateAppearance", checked)
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: BarWidgetService.focusedScreenDetectionSupported
}
Column {
id: unfocusedOptions
width: parent.width
spacing: Theme.spacingM
visible: BarWidgetService.focusedScreenDetectionSupported
enabled: SettingsData.workspaceUnfocusedMonitorSeparateAppearance
opacity: enabled ? 1 : 0.5
WorkspaceAppearanceColorOptions {
focusedColorOptions: root.focusedColorOptions
occupiedColorOptions: root.occupiedColorOptions
unfocusedColorOptions: root.unfocusedColorOptions
urgentColorOptions: root.urgentColorOptions
occupiedColorVisible: root.workspaceStateColorsVisible
urgentColorVisible: root.urgentWorkspaceColorsVisible
extraTags: ["unfocused", "monitor", "display"]
focusedColorModeKey: "workspaceUnfocusedMonitorColorMode"
focusedCustomColorKey: "workspaceUnfocusedMonitorFocusedCustomColor"
occupiedColorModeKey: "workspaceUnfocusedMonitorOccupiedColorMode"
occupiedCustomColorKey: "workspaceUnfocusedMonitorOccupiedCustomColor"
unfocusedColorModeKey: "workspaceUnfocusedMonitorUnfocusedColorMode"
unfocusedCustomColorKey: "workspaceUnfocusedMonitorUnfocusedCustomColor"
urgentColorModeKey: "workspaceUnfocusedMonitorUrgentColorMode"
urgentCustomColorKey: "workspaceUnfocusedMonitorUrgentCustomColor"
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
SettingsToggleRow {
settingKey: "workspaceUnfocusedMonitorBorderEnabled"
tags: ["workspace", "border", "outline", "focused", "ring", "unfocused", "monitor", "display"]
text: I18n.tr("Focused Border")
description: I18n.tr("Show an outline ring around the focused workspace indicator")
checked: SettingsData.workspaceUnfocusedMonitorBorderEnabled
onToggled: checked => SettingsData.set("workspaceUnfocusedMonitorBorderEnabled", checked)
}
WorkspaceAppearanceBorderFields {
visible: SettingsData.workspaceUnfocusedMonitorBorderEnabled
borderColorOptions: root.borderColorOptions
extraTags: ["unfocused", "monitor", "display"]
borderColorKey: "workspaceUnfocusedMonitorBorderColor"
borderCustomColorKey: "workspaceUnfocusedMonitorBorderCustomColor"
borderThicknessKey: "workspaceUnfocusedMonitorBorderThickness"
}
}
}
}