1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-27 13:35:18 -04:00

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.
This commit is contained in:
Maddison Cohodas
2026-06-26 20:34:24 -07:00
committed by GitHub
parent 03d86f78f4
commit 48f6a0c632
8 changed files with 432 additions and 126 deletions
+13
View File
@@ -425,6 +425,19 @@ Singleton {
property string workspaceFocusedBorderColor: "primary" property string workspaceFocusedBorderColor: "primary"
property string workspaceFocusedBorderCustomColor: "#6750A4" property string workspaceFocusedBorderCustomColor: "#6750A4"
property int workspaceFocusedBorderThickness: 2 property int workspaceFocusedBorderThickness: 2
property bool workspaceUnfocusedMonitorSeparateAppearance: false
property string workspaceUnfocusedMonitorColorMode: "default"
property string workspaceUnfocusedMonitorFocusedCustomColor: "#6750A4"
property string workspaceUnfocusedMonitorOccupiedColorMode: "none"
property string workspaceUnfocusedMonitorOccupiedCustomColor: "#625B71"
property string workspaceUnfocusedMonitorUnfocusedColorMode: "default"
property string workspaceUnfocusedMonitorUnfocusedCustomColor: "#49454E"
property string workspaceUnfocusedMonitorUrgentColorMode: "default"
property string workspaceUnfocusedMonitorUrgentCustomColor: "#B3261E"
property bool workspaceUnfocusedMonitorBorderEnabled: false
property string workspaceUnfocusedMonitorBorderColor: "primary"
property string workspaceUnfocusedMonitorBorderCustomColor: "#6750A4"
property int workspaceUnfocusedMonitorBorderThickness: 2
property var workspaceNameIcons: ({}) property var workspaceNameIcons: ({})
property bool waveProgressEnabled: true property bool waveProgressEnabled: true
property bool scrollTitleEnabled: true property bool scrollTitleEnabled: true
@@ -163,6 +163,19 @@ var SPEC = {
workspaceFocusedBorderColor: { def: "primary" }, workspaceFocusedBorderColor: { def: "primary" },
workspaceFocusedBorderCustomColor: { def: "#6750A4" }, workspaceFocusedBorderCustomColor: { def: "#6750A4" },
workspaceFocusedBorderThickness: { def: 2 }, workspaceFocusedBorderThickness: { def: 2 },
workspaceUnfocusedMonitorSeparateAppearance: { def: false },
workspaceUnfocusedMonitorColorMode: { def: "default" },
workspaceUnfocusedMonitorFocusedCustomColor: { def: "#6750A4" },
workspaceUnfocusedMonitorOccupiedColorMode: { def: "none" },
workspaceUnfocusedMonitorOccupiedCustomColor: { def: "#625B71" },
workspaceUnfocusedMonitorUnfocusedColorMode: { def: "default" },
workspaceUnfocusedMonitorUnfocusedCustomColor: { def: "#49454E" },
workspaceUnfocusedMonitorUrgentColorMode: { def: "default" },
workspaceUnfocusedMonitorUrgentCustomColor: { def: "#B3261E" },
workspaceUnfocusedMonitorBorderEnabled: { def: false },
workspaceUnfocusedMonitorBorderColor: { def: "primary" },
workspaceUnfocusedMonitorBorderCustomColor: { def: "#6750A4" },
workspaceUnfocusedMonitorBorderThickness: { def: 2 },
workspaceNameIcons: { def: {} }, workspaceNameIcons: { def: {} },
waveProgressEnabled: { def: true }, waveProgressEnabled: { def: true },
scrollTitleEnabled: { def: true }, scrollTitleEnabled: { def: true },
@@ -71,25 +71,16 @@ Item {
readonly property string effectiveScreenName: { readonly property string effectiveScreenName: {
if (!SettingsData.workspaceFollowFocus) if (!SettingsData.workspaceFollowFocus)
return root.screenName; return root.screenName;
return BarWidgetService.getFocusedScreenName() || root.screenName;
switch (CompositorService.compositor) {
case "niri":
return NiriService.currentOutput || root.screenName;
case "hyprland":
return Hyprland.focusedWorkspace?.monitor?.name || root.screenName;
case "mango":
return MangoService.activeOutput || root.screenName;
case "sway":
case "scroll":
case "miracle":
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
return focusedWs?.monitor?.name || root.screenName;
default:
return root.screenName;
}
} }
readonly property bool mangoOverviewActive: CompositorService.isMango && MangoService.isOutputInOverview(effectiveScreenName) readonly property bool mangoOverviewActive: CompositorService.isMango && MangoService.isOutputInOverview(effectiveScreenName)
readonly property bool isFocusedMonitor: {
const focused = BarWidgetService.getFocusedScreenName();
return focused === "" || root.screenName === "" || focused === root.screenName;
}
readonly property bool useUnfocusedAppearance: !isFocusedMonitor && SettingsData.workspaceUnfocusedMonitorSeparateAppearance && BarWidgetService.focusedScreenDetectionSupported
readonly property var extProjection: (useExtWorkspace && parentScreen) ? WindowManager.screenProjection(parentScreen) : null readonly property var extProjection: (useExtWorkspace && parentScreen) ? WindowManager.screenProjection(parentScreen) : null
readonly property bool useExtWorkspace: { readonly property bool useExtWorkspace: {
if (Quickshell.env("DMS_FORCE_EXTWS") === "1") if (Quickshell.env("DMS_FORCE_EXTWS") === "1")
@@ -1231,23 +1222,36 @@ Item {
} }
} }
readonly property color unfocusedColor: colorFromMode(SettingsData.workspaceUnfocusedColorMode, Theme.surfaceTextAlpha, SettingsData.workspaceUnfocusedCustomColor, Theme.surfaceTextAlpha) function effectiveColorMode(focusedMode, unfocusedMode) {
return root.useUnfocusedAppearance ? unfocusedMode : focusedMode;
}
function effectiveCustomColor(focusedCustom, unfocusedCustom) {
return root.useUnfocusedAppearance ? unfocusedCustom : focusedCustom;
}
readonly property color unfocusedColor: colorFromMode(effectiveColorMode(SettingsData.workspaceUnfocusedColorMode, SettingsData.workspaceUnfocusedMonitorUnfocusedColorMode), Theme.surfaceTextAlpha, effectiveCustomColor(SettingsData.workspaceUnfocusedCustomColor, SettingsData.workspaceUnfocusedMonitorUnfocusedCustomColor), Theme.surfaceTextAlpha)
readonly property color activeColor: { readonly property color activeColor: {
if (SettingsData.workspaceColorMode === "none") const mode = effectiveColorMode(SettingsData.workspaceColorMode, SettingsData.workspaceUnfocusedMonitorColorMode);
if (mode === "none")
return unfocusedColor; return unfocusedColor;
return colorFromMode(SettingsData.workspaceColorMode, Theme.primary, SettingsData.workspaceFocusedCustomColor, Theme.primary); return colorFromMode(mode, Theme.primary, effectiveCustomColor(SettingsData.workspaceFocusedCustomColor, SettingsData.workspaceUnfocusedMonitorFocusedCustomColor), Theme.primary);
} }
readonly property color occupiedColor: { readonly property color occupiedColor: {
if (SettingsData.workspaceOccupiedColorMode === "none") const mode = effectiveColorMode(SettingsData.workspaceOccupiedColorMode, SettingsData.workspaceUnfocusedMonitorOccupiedColorMode);
if (mode === "none")
return unfocusedColor; return unfocusedColor;
return colorFromMode(SettingsData.workspaceOccupiedColorMode, unfocusedColor, SettingsData.workspaceOccupiedCustomColor, Theme.secondary); return colorFromMode(mode, unfocusedColor, effectiveCustomColor(SettingsData.workspaceOccupiedCustomColor, SettingsData.workspaceUnfocusedMonitorOccupiedCustomColor), Theme.secondary);
} }
readonly property color urgentColor: colorFromMode(SettingsData.workspaceUrgentColorMode, Theme.error, SettingsData.workspaceUrgentCustomColor, Theme.error) readonly property color urgentColor: colorFromMode(effectiveColorMode(SettingsData.workspaceUrgentColorMode, SettingsData.workspaceUnfocusedMonitorUrgentColorMode), Theme.error, effectiveCustomColor(SettingsData.workspaceUrgentCustomColor, SettingsData.workspaceUnfocusedMonitorUrgentCustomColor), Theme.error)
readonly property color focusedBorderColor: colorFromMode(SettingsData.workspaceFocusedBorderColor, Theme.primary, SettingsData.workspaceFocusedBorderCustomColor, Theme.primary) readonly property color focusedBorderColor: colorFromMode(effectiveColorMode(SettingsData.workspaceFocusedBorderColor, SettingsData.workspaceUnfocusedMonitorBorderColor), Theme.primary, effectiveCustomColor(SettingsData.workspaceFocusedBorderCustomColor, SettingsData.workspaceUnfocusedMonitorBorderCustomColor), Theme.primary)
readonly property bool focusedBorderEnabledForMonitor: root.useUnfocusedAppearance ? SettingsData.workspaceUnfocusedMonitorBorderEnabled : SettingsData.workspaceFocusedBorderEnabled
readonly property int focusedBorderThicknessForMonitor: root.useUnfocusedAppearance ? SettingsData.workspaceUnfocusedMonitorBorderThickness : SettingsData.workspaceFocusedBorderThickness
function getContrastingIconColor(bgColor) { function getContrastingIconColor(bgColor) {
const luminance = 0.299 * bgColor.r + 0.587 * bgColor.g + 0.114 * bgColor.b; const luminance = 0.299 * bgColor.r + 0.587 * bgColor.g + 0.114 * bgColor.b;
@@ -1449,17 +1453,17 @@ Item {
x: root.isVertical ? (root.widgetHeight - width) / 2 : (parent.width - width) / 2 x: root.isVertical ? (root.widgetHeight - width) / 2 : (parent.width - width) / 2
y: root.isVertical ? (parent.height - height) / 2 : (root.widgetHeight - height) / 2 y: root.isVertical ? (parent.height - height) / 2 : (root.widgetHeight - height) / 2
width: { width: {
const borderWidth = (SettingsData.workspaceFocusedBorderEnabled && isActive && !isPlaceholder) ? SettingsData.workspaceFocusedBorderThickness : 0; const borderWidth = (delegateRoot.focusedBorderEnabledForMonitor && isActive && !isPlaceholder) ? delegateRoot.focusedBorderThicknessForMonitor : 0;
return delegateRoot.visualWidth + borderWidth * 2; return delegateRoot.visualWidth + borderWidth * 2;
} }
height: { height: {
const borderWidth = (SettingsData.workspaceFocusedBorderEnabled && isActive && !isPlaceholder) ? SettingsData.workspaceFocusedBorderThickness : 0; const borderWidth = (delegateRoot.focusedBorderEnabledForMonitor && isActive && !isPlaceholder) ? delegateRoot.focusedBorderThicknessForMonitor : 0;
return delegateRoot.visualHeight + borderWidth * 2; return delegateRoot.visualHeight + borderWidth * 2;
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "transparent" color: "transparent"
border.width: (SettingsData.workspaceFocusedBorderEnabled && isActive && !isPlaceholder) ? SettingsData.workspaceFocusedBorderThickness : 0 border.width: (delegateRoot.focusedBorderEnabledForMonitor && isActive && !isPlaceholder) ? delegateRoot.focusedBorderThicknessForMonitor : 0
border.color: (SettingsData.workspaceFocusedBorderEnabled && isActive && !isPlaceholder) ? focusedBorderColor : "transparent" border.color: (delegateRoot.focusedBorderEnabledForMonitor && isActive && !isPlaceholder) ? focusedBorderColor : "transparent"
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {
@@ -0,0 +1,42 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Modules.Settings.Widgets
Column {
id: root
property var borderColorOptions: []
property string borderColorKey: ""
property string borderCustomColorKey: ""
property string borderThicknessKey: ""
property var extraTags: []
width: parent?.width ?? 0
spacing: Theme.spacingS
leftPadding: Theme.spacingM
ColorDropdownRow {
width: parent.width - parent.leftPadding
text: I18n.tr("Border Color")
settingKey: root.borderColorKey
tags: ["workspace", "focused", "border", "color", "custom"].concat(root.extraTags)
options: root.borderColorOptions
currentMode: SettingsData[root.borderColorKey]
customColor: SettingsData[root.borderCustomColorKey] || "#6750A4"
onModeSelected: mode => SettingsData.set(root.borderColorKey, mode)
onCustomColorSelected: selectedColor => SettingsData.set(root.borderCustomColorKey, selectedColor.toString())
}
SettingsSliderRow {
width: parent.width - parent.leftPadding
text: I18n.tr("Thickness")
value: SettingsData[root.borderThicknessKey]
minimum: 1
maximum: 6
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set(root.borderThicknessKey, newValue)
}
}
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Services import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets import qs.Modules.Settings.Widgets
SettingsCard { SettingsCard {
@@ -9,6 +10,7 @@ SettingsCard {
iconName: "palette" iconName: "palette"
title: I18n.tr("Workspace Appearance") title: I18n.tr("Workspace Appearance")
settingKey: "workspaceAppearance" settingKey: "workspaceAppearance"
tags: ["workspace", "focused", "color", "custom"]
collapsible: true collapsible: true
expanded: false expanded: false
@@ -184,120 +186,182 @@ SettingsCard {
readonly property bool workspaceStateColorsVisible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango readonly property bool workspaceStateColorsVisible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango
readonly property bool urgentWorkspaceColorsVisible: workspaceStateColorsVisible || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle readonly property bool urgentWorkspaceColorsVisible: workspaceStateColorsVisible || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
ColorDropdownRow { function isFocusedAppearanceSection(section) {
text: I18n.tr("Focused Color") return ["workspaceAppearance", "workspaceColorMode", "workspaceOccupiedColorMode", "workspaceUnfocusedColorMode", "workspaceUrgentColorMode", "workspaceFocusedBorderEnabled", "workspaceFocusedBorderColor", "workspaceFocusedBorderThickness"].includes(section);
settingKey: "workspaceColorMode"
tags: ["workspace", "focused", "color", "custom"]
options: root.focusedColorOptions
currentMode: SettingsData.workspaceColorMode
customColor: SettingsData.workspaceFocusedCustomColor || "#6750A4"
onModeSelected: mode => SettingsData.set("workspaceColorMode", mode)
onCustomColorSelected: selectedColor => SettingsData.set("workspaceFocusedCustomColor", selectedColor.toString())
} }
Rectangle { Item {
width: parent.width width: parent.width
height: 1 height: workspaceTabBar.height + Theme.spacingM
color: Theme.outline
opacity: 0.15
}
ColorDropdownRow { DankTabBar {
text: I18n.tr("Occupied Color") id: workspaceTabBar
settingKey: "workspaceOccupiedColorMode" width: parent.width
tags: ["workspace", "occupied", "color", "custom"] tabHeight: 44
visible: root.workspaceStateColorsVisible showIcons: false
options: root.occupiedColorOptions model: [({
currentMode: SettingsData.workspaceOccupiedColorMode "text": I18n.tr("Focused Display", "workspace appearance tab")
customColor: SettingsData.workspaceOccupiedCustomColor || "#625B71" }), ({
onModeSelected: mode => SettingsData.set("workspaceOccupiedColorMode", mode) "text": I18n.tr("Unfocused Display(s)", "workspace appearance tab")
onCustomColorSelected: selectedColor => SettingsData.set("workspaceOccupiedCustomColor", selectedColor.toString()) })]
} onTabClicked: index => currentIndex = index
Component.onCompleted: Qt.callLater(updateIndicator)
Rectangle { Connections {
width: parent.width target: SettingsSearchService
height: 1
color: Theme.outline
opacity: 0.15
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango
}
ColorDropdownRow { function onTargetSectionChanged() {
text: I18n.tr("Unfocused Color") const section = SettingsSearchService.targetSection;
settingKey: "workspaceUnfocusedColorMode" if (!section)
tags: ["workspace", "unfocused", "color", "custom"] return;
options: root.unfocusedColorOptions
defaultColor: Theme.surfaceText
currentMode: SettingsData.workspaceUnfocusedColorMode
customColor: SettingsData.workspaceUnfocusedCustomColor || "#49454E"
onModeSelected: mode => SettingsData.set("workspaceUnfocusedColorMode", mode)
onCustomColorSelected: selectedColor => SettingsData.set("workspaceUnfocusedCustomColor", selectedColor.toString())
}
Rectangle { if (section.startsWith("workspaceUnfocusedMonitor")) {
width: parent.width root.expanded = true;
height: 1 workspaceTabBar.currentIndex = 1;
color: Theme.outline } else if (root.isFocusedAppearanceSection(section)) {
opacity: 0.15 root.expanded = true;
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle workspaceTabBar.currentIndex = 0;
} } else {
return;
}
ColorDropdownRow { Qt.callLater(workspaceTabBar.updateIndicator);
text: I18n.tr("Urgent Color") }
settingKey: "workspaceUrgentColorMode" }
tags: ["workspace", "urgent", "color", "custom"] }
visible: root.urgentWorkspaceColorsVisible
options: root.urgentColorOptions
defaultColor: Theme.error
currentMode: SettingsData.workspaceUrgentColorMode
customColor: SettingsData.workspaceUrgentCustomColor || "#B3261E"
onModeSelected: mode => SettingsData.set("workspaceUrgentColorMode", mode)
onCustomColorSelected: selectedColor => SettingsData.set("workspaceUrgentCustomColor", selectedColor.toString())
}
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)
} }
Column { Column {
id: focusedTab
width: parent.width width: parent.width
spacing: Theme.spacingS spacing: Theme.spacingM
visible: SettingsData.workspaceFocusedBorderEnabled visible: workspaceTabBar.currentIndex === 0
leftPadding: Theme.spacingM
ColorDropdownRow { WorkspaceAppearanceColorOptions {
width: parent.width - parent.leftPadding focusedColorOptions: root.focusedColorOptions
text: I18n.tr("Border Color") occupiedColorOptions: root.occupiedColorOptions
settingKey: "workspaceFocusedBorderColor" unfocusedColorOptions: root.unfocusedColorOptions
tags: ["workspace", "focused", "border", "color", "custom"] urgentColorOptions: root.urgentColorOptions
options: root.borderColorOptions occupiedColorVisible: root.workspaceStateColorsVisible
currentMode: SettingsData.workspaceFocusedBorderColor urgentColorVisible: root.urgentWorkspaceColorsVisible
customColor: SettingsData.workspaceFocusedBorderCustomColor || "#6750A4" focusedColorModeKey: "workspaceColorMode"
onModeSelected: mode => SettingsData.set("workspaceFocusedBorderColor", mode) focusedCustomColorKey: "workspaceFocusedCustomColor"
onCustomColorSelected: selectedColor => SettingsData.set("workspaceFocusedBorderCustomColor", selectedColor.toString()) occupiedColorModeKey: "workspaceOccupiedColorMode"
occupiedCustomColorKey: "workspaceOccupiedCustomColor"
unfocusedColorModeKey: "workspaceUnfocusedColorMode"
unfocusedCustomColorKey: "workspaceUnfocusedCustomColor"
urgentColorModeKey: "workspaceUrgentColorMode"
urgentCustomColorKey: "workspaceUrgentCustomColor"
} }
SettingsSliderRow { Rectangle {
width: parent.width - parent.leftPadding width: parent.width
text: I18n.tr("Thickness") height: 1
value: SettingsData.workspaceFocusedBorderThickness color: Theme.outline
minimum: 1 opacity: 0.15
maximum: 6 }
unit: "px"
defaultValue: 2 SettingsToggleRow {
onSliderValueChanged: newValue => SettingsData.set("workspaceFocusedBorderThickness", newValue) 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"
}
} }
} }
} }
@@ -0,0 +1,101 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
Column {
id: root
property var focusedColorOptions: []
property var occupiedColorOptions: []
property var unfocusedColorOptions: []
property var urgentColorOptions: []
property bool occupiedColorVisible: true
property bool urgentColorVisible: true
property string focusedColorModeKey: ""
property string focusedCustomColorKey: ""
property string occupiedColorModeKey: ""
property string occupiedCustomColorKey: ""
property string unfocusedColorModeKey: ""
property string unfocusedCustomColorKey: ""
property string urgentColorModeKey: ""
property string urgentCustomColorKey: ""
property var extraTags: []
width: parent?.width ?? 0
spacing: Theme.spacingM
ColorDropdownRow {
text: I18n.tr("Focused Color")
settingKey: root.focusedColorModeKey
tags: ["workspace", "focused", "color", "custom"].concat(root.extraTags)
options: root.focusedColorOptions
currentMode: SettingsData[root.focusedColorModeKey]
customColor: SettingsData[root.focusedCustomColorKey] || "#6750A4"
onModeSelected: mode => SettingsData.set(root.focusedColorModeKey, mode)
onCustomColorSelected: selectedColor => SettingsData.set(root.focusedCustomColorKey, selectedColor.toString())
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
ColorDropdownRow {
text: I18n.tr("Occupied Color")
settingKey: root.occupiedColorModeKey
tags: ["workspace", "occupied", "color", "custom"].concat(root.extraTags)
visible: root.occupiedColorVisible
options: root.occupiedColorOptions
currentMode: SettingsData[root.occupiedColorModeKey]
customColor: SettingsData[root.occupiedCustomColorKey] || "#625B71"
onModeSelected: mode => SettingsData.set(root.occupiedColorModeKey, mode)
onCustomColorSelected: selectedColor => SettingsData.set(root.occupiedCustomColorKey, selectedColor.toString())
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: root.occupiedColorVisible
}
ColorDropdownRow {
text: I18n.tr("Unfocused Color")
settingKey: root.unfocusedColorModeKey
tags: ["workspace", "unfocused", "color", "custom"].concat(root.extraTags)
options: root.unfocusedColorOptions
defaultColor: Theme.surfaceText
currentMode: SettingsData[root.unfocusedColorModeKey]
customColor: SettingsData[root.unfocusedCustomColorKey] || "#49454E"
onModeSelected: mode => SettingsData.set(root.unfocusedColorModeKey, mode)
onCustomColorSelected: selectedColor => SettingsData.set(root.unfocusedCustomColorKey, selectedColor.toString())
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: root.urgentColorVisible
}
ColorDropdownRow {
text: I18n.tr("Urgent Color")
settingKey: root.urgentColorModeKey
tags: ["workspace", "urgent", "color", "custom"].concat(root.extraTags)
visible: root.urgentColorVisible
options: root.urgentColorOptions
defaultColor: Theme.error
currentMode: SettingsData[root.urgentColorModeKey]
customColor: SettingsData[root.urgentCustomColorKey] || "#B3261E"
onModeSelected: mode => SettingsData.set(root.urgentColorModeKey, mode)
onCustomColorSelected: selectedColor => SettingsData.set(root.urgentCustomColorKey, selectedColor.toString())
}
}
+4
View File
@@ -70,11 +70,15 @@ Singleton {
return screens.length > 0 ? widgetRegistry[widgetId][screens[0]] : null; return screens.length > 0 ? widgetRegistry[widgetId][screens[0]] : null;
} }
readonly property bool focusedScreenDetectionSupported: CompositorService.isHyprland || CompositorService.isNiri || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
function getFocusedScreenName() { function getFocusedScreenName() {
if (CompositorService.isHyprland && Hyprland.focusedWorkspace?.monitor) if (CompositorService.isHyprland && Hyprland.focusedWorkspace?.monitor)
return Hyprland.focusedWorkspace.monitor.name; return Hyprland.focusedWorkspace.monitor.name;
if (CompositorService.isNiri && NiriService.currentOutput) if (CompositorService.isNiri && NiriService.currentOutput)
return NiriService.currentOutput; return NiriService.currentOutput;
if (CompositorService.isMango && MangoService.activeOutput)
return MangoService.activeOutput;
if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) { if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true); const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
return focusedWs?.monitor?.name || ""; return focusedWs?.monitor?.name || "";
@@ -1899,6 +1899,33 @@
], ],
"description": "Show an outline ring around the focused workspace indicator" "description": "Show an outline ring around the focused workspace indicator"
}, },
{
"section": "workspaceUnfocusedMonitorBorderEnabled",
"label": "Focused Border",
"tabIndex": 6,
"category": "Dank Bar",
"keywords": [
"around",
"bar",
"border",
"dank",
"desktop",
"display",
"focused",
"indicator",
"monitor",
"outline",
"panel",
"ring",
"show",
"statusbar",
"topbar",
"unfocused",
"virtual",
"workspace"
],
"description": "Show an outline ring around the focused workspace indicator"
},
{ {
"section": "barFontScale", "section": "barFontScale",
"label": "Font Scale", "label": "Font Scale",
@@ -1979,6 +2006,44 @@
"icon": "opacity", "icon": "opacity",
"description": "Controls opacity of the bar background" "description": "Controls opacity of the bar background"
}, },
{
"section": "workspaceUnfocusedMonitorSeparateAppearance",
"label": "Separate Appearance for Unfocused Display(s)",
"tabIndex": 6,
"category": "Dank Bar",
"keywords": [
"appearance",
"bar",
"color",
"colors",
"colour",
"colours",
"dank",
"desktop",
"different",
"display",
"display(s)",
"displays",
"focused",
"hue",
"monitor",
"monitors",
"output",
"outputs",
"palette",
"panel",
"screen",
"screens",
"separate",
"statusbar",
"tint",
"topbar",
"unfocused",
"virtual",
"workspace"
],
"description": "Use different workspace colors on displays that are not focused"
},
{ {
"section": "barShadow", "section": "barShadow",
"label": "Shadow Override", "label": "Shadow Override",