1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-13 14:36:32 -04:00

feat(settings): add compositor section & restructured settings

- add dedicated Compositor pages for comp specifc features
- add Dank Bar Appearance subsection
- improve lazy loading, caching, search routing, & IPC navigation
- standardized responsive Setting categories from global animations
This commit is contained in:
purian23
2026-06-07 03:52:00 -04:00
parent 8155970ba2
commit 69f3dee25a
19 changed files with 2355 additions and 1503 deletions
+4
View File
@@ -11,6 +11,10 @@ Singleton {
readonly property int durMed: 450 readonly property int durMed: 450
readonly property int durLong: 600 readonly property int durLong: 600
// Navigation feedback stays responsive even when ambient shell motion is slow.
readonly property int settingsNavigationStateDuration: 180
readonly property int settingsNavigationRippleDuration: 200
readonly property int slidePx: 80 readonly property int slidePx: 80
readonly property var emphasized: [0.05, 0.00, 0.133333, 0.06, 0.166667, 0.40, 0.208333, 0.82, 0.25, 1.00, 1.00, 1.00] readonly property var emphasized: [0.05, 0.00, 0.133333, 0.06, 0.166667, 0.40, 0.208333, 0.82, 0.25, 1.00, 1.00, 1.00]
+25
View File
@@ -0,0 +1,25 @@
pragma Singleton
import QtQuick
import Quickshell
import qs.Common
Singleton {
id: root
property string selectedBarId: "default"
function normalizeSelectedBar() {
if (SettingsData.getBarConfig(selectedBarId))
return;
selectedBarId = SettingsData.barConfigs[0]?.id ?? "default";
}
Connections {
target: SettingsData
function onBarConfigsChanged() {
root.normalizeSelectedBar();
}
}
}
+1 -1
View File
@@ -947,7 +947,7 @@ Item {
function tabs(): string { function tabs(): string {
if (!PopoutService.settingsModal) if (!PopoutService.settingsModal)
return "wallpaper\ntheme\ntypography\ntime_weather\nsounds\ndankbar\ndankbar_settings\ndankbar_widgets\nworkspaces\nmedia_player\nnotifications\nosd\nrunning_apps\nupdater\ndock\nlauncher\nkeybinds\ndisplays\nnetwork\nprinters\nlock_screen\npower_sleep\nplugins\nabout"; return "wallpaper\ntheme\ntypography\ntime_weather\nsounds\ndankbar\ndankbar_settings\ndankbar_appearance\ndankbar_widgets\nframe\nworkspaces\ncompositor\nmedia_player\nnotifications\nosd\nrunning_apps\nupdater\ndock\nlauncher\nkeybinds\ndisplays\nnetwork\nprinters\nlock_screen\npower_sleep\nplugins\nabout";
var modal = PopoutService.settingsModal; var modal = PopoutService.settingsModal;
var ids = []; var ids = [];
var structure = modal.sidebar?.categoryStructure ?? []; var structure = modal.sidebar?.categoryStructure ?? [];
+41 -23
View File
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modules.Settings import qs.Modules.Settings
import qs.Widgets
FocusScope { FocusScope {
id: root id: root
@@ -97,7 +98,24 @@ FocusScope {
visible: active visible: active
focus: active focus: active
sourceComponent: WorkspacesTab {} sourceComponent: CompositorTab {}
onActiveChanged: {
if (active && item)
Qt.callLater(() => item.forceActiveFocus());
}
}
Loader {
id: dankBarAppearanceLoader
anchors.fill: parent
active: root.currentIndex === 6
visible: active
focus: active
sourceComponent: DankBarAppearanceTab {
parentModal: root.parentModal
}
onActiveChanged: { onActiveChanged: {
if (active && item) if (active && item)
@@ -432,19 +450,36 @@ FocusScope {
Loader { Loader {
id: widgetsLoader id: widgetsLoader
property bool loadedOnce: false
anchors.fill: parent anchors.fill: parent
active: root.currentIndex === 22 active: root.currentIndex === 22 || loadedOnce
visible: active visible: root.currentIndex === 22 && status === Loader.Ready
focus: active focus: visible
asynchronous: true
sourceComponent: WidgetsTab { sourceComponent: WidgetsTab {
parentModal: root.parentModal parentModal: root.parentModal
} }
onActiveChanged: { onLoaded: {
if (active && item) loadedOnce = true;
if (visible && item)
Qt.callLater(() => item.forceActiveFocus()); Qt.callLater(() => item.forceActiveFocus());
} }
onVisibleChanged: {
if (visible && item)
Qt.callLater(() => item.forceActiveFocus());
}
}
StyledText {
anchors.centerIn: parent
visible: root.currentIndex === 22 && widgetsLoader.status === Loader.Loading
text: I18n.tr("Loading...", "loading indicator")
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeMedium
} }
Loader { Loader {
@@ -479,23 +514,6 @@ FocusScope {
} }
} }
Loader {
id: windowRulesLoader
anchors.fill: parent
active: root.currentIndex === 28
visible: active
focus: active
sourceComponent: WindowRulesTab {
parentModal: root.parentModal
}
onActiveChanged: {
if (active && item)
Qt.callLater(() => item.forceActiveFocus());
}
}
Loader { Loader {
id: audioLoader id: audioLoader
anchors.fill: parent anchors.fill: parent
+29 -21
View File
@@ -23,6 +23,7 @@ Rectangle {
property bool searchActive: searchField.text.length > 0 property bool searchActive: searchField.text.length > 0
property int searchSelectedIndex: 0 property int searchSelectedIndex: 0
property int keyboardHighlightIndex: -1 property int keyboardHighlightIndex: -1
readonly property int navigationStateDuration: Theme.currentAnimationSpeed === SettingsData.AnimationSpeed.None ? 0 : Anims.settingsNavigationStateDuration
function focusSearch() { function focusSearch() {
searchField.forceActiveFocus(); searchField.forceActiveFocus();
@@ -115,6 +116,12 @@ Rectangle {
"icon": "tune", "icon": "tune",
"tabIndex": 3 "tabIndex": 3
}, },
{
"id": "dankbar_appearance",
"text": I18n.tr("Appearance"),
"icon": "palette",
"tabIndex": 6
},
{ {
"id": "dankbar_widgets", "id": "dankbar_widgets",
"text": I18n.tr("Widgets"), "text": I18n.tr("Widgets"),
@@ -131,16 +138,10 @@ Rectangle {
}, },
{ {
"id": "workspaces_widgets", "id": "workspaces_widgets",
"text": I18n.tr("Workspaces & Widgets"), "text": I18n.tr("Widgets & Notifications"),
"icon": "dashboard", "icon": "dashboard",
"collapsedByDefault": true, "collapsedByDefault": true,
"children": [ "children": [
{
"id": "workspaces",
"text": I18n.tr("Workspaces"),
"icon": "view_module",
"tabIndex": 4
},
{ {
"id": "media_player", "id": "media_player",
"text": I18n.tr("Media Player"), "text": I18n.tr("Media Player"),
@@ -187,6 +188,12 @@ Rectangle {
} }
] ]
}, },
{
"id": "compositor",
"text": I18n.tr("Compositor"),
"icon": "layers",
"tabIndex": 4
},
{ {
"id": "keybinds", "id": "keybinds",
"text": I18n.tr("Keyboard Shortcuts"), "text": I18n.tr("Keyboard Shortcuts"),
@@ -305,13 +312,6 @@ Rectangle {
"text": I18n.tr("Users"), "text": I18n.tr("Users"),
"icon": "manage_accounts", "icon": "manage_accounts",
"tabIndex": 35 "tabIndex": 35
},
{
"id": "window_rules",
"text": I18n.tr("Window Rules"),
"icon": "select_window",
"tabIndex": 28,
"windowRulesCapable": true
} }
] ]
}, },
@@ -544,6 +544,8 @@ Rectangle {
return -1; return -1;
var normalized = name.toLowerCase().replace(/[_\-\s]/g, ""); var normalized = name.toLowerCase().replace(/[_\-\s]/g, "");
if (normalized === "workspaces")
normalized = "compositor";
for (var i = 0; i < categoryStructure.length; i++) { for (var i = 0; i < categoryStructure.length; i++) {
var cat = categoryStructure[i]; var cat = categoryStructure[i];
@@ -588,7 +590,7 @@ Rectangle {
id: __m1 id: __m1
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium font.weight: Font.Medium
text: I18n.tr("Workspaces & Widgets") text: I18n.tr("Widgets & Notifications")
} }
StyledTextMetrics { StyledTextMetrics {
id: __m2 id: __m2
@@ -782,6 +784,7 @@ Rectangle {
id: resultRipple id: resultRipple
rippleColor: root.searchSelectedIndex === resultDelegate.index ? Theme.buttonText : Theme.surfaceText rippleColor: root.searchSelectedIndex === resultDelegate.index ? Theme.buttonText : Theme.surfaceText
cornerRadius: resultDelegate.radius cornerRadius: resultDelegate.radius
animationDuration: Anims.settingsNavigationRippleDuration
} }
Row { Row {
@@ -837,8 +840,9 @@ Rectangle {
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: root.navigationStateDuration
easing.type: Theme.standardEasing easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.expressiveEffects
} }
} }
} }
@@ -912,6 +916,7 @@ Rectangle {
id: categoryRipple id: categoryRipple
rippleColor: categoryRow.isActive ? Theme.buttonText : Theme.surfaceText rippleColor: categoryRow.isActive ? Theme.buttonText : Theme.surfaceText
cornerRadius: categoryRow.radius cornerRadius: categoryRow.radius
animationDuration: Anims.settingsNavigationRippleDuration
} }
Row { Row {
@@ -967,8 +972,9 @@ Rectangle {
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: root.navigationStateDuration
easing.type: Theme.standardEasing easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.expressiveEffects
} }
} }
} }
@@ -1009,6 +1015,7 @@ Rectangle {
id: childRipple id: childRipple
rippleColor: childDelegate.isActive ? Theme.buttonText : Theme.surfaceText rippleColor: childDelegate.isActive ? Theme.buttonText : Theme.surfaceText
cornerRadius: childDelegate.radius cornerRadius: childDelegate.radius
animationDuration: Anims.settingsNavigationRippleDuration
} }
Row { Row {
@@ -1049,8 +1056,9 @@ Rectangle {
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: root.navigationStateDuration
easing.type: Theme.standardEasing easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.expressiveEffects
} }
} }
} }
@@ -0,0 +1,324 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
Item {
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: layoutColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: layoutColumn
topPadding: Theme.spacingXL
bottomPadding: Theme.spacingXL
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
SettingsCard {
width: parent.width
tags: ["niri", "layout", "gaps", "radius", "window", "border"]
title: I18n.tr("Niri Layout Overrides").replace("Niri", "niri")
settingKey: "niriLayout"
iconName: "crop_square"
visible: CompositorService.isNiri
SettingsToggleRow {
tags: ["niri", "gaps", "override"]
settingKey: "niriLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.niriLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("niriLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("niriLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tags: ["niri", "gaps", "override"]
settingKey: "niriLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows")
visible: SettingsData.niriLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.niriLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("niriLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tags: ["niri", "radius", "override"]
settingKey: "niriLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window radius instead of theme radius")
checked: SettingsData.niriLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("niriLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("niriLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tags: ["niri", "radius", "override"]
settingKey: "niriLayoutRadiusOverride"
text: I18n.tr("Window Corner Radius")
description: I18n.tr("Rounded corners for windows")
visible: SettingsData.niriLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.niriLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("niriLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tags: ["niri", "border", "override"]
settingKey: "niriLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border/focus-ring width")
checked: SettingsData.niriLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("niriLayoutBorderSize", 2);
return;
}
SettingsData.set("niriLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tags: ["niri", "border", "override"]
settingKey: "niriLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border and focus ring")
visible: SettingsData.niriLayoutBorderSize >= 0
value: Math.max(0, SettingsData.niriLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("niriLayoutBorderSize", newValue)
}
}
SettingsCard {
width: parent.width
tags: ["hyprland", "layout", "gaps", "radius", "window", "border", "rounding"]
title: I18n.tr("Hyprland Layout Overrides")
settingKey: "hyprlandLayout"
iconName: "crop_square"
visible: CompositorService.isHyprland
SettingsToggleRow {
tags: ["hyprland", "gaps", "override"]
settingKey: "hyprlandLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.hyprlandLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("hyprlandLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("hyprlandLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tags: ["hyprland", "gaps", "override"]
settingKey: "hyprlandLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows (gaps_in and gaps_out)")
visible: SettingsData.hyprlandLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.hyprlandLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tags: ["hyprland", "radius", "override", "rounding"]
settingKey: "hyprlandLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window rounding instead of theme radius")
checked: SettingsData.hyprlandLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("hyprlandLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("hyprlandLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tags: ["hyprland", "radius", "override", "rounding"]
settingKey: "hyprlandLayoutRadiusOverride"
text: I18n.tr("Window Rounding")
description: I18n.tr("Rounded corners for windows (decoration.rounding)")
visible: SettingsData.hyprlandLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.hyprlandLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tags: ["hyprland", "border", "override"]
settingKey: "hyprlandLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border size")
checked: SettingsData.hyprlandLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("hyprlandLayoutBorderSize", 2);
return;
}
SettingsData.set("hyprlandLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tags: ["hyprland", "border", "override"]
settingKey: "hyprlandLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border (general.border_size)")
visible: SettingsData.hyprlandLayoutBorderSize >= 0
value: Math.max(0, SettingsData.hyprlandLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutBorderSize", newValue)
}
SettingsToggleRow {
tags: ["hyprland", "resize", "border", "mouse", "drag"]
settingKey: "hyprlandResizeOnBorder"
text: I18n.tr("Resize on Border")
description: I18n.tr("Resize windows by dragging their edges with the mouse")
checked: SettingsData.hyprlandResizeOnBorder
onToggled: checked => SettingsData.set("hyprlandResizeOnBorder", checked)
}
}
SettingsCard {
width: parent.width
tags: ["mangowc", "mango", "dwl", "layout", "gaps", "radius", "window", "border"]
title: I18n.tr("MangoWC Layout Overrides")
settingKey: "mangoLayout"
iconName: "crop_square"
visible: CompositorService.isDwl || CompositorService.isMango
SettingsToggleRow {
tags: ["mangowc", "mango", "gaps", "override"]
settingKey: "mangoLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.mangoLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("mangoLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("mangoLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tags: ["mangowc", "mango", "gaps", "override"]
settingKey: "mangoLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows (gappih/gappiv/gappoh/gappov)")
visible: SettingsData.mangoLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.mangoLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tags: ["mangowc", "mango", "radius", "override"]
settingKey: "mangoLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window radius instead of theme radius")
checked: SettingsData.mangoLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("mangoLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("mangoLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tags: ["mangowc", "mango", "radius", "override"]
settingKey: "mangoLayoutRadiusOverride"
text: I18n.tr("Window Corner Radius")
description: I18n.tr("Rounded corners for windows (border_radius)")
visible: SettingsData.mangoLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.mangoLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tags: ["mangowc", "mango", "border", "override"]
settingKey: "mangoLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border size")
checked: SettingsData.mangoLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("mangoLayoutBorderSize", 2);
return;
}
SettingsData.set("mangoLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tags: ["mangowc", "mango", "border", "override"]
settingKey: "mangoLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border (borderpx)")
visible: SettingsData.mangoLayoutBorderSize >= 0
value: Math.max(0, SettingsData.mangoLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutBorderSize", newValue)
}
}
}
}
}
@@ -0,0 +1,167 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property int subTabIndex: 0
readonly property var workspaceSections: ({
"workspaceSettings": true,
"showWorkspaceIndex": true,
"showWorkspaceName": true,
"showWorkspacePadding": true,
"showWorkspaceApps": true,
"groupWorkspaceApps": true,
"groupActiveWorkspaceApps": true,
"workspaceActiveAppHighlightEnabled": true,
"workspaceFollowFocus": true,
"showOccupiedWorkspacesOnly": true,
"reverseScrolling": true,
"workspaceDragReorder": true,
"dwlShowAllTags": true,
"workspaceIcons": true
})
readonly property var layoutSections: ({
"niriLayout": true,
"niriLayoutGapsOverrideEnabled": true,
"niriLayoutGapsOverride": true,
"niriLayoutRadiusOverrideEnabled": true,
"niriLayoutRadiusOverride": true,
"niriLayoutBorderSizeEnabled": true,
"niriLayoutBorderSize": true,
"hyprlandLayout": true,
"hyprlandLayoutGapsOverrideEnabled": true,
"hyprlandLayoutGapsOverride": true,
"hyprlandLayoutRadiusOverrideEnabled": true,
"hyprlandLayoutRadiusOverride": true,
"hyprlandLayoutBorderSizeEnabled": true,
"hyprlandLayoutBorderSize": true,
"hyprlandResizeOnBorder": true,
"mangoLayout": true,
"mangoLayoutGapsOverrideEnabled": true,
"mangoLayoutGapsOverride": true,
"mangoLayoutRadiusOverrideEnabled": true,
"mangoLayoutRadiusOverride": true,
"mangoLayoutBorderSizeEnabled": true,
"mangoLayoutBorderSize": true
})
function routeSearchTarget(target) {
if (!target)
return;
if (workspaceSections[target]) {
subTabIndex = 0;
} else if (layoutSections[target]) {
subTabIndex = 1;
} else if (target === "windowRules" || target.startsWith("windowRule")) {
subTabIndex = 2;
}
}
Component.onCompleted: routeSearchTarget(SettingsSearchService.targetSection)
Connections {
target: SettingsSearchService
function onTargetSectionChanged() {
root.routeSearchTarget(SettingsSearchService.targetSection);
}
}
ColumnLayout {
anchors.fill: parent
spacing: 0
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 60
color: "transparent"
DankTabBar {
id: compositorTabBar
width: Math.min(500, parent.width - Theme.spacingL * 2)
height: 45
anchors.centerIn: parent
model: [
{
"text": I18n.tr("Workspaces"),
"icon": "view_module"
},
{
"text": I18n.tr("Window Layout"),
"icon": "crop_square"
},
{
"text": I18n.tr("Window Rules"),
"icon": "select_window"
}
]
currentIndex: root.subTabIndex
onTabClicked: index => root.subTabIndex = index
}
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
y: compositorTabBar.y + compositorTabBar.height + 10
width: compositorTabBar.width
height: 1
color: Theme.surface
opacity: 0.56
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
Loader {
anchors.fill: parent
active: root.subTabIndex === 0
visible: active
sourceComponent: WorkspacesTab {}
}
Loader {
anchors.fill: parent
active: root.subTabIndex === 1
visible: active
sourceComponent: CompositorLayoutTab {}
}
Loader {
id: windowRulesLoader
property bool loadedOnce: false
anchors.fill: parent
active: root.subTabIndex === 2 || loadedOnce
visible: root.subTabIndex === 2 && status === Loader.Ready
asynchronous: true
sourceComponent: WindowRulesTab {
pageActive: root.subTabIndex === 2
}
onLoaded: loadedOnce = true
}
StyledText {
anchors.centerIn: parent
visible: root.subTabIndex === 2 && windowRulesLoader.status === Loader.Loading
text: I18n.tr("Loading...", "loading indicator")
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeMedium
}
}
}
}
@@ -0,0 +1,5 @@
import QtQuick
DankBarTab {
appearanceOnly: true
}
+85 -21
View File
@@ -13,7 +13,22 @@ Item {
LayoutMirroring.childrenInherit: true LayoutMirroring.childrenInherit: true
property var parentModal: null property var parentModal: null
property string selectedBarId: "default" property bool appearanceOnly: false
property string selectedBarId: SettingsUiState.selectedBarId
onSelectedBarIdChanged: {
if (SettingsUiState.selectedBarId !== selectedBarId)
SettingsUiState.selectedBarId = selectedBarId;
}
Connections {
target: SettingsUiState
function onSelectedBarIdChanged() {
if (dankBarTab.selectedBarId !== SettingsUiState.selectedBarId)
dankBarTab.selectedBarId = SettingsUiState.selectedBarId;
}
}
property var selectedBarConfig: { property var selectedBarConfig: {
selectedBarId; selectedBarId;
@@ -21,6 +36,14 @@ Item {
const index = SettingsData.barConfigs.findIndex(cfg => cfg.id === selectedBarId); const index = SettingsData.barConfigs.findIndex(cfg => cfg.id === selectedBarId);
return index !== -1 ? SettingsData.barConfigs[index] : SettingsData.barConfigs[0]; return index !== -1 ? SettingsData.barConfigs[index] : SettingsData.barConfigs[0];
} }
readonly property string selectedBarName: {
selectedBarId;
SettingsData.barConfigs;
const index = SettingsData.barConfigs.findIndex(config => config.id === selectedBarId);
if (index < 0)
return I18n.tr("Bar");
return SettingsData.barConfigs[index].name || I18n.tr("Bar %1").arg(index + 1);
}
property bool selectedBarIsVertical: { property bool selectedBarIsVertical: {
selectedBarId; selectedBarId;
@@ -210,10 +233,33 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL spacing: Theme.spacingXL
SettingsCard {
tab: "appearance"
iconName: "toolbar"
title: I18n.tr("Dank Bar")
settingKey: "barAppearance"
visible: dankBarTab.appearanceOnly
SettingsButtonGroupRow {
text: I18n.tr("Editing changes on %1").arg(dankBarTab.selectedBarName)
model: SettingsData.barConfigs.map((config, index) => config.name || I18n.tr("Bar %1").arg(index + 1))
currentIndex: {
const index = SettingsData.barConfigs.findIndex(config => config.id === dankBarTab.selectedBarId);
return Math.max(0, index);
}
onSelectionChanged: (index, selected) => {
if (!selected || index < 0 || index >= SettingsData.barConfigs.length)
return;
dankBarTab.selectedBarId = SettingsData.barConfigs[index].id;
}
}
}
SettingsCard { SettingsCard {
iconName: "dashboard" iconName: "dashboard"
title: I18n.tr("Bar Configurations") title: I18n.tr("Bar Configurations")
settingKey: "barConfigurations" settingKey: "barConfigurations"
visible: !dankBarTab.appearanceOnly
RowLayout { RowLayout {
width: parent.width width: parent.width
@@ -410,7 +456,7 @@ Item {
SettingsCard { SettingsCard {
iconName: selectedBarConfig?.enabled ? "visibility" : "visibility_off" iconName: selectedBarConfig?.enabled ? "visibility" : "visibility_off"
title: I18n.tr("Enable Bar") title: I18n.tr("Enable Bar")
visible: selectedBarId !== "default" visible: !dankBarTab.appearanceOnly && selectedBarId !== "default"
SettingsToggleRow { SettingsToggleRow {
text: I18n.tr("Toggle visibility of this bar configuration") text: I18n.tr("Toggle visibility of this bar configuration")
@@ -426,7 +472,7 @@ Item {
iconName: "vertical_align_center" iconName: "vertical_align_center"
title: I18n.tr("Position") title: I18n.tr("Position")
settingKey: "barPosition" settingKey: "barPosition"
visible: selectedBarConfig?.enabled visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
Item { Item {
width: parent.width width: parent.width
@@ -486,7 +532,7 @@ Item {
settingKey: "barDisplay" settingKey: "barDisplay"
collapsible: true collapsible: true
expanded: false expanded: false
visible: selectedBarConfig?.enabled visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
StyledText { StyledText {
width: parent.width width: parent.width
@@ -588,12 +634,12 @@ Item {
} }
SettingsCard { SettingsCard {
iconName: "visibility_off" iconName: "visibility"
title: I18n.tr("Visibility") title: I18n.tr("Visibility")
settingKey: "barVisibility" settingKey: "barVisibility"
collapsible: true collapsible: true
expanded: false expanded: true
visible: selectedBarConfig?.enabled visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
SettingsToggleRow { SettingsToggleRow {
text: I18n.tr("Auto-hide") text: I18n.tr("Auto-hide")
@@ -751,7 +797,7 @@ Item {
} }
SettingsControlledByFrame { SettingsControlledByFrame {
visible: SettingsData.frameEnabled visible: !dankBarTab.appearanceOnly && SettingsData.frameEnabled
parentModal: dankBarTab.parentModal parentModal: dankBarTab.parentModal
settingLabel: I18n.tr("Bar spacing and size") settingLabel: I18n.tr("Bar spacing and size")
reason: I18n.tr("Managed by Frame") reason: I18n.tr("Managed by Frame")
@@ -761,7 +807,7 @@ Item {
iconName: "space_bar" iconName: "space_bar"
title: I18n.tr("Spacing") title: I18n.tr("Spacing")
settingKey: "barSpacing" settingKey: "barSpacing"
visible: (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled visible: !dankBarTab.appearanceOnly && (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled
SettingsSliderRow { SettingsSliderRow {
id: edgeSpacingSlider id: edgeSpacingSlider
@@ -911,10 +957,11 @@ Item {
} }
SettingsCard { SettingsCard {
tab: "appearance"
iconName: "opacity" iconName: "opacity"
title: I18n.tr("Transparency") title: I18n.tr("Transparency")
settingKey: "barTransparency" settingKey: "barTransparency"
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
SettingsSliderRow { SettingsSliderRow {
id: barTransparencySlider id: barTransparencySlider
@@ -973,10 +1020,12 @@ Item {
SettingsSliderCard { SettingsSliderCard {
id: fontScaleSliderCard id: fontScaleSliderCard
tab: "appearance"
settingKey: "barFontScale"
iconName: "text_fields" iconName: "text_fields"
title: I18n.tr("Font Scale") title: I18n.tr("Font Scale")
description: I18n.tr("Scale DankBar font sizes independently") description: I18n.tr("Scale DankBar font sizes independently")
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
minimum: 50 minimum: 50
maximum: 200 maximum: 200
value: Math.round((selectedBarConfig?.fontScale ?? 1.0) * 100) value: Math.round((selectedBarConfig?.fontScale ?? 1.0) * 100)
@@ -998,10 +1047,12 @@ Item {
SettingsSliderCard { SettingsSliderCard {
id: iconScaleSliderCard id: iconScaleSliderCard
tab: "appearance"
settingKey: "barIconScale"
iconName: "interests" iconName: "interests"
title: I18n.tr("Icon Scale") title: I18n.tr("Icon Scale")
description: I18n.tr("Scale DankBar icon sizes independently") description: I18n.tr("Scale DankBar icon sizes independently")
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
minimum: 50 minimum: 50
maximum: 200 maximum: 200
value: Math.round((selectedBarConfig?.iconScale ?? 1.0) * 100) value: Math.round((selectedBarConfig?.iconScale ?? 1.0) * 100)
@@ -1021,13 +1072,18 @@ Item {
} }
} }
WorkspaceAppearanceCard {
visible: dankBarTab.appearanceOnly
}
SettingsCard { SettingsCard {
tab: "appearance"
iconName: "rounded_corner" iconName: "rounded_corner"
title: I18n.tr("Corners & Background") title: I18n.tr("Corners & Background")
settingKey: "barCorners" settingKey: "barCorners"
collapsible: true collapsible: true
expanded: false expanded: true
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
SettingsControlledByFrame { SettingsControlledByFrame {
visible: SettingsData.frameEnabled visible: SettingsData.frameEnabled
@@ -1144,7 +1200,7 @@ Item {
iconName: "fit_screen" iconName: "fit_screen"
title: I18n.tr("Maximize Detection") title: I18n.tr("Maximize Detection")
description: I18n.tr("Remove gaps and border when windows are maximized") description: I18n.tr("Remove gaps and border when windows are maximized")
visible: selectedBarConfig?.enabled && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango)
checked: selectedBarConfig?.maximizeDetection ?? true checked: selectedBarConfig?.maximizeDetection ?? true
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
maximizeDetection: checked maximizeDetection: checked
@@ -1152,10 +1208,11 @@ Item {
} }
SettingsCard { SettingsCard {
tab: "appearance"
iconName: "filter_b_and_w" iconName: "filter_b_and_w"
title: I18n.tr("System Tray Icon Tint") title: I18n.tr("System Tray Icon Tint")
settingKey: "trayIconTint" settingKey: "trayIconTint"
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
StyledText { StyledText {
text: I18n.tr("Choose monochrome or a theme color tint for system tray icons") text: I18n.tr("Choose monochrome or a theme color tint for system tray icons")
@@ -1251,9 +1308,11 @@ Item {
} }
SettingsToggleCard { SettingsToggleCard {
tab: "appearance"
settingKey: "barBorder"
iconName: "border_style" iconName: "border_style"
title: I18n.tr("Border") title: I18n.tr("Border")
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled && !dankBarTab.connectedFrameModeActive
checked: selectedBarConfig?.borderEnabled ?? false checked: selectedBarConfig?.borderEnabled ?? false
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
borderEnabled: checked borderEnabled: checked
@@ -1344,9 +1403,11 @@ Item {
} }
SettingsToggleCard { SettingsToggleCard {
tab: "appearance"
settingKey: "barWidgetOutline"
iconName: "highlight" iconName: "highlight"
title: I18n.tr("Widget Outline") title: I18n.tr("Widget Outline")
visible: selectedBarConfig?.enabled visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
checked: selectedBarConfig?.widgetOutlineEnabled ?? false checked: selectedBarConfig?.widgetOutlineEnabled ?? false
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
widgetOutlineEnabled: checked widgetOutlineEnabled: checked
@@ -1437,7 +1498,7 @@ Item {
} }
SettingsControlledByFrame { SettingsControlledByFrame {
visible: dankBarTab.connectedFrameModeActive visible: dankBarTab.appearanceOnly && dankBarTab.connectedFrameModeActive
parentModal: dankBarTab.parentModal parentModal: dankBarTab.parentModal
settingLabel: I18n.tr("Bar shadow, border, and corners") settingLabel: I18n.tr("Bar shadow, border, and corners")
reason: I18n.tr("Managed by Frame in Connected Mode") reason: I18n.tr("Managed by Frame in Connected Mode")
@@ -1445,12 +1506,13 @@ Item {
SettingsCard { SettingsCard {
id: shadowCard id: shadowCard
tab: "appearance"
iconName: "layers" iconName: "layers"
title: I18n.tr("Shadow Override", "bar shadow settings card") title: I18n.tr("Shadow Override", "bar shadow settings card")
settingKey: "barShadow" settingKey: "barShadow"
collapsible: true collapsible: true
expanded: false expanded: false
visible: (selectedBarConfig?.enabled ?? false) && !dankBarTab.connectedFrameModeActive visible: dankBarTab.appearanceOnly && (selectedBarConfig?.enabled ?? false) && !dankBarTab.connectedFrameModeActive
readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0 readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0
readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom" readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom"
@@ -1512,6 +1574,7 @@ Item {
} }
SettingsDropdownRow { SettingsDropdownRow {
tab: "appearance"
visible: shadowCard.shadowActive visible: shadowCard.shadowActive
text: I18n.tr("Direction Source", "bar shadow direction source") text: I18n.tr("Direction Source", "bar shadow direction source")
description: I18n.tr("Choose how this bar resolves shadow direction") description: I18n.tr("Choose how this bar resolves shadow direction")
@@ -1545,6 +1608,7 @@ Item {
} }
SettingsDropdownRow { SettingsDropdownRow {
tab: "appearance"
visible: shadowCard.shadowActive && shadowCard.directionSource === "manual" visible: shadowCard.shadowActive && shadowCard.directionSource === "manual"
text: I18n.tr("Manual Direction", "bar manual shadow direction") text: I18n.tr("Manual Direction", "bar manual shadow direction")
description: I18n.tr("Use a fixed shadow direction for this bar") description: I18n.tr("Use a fixed shadow direction for this bar")
@@ -1680,7 +1744,7 @@ Item {
iconName: "mouse" iconName: "mouse"
title: I18n.tr("Scroll Wheel") title: I18n.tr("Scroll Wheel")
description: I18n.tr("Control workspaces and columns by scrolling on the bar") description: I18n.tr("Control workspaces and columns by scrolling on the bar")
visible: selectedBarConfig?.enabled visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
checked: selectedBarConfig?.scrollEnabled ?? true checked: selectedBarConfig?.scrollEnabled ?? true
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, { onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
scrollEnabled: checked scrollEnabled: checked
+2 -321
View File
@@ -1640,7 +1640,7 @@ Item {
SettingsControlledByFrame { SettingsControlledByFrame {
visible: themeColorsTab.connectedFrameModeActive visible: themeColorsTab.connectedFrameModeActive
parentModal: themeColorsTab.parentModal parentModal: themeColorsTab.parentModal
settingLabel: I18n.tr("Surface Opacity") settingLabel: I18n.tr("Transparency")
reason: I18n.tr("Managed by Frame in Connected Mode") reason: I18n.tr("Managed by Frame in Connected Mode")
} }
@@ -1648,7 +1648,7 @@ Item {
tab: "theme" tab: "theme"
tags: ["surface", "popup", "transparency", "opacity", "modal"] tags: ["surface", "popup", "transparency", "opacity", "modal"]
settingKey: "popupTransparency" settingKey: "popupTransparency"
text: I18n.tr("Surface Opacity") text: I18n.tr("Transparency")
description: I18n.tr("Controls opacity of all popouts, modals, and their content layers") description: I18n.tr("Controls opacity of all popouts, modals, and their content layers")
visible: !themeColorsTab.connectedFrameModeActive visible: !themeColorsTab.connectedFrameModeActive
value: Math.round(SettingsData.popupTransparency * 100) value: Math.round(SettingsData.popupTransparency * 100)
@@ -1956,325 +1956,6 @@ Item {
} }
} }
SettingsCard {
tab: "theme"
tags: ["niri", "layout", "gaps", "radius", "window", "border"]
title: I18n.tr("Niri Layout Overrides").replace("Niri", "niri")
settingKey: "niriLayout"
iconName: "crop_square"
visible: CompositorService.isNiri
SettingsToggleRow {
tab: "theme"
tags: ["niri", "gaps", "override"]
settingKey: "niriLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.niriLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("niriLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("niriLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["niri", "gaps", "override"]
settingKey: "niriLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows")
visible: SettingsData.niriLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.niriLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("niriLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["niri", "radius", "override"]
settingKey: "niriLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window radius instead of theme radius")
checked: SettingsData.niriLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("niriLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("niriLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["niri", "radius", "override"]
settingKey: "niriLayoutRadiusOverride"
text: I18n.tr("Window Corner Radius")
description: I18n.tr("Rounded corners for windows")
visible: SettingsData.niriLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.niriLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("niriLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["niri", "border", "override"]
settingKey: "niriLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border/focus-ring width")
checked: SettingsData.niriLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("niriLayoutBorderSize", 2);
return;
}
SettingsData.set("niriLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["niri", "border", "override"]
settingKey: "niriLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border and focus ring")
visible: SettingsData.niriLayoutBorderSize >= 0
value: Math.max(0, SettingsData.niriLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("niriLayoutBorderSize", newValue)
}
}
SettingsCard {
tab: "theme"
tags: ["hyprland", "layout", "gaps", "radius", "window", "border", "rounding"]
title: I18n.tr("Hyprland Layout Overrides")
settingKey: "hyprlandLayout"
iconName: "crop_square"
visible: CompositorService.isHyprland
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "gaps", "override"]
settingKey: "hyprlandLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.hyprlandLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("hyprlandLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("hyprlandLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["hyprland", "gaps", "override"]
settingKey: "hyprlandLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows (gaps_in and gaps_out)")
visible: SettingsData.hyprlandLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.hyprlandLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "radius", "override", "rounding"]
settingKey: "hyprlandLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window rounding instead of theme radius")
checked: SettingsData.hyprlandLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("hyprlandLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("hyprlandLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["hyprland", "radius", "override", "rounding"]
settingKey: "hyprlandLayoutRadiusOverride"
text: I18n.tr("Window Rounding")
description: I18n.tr("Rounded corners for windows (decoration.rounding)")
visible: SettingsData.hyprlandLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.hyprlandLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "border", "override"]
settingKey: "hyprlandLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border size")
checked: SettingsData.hyprlandLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("hyprlandLayoutBorderSize", 2);
return;
}
SettingsData.set("hyprlandLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["hyprland", "border", "override"]
settingKey: "hyprlandLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border (general.border_size)")
visible: SettingsData.hyprlandLayoutBorderSize >= 0
value: Math.max(0, SettingsData.hyprlandLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("hyprlandLayoutBorderSize", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["hyprland", "resize", "border", "mouse", "drag"]
settingKey: "hyprlandResizeOnBorder"
text: I18n.tr("Resize on Border")
description: I18n.tr("Resize windows by dragging their edges with the mouse")
checked: SettingsData.hyprlandResizeOnBorder
onToggled: checked => SettingsData.set("hyprlandResizeOnBorder", checked)
}
}
SettingsCard {
tab: "theme"
tags: ["mangowc", "mango", "dwl", "layout", "gaps", "radius", "window", "border"]
title: I18n.tr("MangoWC Layout Overrides")
settingKey: "mangoLayout"
iconName: "crop_square"
visible: CompositorService.isDwl || CompositorService.isMango
SettingsToggleRow {
tab: "theme"
tags: ["mangowc", "mango", "gaps", "override"]
settingKey: "mangoLayoutGapsOverrideEnabled"
text: I18n.tr("Override Gaps")
description: I18n.tr("Use custom gaps instead of bar spacing")
checked: SettingsData.mangoLayoutGapsOverride >= 0
onToggled: checked => {
if (checked) {
const currentGaps = Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4));
SettingsData.set("mangoLayoutGapsOverride", currentGaps);
return;
}
SettingsData.set("mangoLayoutGapsOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["mangowc", "mango", "gaps", "override"]
settingKey: "mangoLayoutGapsOverride"
text: I18n.tr("Window Gaps")
description: I18n.tr("Space between windows (gappih/gappiv/gappoh/gappov)")
visible: SettingsData.mangoLayoutGapsOverride >= 0
value: Math.max(0, SettingsData.mangoLayoutGapsOverride)
minimum: 0
maximum: 50
unit: "px"
defaultValue: Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4))
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutGapsOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["mangowc", "mango", "radius", "override"]
settingKey: "mangoLayoutRadiusOverrideEnabled"
text: I18n.tr("Override Corner Radius")
description: I18n.tr("Use custom window radius instead of theme radius")
checked: SettingsData.mangoLayoutRadiusOverride >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("mangoLayoutRadiusOverride", SettingsData.cornerRadius);
return;
}
SettingsData.set("mangoLayoutRadiusOverride", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["mangowc", "mango", "radius", "override"]
settingKey: "mangoLayoutRadiusOverride"
text: I18n.tr("Window Corner Radius")
description: I18n.tr("Rounded corners for windows (border_radius)")
visible: SettingsData.mangoLayoutRadiusOverride >= 0
value: Math.max(0, SettingsData.mangoLayoutRadiusOverride)
minimum: 0
maximum: 100
unit: "px"
defaultValue: SettingsData.cornerRadius
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutRadiusOverride", newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["mangowc", "mango", "border", "override"]
settingKey: "mangoLayoutBorderSizeEnabled"
text: I18n.tr("Override Border Size")
description: I18n.tr("Use custom border size")
checked: SettingsData.mangoLayoutBorderSize >= 0
onToggled: checked => {
if (checked) {
SettingsData.set("mangoLayoutBorderSize", 2);
return;
}
SettingsData.set("mangoLayoutBorderSize", -1);
}
}
SettingsSliderRow {
tab: "theme"
tags: ["mangowc", "mango", "border", "override"]
settingKey: "mangoLayoutBorderSize"
text: I18n.tr("Border Size")
description: I18n.tr("Width of window border (borderpx)")
visible: SettingsData.mangoLayoutBorderSize >= 0
value: Math.max(0, SettingsData.mangoLayoutBorderSize)
minimum: 0
maximum: 10
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("mangoLayoutBorderSize", newValue)
}
}
SettingsCard { SettingsCard {
tab: "theme" tab: "theme"
tags: ["modal", "darken", "background", "overlay"] tags: ["modal", "darken", "background", "overlay"]
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Services
import qs.Widgets import qs.Widgets
StyledRect { StyledRect {
@@ -12,6 +13,7 @@ StyledRect {
property string tab: "" property string tab: ""
property var tags: [] property var tags: []
property string settingKey: ""
property string title: "" property string title: ""
property string description: "" property string description: ""
@@ -29,6 +31,34 @@ StyledRect {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
const key = settingKey;
Qt.callLater(() => {
if (!root.parent)
return;
const flickable = findParentFlickable();
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
}
Component.onDestruction: {
if (settingKey)
SettingsSearchService.unregisterCard(settingKey);
}
Column { Column {
id: contentColumn id: contentColumn
anchors.left: parent.left anchors.left: parent.left
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Services
import qs.Widgets import qs.Widgets
StyledRect { StyledRect {
@@ -12,6 +13,7 @@ StyledRect {
property string tab: "" property string tab: ""
property var tags: [] property var tags: []
property string settingKey: ""
property string title: "" property string title: ""
property string description: "" property string description: ""
@@ -28,6 +30,34 @@ StyledRect {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
function findParentFlickable() {
let p = root.parent;
while (p) {
if (p.hasOwnProperty("contentY") && p.hasOwnProperty("contentItem"))
return p;
p = p.parent;
}
return null;
}
Component.onCompleted: {
if (!settingKey)
return;
const key = settingKey;
Qt.callLater(() => {
if (!root.parent)
return;
const flickable = findParentFlickable();
if (flickable)
SettingsSearchService.registerCard(key, root, flickable);
});
}
Component.onDestruction: {
if (settingKey)
SettingsSearchService.unregisterCard(settingKey);
}
Column { Column {
id: mainColumn id: mainColumn
anchors.left: parent.left anchors.left: parent.left
@@ -25,12 +25,14 @@ Item {
} }
property bool hasMultipleBars: SettingsData.barConfigs.length > 1 property bool hasMultipleBars: SettingsData.barConfigs.length > 1
property int pluginCatalogRevision: 0
DankTooltipV2 { DankTooltipV2 {
id: sharedTooltip id: sharedTooltip
} }
property var baseWidgetDefinitions: { property var baseWidgetDefinitions: {
pluginCatalogRevision;
var coreWidgets = [ var coreWidgets = [
{ {
"id": "layout", "id": "layout",
@@ -274,6 +276,30 @@ Item {
return coreWidgets; return coreWidgets;
} }
Connections {
target: PluginService
function onPluginDataChanged() {
widgetsTab.pluginCatalogRevision++;
}
function onPluginListUpdated() {
widgetsTab.pluginCatalogRevision++;
}
function onPluginLoaded() {
widgetsTab.pluginCatalogRevision++;
}
function onPluginStateChanged() {
widgetsTab.pluginCatalogRevision++;
}
function onPluginUnloaded() {
widgetsTab.pluginCatalogRevision++;
}
}
property var defaultLeftWidgets: [ property var defaultLeftWidgets: [
{ {
"id": "launcherButton", "id": "launcherButton",
+18 -43
View File
@@ -17,6 +17,8 @@ Item {
LayoutMirroring.childrenInherit: true LayoutMirroring.childrenInherit: true
property var parentModal: null property var parentModal: null
property bool pageActive: true
property bool componentReady: false
property var windowRulesIncludeStatus: ({ property var windowRulesIncludeStatus: ({
"exists": false, "exists": false,
"included": false, "included": false,
@@ -32,6 +34,13 @@ Item {
property string expandedExternalId: "" property string expandedExternalId: ""
readonly property string dmsRulesFileName: CompositorService.isNiri ? "dms/windowrules.kdl" : CompositorService.isMango ? "dms/windowrules.conf" : "dms/windowrules.lua" readonly property string dmsRulesFileName: CompositorService.isNiri ? "dms/windowrules.kdl" : CompositorService.isMango ? "dms/windowrules.conf" : "dms/windowrules.lua"
Component.onDestruction: SettingsSearchService.unregisterCard("windowRules")
onPageActiveChanged: {
if (componentReady && pageActive)
loadWindowRules();
}
readonly property var matchLabels: ({ readonly property var matchLabels: ({
"appId": I18n.tr("App ID"), "appId": I18n.tr("App ID"),
"title": I18n.tr("Title"), "title": I18n.tr("Title"),
@@ -182,12 +191,15 @@ Item {
function loadWindowRules() { function loadWindowRules() {
const compositor = CompositorService.compositor; const compositor = CompositorService.compositor;
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "mango") { if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "mango") {
checkingInclude = false;
windowRules = []; windowRules = [];
externalRules = []; externalRules = [];
return; return;
} }
checkingInclude = true;
Proc.runCommand("load-windowrules", ["dms", "config", "windowrules", "list", compositor], (output, exitCode) => { Proc.runCommand("load-windowrules", ["dms", "config", "windowrules", "list", compositor], (output, exitCode) => {
checkingInclude = false;
if (exitCode !== 0) { if (exitCode !== 0) {
windowRules = []; windowRules = [];
externalRules = []; externalRules = [];
@@ -258,44 +270,6 @@ Item {
}); });
} }
function checkWindowRulesIncludeStatus() {
const compositor = CompositorService.compositor;
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "mango") {
windowRulesIncludeStatus = {
"exists": false,
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
const filename = (compositor === "niri") ? "windowrules.kdl" : (compositor === "mango") ? "windowrules.conf" : "windowrules.lua";
checkingInclude = true;
Proc.runCommand("check-windowrules-include", ["dms", "config", "resolve-include", compositor, filename], (output, exitCode) => {
checkingInclude = false;
if (exitCode !== 0) {
windowRulesIncludeStatus = {
"exists": false,
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
try {
windowRulesIncludeStatus = JSON.parse(output.trim());
} catch (e) {
windowRulesIncludeStatus = {
"exists": false,
"included": false,
"configFormat": "",
"readOnly": false
};
}
});
}
function fixWindowRulesInclude() { function fixWindowRulesInclude() {
if (readOnly) { if (readOnly) {
showHyprlandReadOnlyWarning(); showHyprlandReadOnlyWarning();
@@ -320,7 +294,6 @@ Item {
return; return;
if (CompositorService.isMango) if (CompositorService.isMango)
MangoService.reloadConfig(); MangoService.reloadConfig();
checkWindowRulesIncludeStatus();
loadWindowRules(); loadWindowRules();
}); });
} }
@@ -372,10 +345,12 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) { componentReady = true;
checkWindowRulesIncludeStatus(); Qt.callLater(() => {
loadWindowRules(); SettingsSearchService.registerCard("windowRules", headerSection, flickable);
} if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango)
loadWindowRules();
});
} }
DankFlickable { DankFlickable {
@@ -0,0 +1,230 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Modules.Settings.Widgets
SettingsCard {
iconName: "palette"
title: I18n.tr("Workspace Appearance")
settingKey: "workspaceAppearance"
collapsible: true
expanded: false
SettingsButtonGroupRow {
text: I18n.tr("Focused Color")
model: ["pri", "s", "sc", "sch", "none"]
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceColorMode) {
case "s":
return 1;
case "sc":
return 2;
case "sch":
return 3;
case "none":
return 4;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["default", "s", "sc", "sch", "none"];
SettingsData.set("workspaceColorMode", modes[index]);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
SettingsButtonGroupRow {
text: I18n.tr("Occupied Color")
model: ["none", "sec", "s", "sc", "sch", "schh"]
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceOccupiedColorMode) {
case "sec":
return 1;
case "s":
return 2;
case "sc":
return 3;
case "sch":
return 4;
case "schh":
return 5;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["none", "sec", "s", "sc", "sch", "schh"];
SettingsData.set("workspaceOccupiedColorMode", modes[index]);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango
}
SettingsButtonGroupRow {
text: I18n.tr("Unfocused Color")
model: ["def", "s", "sc", "sch"]
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceUnfocusedColorMode) {
case "s":
return 1;
case "sc":
return 2;
case "sch":
return 3;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["default", "s", "sc", "sch"];
SettingsData.set("workspaceUnfocusedColorMode", modes[index]);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
}
SettingsButtonGroupRow {
text: I18n.tr("Urgent Color")
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
model: ["err", "pri", "sec", "s", "sc"]
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceUrgentColorMode) {
case "primary":
return 1;
case "secondary":
return 2;
case "s":
return 3;
case "sc":
return 4;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["default", "primary", "secondary", "s", "sc"];
SettingsData.set("workspaceUrgentColorMode", modes[index]);
}
}
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 {
width: parent.width
spacing: Theme.spacingS
visible: SettingsData.workspaceFocusedBorderEnabled
leftPadding: Theme.spacingM
SettingsButtonGroupRow {
width: parent.width - parent.leftPadding
text: I18n.tr("Border Color")
model: [I18n.tr("Surface"), I18n.tr("Secondary"), I18n.tr("Primary")]
currentIndex: {
switch (SettingsData.workspaceFocusedBorderColor) {
case "surfaceText":
return 0;
case "secondary":
return 1;
case "primary":
return 2;
default:
return 2;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
let newColor = "primary";
switch (index) {
case 0:
newColor = "surfaceText";
break;
case 1:
newColor = "secondary";
break;
case 2:
newColor = "primary";
break;
}
SettingsData.set("workspaceFocusedBorderColor", newColor);
}
}
SettingsSliderRow {
width: parent.width - parent.leftPadding
text: I18n.tr("Thickness")
value: SettingsData.workspaceFocusedBorderThickness
minimum: 1
maximum: 6
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("workspaceFocusedBorderThickness", newValue)
}
}
}
+3 -226
View File
@@ -15,7 +15,9 @@ Item {
Column { Column {
id: mainColumn id: mainColumn
topPadding: 4
topPadding: Theme.spacingXL
bottomPadding: Theme.spacingXL
width: Math.min(550, parent.width - Theme.spacingL * 2) width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL spacing: Theme.spacingXL
@@ -196,231 +198,6 @@ Item {
} }
} }
SettingsCard {
width: parent.width
iconName: "palette"
title: I18n.tr("Workspace Appearance")
settingKey: "workspaceAppearance"
SettingsButtonGroupRow {
text: I18n.tr("Focused Color")
model: ["pri", "s", "sc", "sch", "none"]
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceColorMode) {
case "s":
return 1;
case "sc":
return 2;
case "sch":
return 3;
case "none":
return 4;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["default", "s", "sc", "sch", "none"];
SettingsData.set("workspaceColorMode", modes[index]);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
}
SettingsButtonGroupRow {
text: I18n.tr("Occupied Color")
model: ["none", "sec", "s", "sc", "sch", "schh"]
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceOccupiedColorMode) {
case "sec":
return 1;
case "s":
return 2;
case "sc":
return 3;
case "sch":
return 4;
case "schh":
return 5;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["none", "sec", "s", "sc", "sch", "schh"];
SettingsData.set("workspaceOccupiedColorMode", modes[index]);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango
}
SettingsButtonGroupRow {
text: I18n.tr("Unfocused Color")
model: ["def", "s", "sc", "sch"]
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceUnfocusedColorMode) {
case "s":
return 1;
case "sc":
return 2;
case "sch":
return 3;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["default", "s", "sc", "sch"];
SettingsData.set("workspaceUnfocusedColorMode", modes[index]);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.15
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
}
SettingsButtonGroupRow {
text: I18n.tr("Urgent Color")
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
model: ["err", "pri", "sec", "s", "sc"]
buttonHeight: 22
minButtonWidth: 36
buttonPadding: Theme.spacingS
checkIconSize: Theme.iconSizeSmall - 2
textSize: Theme.fontSizeSmall - 1
spacing: 1
currentIndex: {
switch (SettingsData.workspaceUrgentColorMode) {
case "primary":
return 1;
case "secondary":
return 2;
case "s":
return 3;
case "sc":
return 4;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
const modes = ["default", "primary", "secondary", "s", "sc"];
SettingsData.set("workspaceUrgentColorMode", modes[index]);
}
}
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 {
width: parent.width
spacing: Theme.spacingS
visible: SettingsData.workspaceFocusedBorderEnabled
leftPadding: Theme.spacingM
SettingsButtonGroupRow {
width: parent.width - parent.leftPadding
text: I18n.tr("Border Color")
model: [I18n.tr("Surface"), I18n.tr("Secondary"), I18n.tr("Primary")]
currentIndex: {
switch (SettingsData.workspaceFocusedBorderColor) {
case "surfaceText":
return 0;
case "secondary":
return 1;
case "primary":
return 2;
default:
return 2;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
let newColor = "primary";
switch (index) {
case 0:
newColor = "surfaceText";
break;
case 1:
newColor = "secondary";
break;
case 2:
newColor = "primary";
break;
}
SettingsData.set("workspaceFocusedBorderColor", newColor);
}
}
SettingsSliderRow {
width: parent.width - parent.leftPadding
text: I18n.tr("Thickness")
value: SettingsData.workspaceFocusedBorderThickness
minimum: 1
maximum: 6
unit: "px"
defaultValue: 2
onSliderValueChanged: newValue => SettingsData.set("workspaceFocusedBorderThickness", newValue)
}
}
}
SettingsCard { SettingsCard {
width: parent.width width: parent.width
iconName: "label" iconName: "label"
+4 -3
View File
@@ -7,6 +7,7 @@ Item {
property color rippleColor: Theme.primary property color rippleColor: Theme.primary
property real cornerRadius: 0 property real cornerRadius: 0
property bool enableRipple: typeof SettingsData !== "undefined" ? (SettingsData.enableRippleEffects ?? true) : true property bool enableRipple: typeof SettingsData !== "undefined" ? (SettingsData.enableRippleEffects ?? true) : true
property int animationDuration: Theme.expressiveDurations.expressiveDefaultSpatial
property real _rippleX: 0 property real _rippleX: 0
property real _rippleY: 0 property real _rippleY: 0
@@ -58,18 +59,18 @@ Item {
property: "rippleRadius" property: "rippleRadius"
from: 0 from: 0
to: root._rippleMaxRadius to: root._rippleMaxRadius
duration: Theme.expressiveDurations.expressiveDefaultSpatial duration: root.animationDuration
easing.bezierCurve: Theme.expressiveCurves.standardDecel easing.bezierCurve: Theme.expressiveCurves.standardDecel
} }
SequentialAnimation { SequentialAnimation {
PauseAnimation { PauseAnimation {
duration: Math.round(Theme.expressiveDurations.expressiveDefaultSpatial * 0.6) duration: Math.round(root.animationDuration * 0.6)
} }
DankAnim { DankAnim {
target: rippleFx target: rippleFx
property: "rippleOpacity" property: "rippleOpacity"
to: 0 to: 0
duration: Theme.expressiveDurations.expressiveDefaultSpatial duration: root.animationDuration
easing.bezierCurve: Theme.expressiveCurves.standard easing.bezierCurve: Theme.expressiveCurves.standard
} }
} }
@@ -62,7 +62,20 @@ CATEGORY_KEYWORDS = {
"Time & Weather": ["clock", "forecast", "date"], "Time & Weather": ["clock", "forecast", "date"],
"Keyboard Shortcuts": ["keys", "bindings", "hotkey"], "Keyboard Shortcuts": ["keys", "bindings", "hotkey"],
"Dank Bar": ["panel", "topbar", "statusbar"], "Dank Bar": ["panel", "topbar", "statusbar"],
"Workspaces": ["virtual desktops", "spaces"], "Compositor": [
"virtual desktops",
"spaces",
"window",
"rules",
"matching",
"floating",
"fullscreen",
"opacity",
"gaps",
"borders",
"corner rounding",
"overrides",
],
"Dock": ["taskbar", "launcher bar"], "Dock": ["taskbar", "launcher bar"],
"Network": ["connectivity", "online"], "Network": ["connectivity", "online"],
"System": ["os", "linux"], "System": ["os", "linux"],
@@ -82,14 +95,6 @@ CATEGORY_KEYWORDS = {
"Displays": ["monitor", "screen", "resolution"], "Displays": ["monitor", "screen", "resolution"],
"Desktop Widgets": ["conky", "desktop clock"], "Desktop Widgets": ["conky", "desktop clock"],
"Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"], "Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"],
"Window Rules": [
"window",
"rules",
"matching",
"floating",
"fullscreen",
"opacity",
],
"Locale": ["locale", "language", "country"], "Locale": ["locale", "language", "country"],
"Greeter": ["login", "greetd", "display manager"], "Greeter": ["login", "greetd", "display manager"],
"Multiplexers": ["tmux", "zellij", "terminal"], "Multiplexers": ["tmux", "zellij", "terminal"],
@@ -104,8 +109,13 @@ TAB_INDEX_MAP = {
"TimeWeatherTab.qml": 1, "TimeWeatherTab.qml": 1,
"KeybindsTab.qml": 2, "KeybindsTab.qml": 2,
"DankBarTab.qml": 3, "DankBarTab.qml": 3,
"CompositorTab.qml": 4,
"CompositorLayoutTab.qml": 4,
"WorkspacesTab.qml": 4, "WorkspacesTab.qml": 4,
"WindowRulesTab.qml": 4,
"DockTab.qml": 5, "DockTab.qml": 5,
"DankBarAppearanceTab.qml": 6,
"WorkspaceAppearanceCard.qml": 6,
"NetworkTab.qml": 7, "NetworkTab.qml": 7,
"PrinterTab.qml": 8, "PrinterTab.qml": 8,
"LauncherTab.qml": 9, "LauncherTab.qml": 9,
@@ -127,7 +137,6 @@ TAB_INDEX_MAP = {
"GammaControlTab.qml": 25, "GammaControlTab.qml": 25,
"DisplayWidgetsTab.qml": 26, "DisplayWidgetsTab.qml": 26,
"DesktopWidgetsTab.qml": 27, "DesktopWidgetsTab.qml": 27,
"WindowRulesTab.qml": 28,
"AudioTab.qml": 29, "AudioTab.qml": 29,
"LocaleTab.qml": 30, "LocaleTab.qml": 30,
"GreeterTab.qml": 31, "GreeterTab.qml": 31,
@@ -143,8 +152,9 @@ TAB_CATEGORY_MAP = {
1: "Time & Weather", 1: "Time & Weather",
2: "Keyboard Shortcuts", 2: "Keyboard Shortcuts",
3: "Dank Bar", 3: "Dank Bar",
4: "Workspaces", 4: "Compositor",
5: "Dock", 5: "Dock",
6: "Dank Bar",
7: "Network", 7: "Network",
8: "System", 8: "System",
9: "Launcher", 9: "Launcher",
@@ -166,7 +176,6 @@ TAB_CATEGORY_MAP = {
25: "Displays", 25: "Displays",
26: "Displays", 26: "Displays",
27: "Desktop Widgets", 27: "Desktop Widgets",
28: "Window Rules",
29: "Audio", 29: "Audio",
30: "Locale", 30: "Locale",
31: "Greeter", 31: "Greeter",
@@ -302,9 +311,9 @@ def extract_property(block, prop_name):
def find_settings_components(content, filename): def find_settings_components(content, filename):
results = [] results = []
tab_index = TAB_INDEX_MAP.get(filename, -1) file_tab_index = TAB_INDEX_MAP.get(filename, -1)
if tab_index == -1: if file_tab_index == -1:
return results return results
for component in SEARCHABLE_COMPONENTS: for component in SEARCHABLE_COMPONENTS:
@@ -321,6 +330,11 @@ def find_settings_components(content, filename):
if not setting_key: if not setting_key:
continue continue
tab_index = file_tab_index
tab_raw = extract_property(block, "tab")
if tab_raw and tab_raw.strip("\"'") == "appearance":
tab_index = 6
title_raw = extract_property(block, "title") title_raw = extract_property(block, "title")
text_raw = extract_property(block, "text") text_raw = extract_property(block, "text")
label = None label = None
@@ -489,7 +503,7 @@ def extract_settings_index(root_dir):
seen_keys = set() seen_keys = set()
for qml_file in settings_dir.glob("*.qml"): for qml_file in settings_dir.glob("*.qml"):
if not qml_file.name.endswith("Tab.qml"): if qml_file.name not in TAB_INDEX_MAP:
continue continue
with open(qml_file, "r", encoding="utf-8") as f: with open(qml_file, "r", encoding="utf-8") as f:
@@ -502,6 +516,24 @@ def extract_settings_index(root_dir):
seen_keys.add(key) seen_keys.add(key)
all_entries.append(entry) all_entries.append(entry)
if "windowRules" not in seen_keys:
all_entries.append(
{
"section": "windowRules",
"label": "Window Rules",
"tabIndex": 4,
"category": "Compositor",
"keywords": enrich_keywords(
"Window Rules",
"Define compositor rules for window behavior",
"Compositor",
["matching", "floating", "fullscreen", "opacity"],
),
"icon": "select_window",
"description": "Define compositor rules for window behavior",
}
)
return all_entries return all_entries
File diff suppressed because it is too large Load Diff