mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 04:09:15 -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:
@@ -11,6 +11,10 @@ Singleton {
|
||||
readonly property int durMed: 450
|
||||
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 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]
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -947,7 +947,7 @@ Item {
|
||||
|
||||
function tabs(): string {
|
||||
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 ids = [];
|
||||
var structure = modal.sidebar?.categoryStructure ?? [];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.Settings
|
||||
import qs.Widgets
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
@@ -97,7 +98,24 @@ FocusScope {
|
||||
visible: 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: {
|
||||
if (active && item)
|
||||
@@ -432,19 +450,36 @@ FocusScope {
|
||||
|
||||
Loader {
|
||||
id: widgetsLoader
|
||||
|
||||
property bool loadedOnce: false
|
||||
|
||||
anchors.fill: parent
|
||||
active: root.currentIndex === 22
|
||||
visible: active
|
||||
focus: active
|
||||
active: root.currentIndex === 22 || loadedOnce
|
||||
visible: root.currentIndex === 22 && status === Loader.Ready
|
||||
focus: visible
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: WidgetsTab {
|
||||
parentModal: root.parentModal
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (active && item)
|
||||
onLoaded: {
|
||||
loadedOnce = true;
|
||||
if (visible && item)
|
||||
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 {
|
||||
@@ -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 {
|
||||
id: audioLoader
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -23,6 +23,7 @@ Rectangle {
|
||||
property bool searchActive: searchField.text.length > 0
|
||||
property int searchSelectedIndex: 0
|
||||
property int keyboardHighlightIndex: -1
|
||||
readonly property int navigationStateDuration: Theme.currentAnimationSpeed === SettingsData.AnimationSpeed.None ? 0 : Anims.settingsNavigationStateDuration
|
||||
|
||||
function focusSearch() {
|
||||
searchField.forceActiveFocus();
|
||||
@@ -115,6 +116,12 @@ Rectangle {
|
||||
"icon": "tune",
|
||||
"tabIndex": 3
|
||||
},
|
||||
{
|
||||
"id": "dankbar_appearance",
|
||||
"text": I18n.tr("Appearance"),
|
||||
"icon": "palette",
|
||||
"tabIndex": 6
|
||||
},
|
||||
{
|
||||
"id": "dankbar_widgets",
|
||||
"text": I18n.tr("Widgets"),
|
||||
@@ -131,16 +138,10 @@ Rectangle {
|
||||
},
|
||||
{
|
||||
"id": "workspaces_widgets",
|
||||
"text": I18n.tr("Workspaces & Widgets"),
|
||||
"text": I18n.tr("Widgets & Notifications"),
|
||||
"icon": "dashboard",
|
||||
"collapsedByDefault": true,
|
||||
"children": [
|
||||
{
|
||||
"id": "workspaces",
|
||||
"text": I18n.tr("Workspaces"),
|
||||
"icon": "view_module",
|
||||
"tabIndex": 4
|
||||
},
|
||||
{
|
||||
"id": "media_player",
|
||||
"text": I18n.tr("Media Player"),
|
||||
@@ -187,6 +188,12 @@ Rectangle {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "compositor",
|
||||
"text": I18n.tr("Compositor"),
|
||||
"icon": "layers",
|
||||
"tabIndex": 4
|
||||
},
|
||||
{
|
||||
"id": "keybinds",
|
||||
"text": I18n.tr("Keyboard Shortcuts"),
|
||||
@@ -305,13 +312,6 @@ Rectangle {
|
||||
"text": I18n.tr("Users"),
|
||||
"icon": "manage_accounts",
|
||||
"tabIndex": 35
|
||||
},
|
||||
{
|
||||
"id": "window_rules",
|
||||
"text": I18n.tr("Window Rules"),
|
||||
"icon": "select_window",
|
||||
"tabIndex": 28,
|
||||
"windowRulesCapable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -544,6 +544,8 @@ Rectangle {
|
||||
return -1;
|
||||
|
||||
var normalized = name.toLowerCase().replace(/[_\-\s]/g, "");
|
||||
if (normalized === "workspaces")
|
||||
normalized = "compositor";
|
||||
|
||||
for (var i = 0; i < categoryStructure.length; i++) {
|
||||
var cat = categoryStructure[i];
|
||||
@@ -588,7 +590,7 @@ Rectangle {
|
||||
id: __m1
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
text: I18n.tr("Workspaces & Widgets")
|
||||
text: I18n.tr("Widgets & Notifications")
|
||||
}
|
||||
StyledTextMetrics {
|
||||
id: __m2
|
||||
@@ -782,6 +784,7 @@ Rectangle {
|
||||
id: resultRipple
|
||||
rippleColor: root.searchSelectedIndex === resultDelegate.index ? Theme.buttonText : Theme.surfaceText
|
||||
cornerRadius: resultDelegate.radius
|
||||
animationDuration: Anims.settingsNavigationRippleDuration
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -837,8 +840,9 @@ Rectangle {
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
duration: root.navigationStateDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Anims.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -912,6 +916,7 @@ Rectangle {
|
||||
id: categoryRipple
|
||||
rippleColor: categoryRow.isActive ? Theme.buttonText : Theme.surfaceText
|
||||
cornerRadius: categoryRow.radius
|
||||
animationDuration: Anims.settingsNavigationRippleDuration
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -967,8 +972,9 @@ Rectangle {
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
duration: root.navigationStateDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Anims.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1009,6 +1015,7 @@ Rectangle {
|
||||
id: childRipple
|
||||
rippleColor: childDelegate.isActive ? Theme.buttonText : Theme.surfaceText
|
||||
cornerRadius: childDelegate.radius
|
||||
animationDuration: Anims.settingsNavigationRippleDuration
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -1049,8 +1056,9 @@ Rectangle {
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
duration: root.navigationStateDuration
|
||||
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
|
||||
}
|
||||
@@ -13,7 +13,22 @@ Item {
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
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: {
|
||||
selectedBarId;
|
||||
@@ -21,6 +36,14 @@ Item {
|
||||
const index = SettingsData.barConfigs.findIndex(cfg => cfg.id === selectedBarId);
|
||||
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: {
|
||||
selectedBarId;
|
||||
@@ -210,10 +233,33 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
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 {
|
||||
iconName: "dashboard"
|
||||
title: I18n.tr("Bar Configurations")
|
||||
settingKey: "barConfigurations"
|
||||
visible: !dankBarTab.appearanceOnly
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
@@ -410,7 +456,7 @@ Item {
|
||||
SettingsCard {
|
||||
iconName: selectedBarConfig?.enabled ? "visibility" : "visibility_off"
|
||||
title: I18n.tr("Enable Bar")
|
||||
visible: selectedBarId !== "default"
|
||||
visible: !dankBarTab.appearanceOnly && selectedBarId !== "default"
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Toggle visibility of this bar configuration")
|
||||
@@ -426,7 +472,7 @@ Item {
|
||||
iconName: "vertical_align_center"
|
||||
title: I18n.tr("Position")
|
||||
settingKey: "barPosition"
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
@@ -486,7 +532,7 @@ Item {
|
||||
settingKey: "barDisplay"
|
||||
collapsible: true
|
||||
expanded: false
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
@@ -588,12 +634,12 @@ Item {
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
iconName: "visibility_off"
|
||||
iconName: "visibility"
|
||||
title: I18n.tr("Visibility")
|
||||
settingKey: "barVisibility"
|
||||
collapsible: true
|
||||
expanded: false
|
||||
visible: selectedBarConfig?.enabled
|
||||
expanded: true
|
||||
visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Auto-hide")
|
||||
@@ -751,7 +797,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: SettingsData.frameEnabled
|
||||
visible: !dankBarTab.appearanceOnly && SettingsData.frameEnabled
|
||||
parentModal: dankBarTab.parentModal
|
||||
settingLabel: I18n.tr("Bar spacing and size")
|
||||
reason: I18n.tr("Managed by Frame")
|
||||
@@ -761,7 +807,7 @@ Item {
|
||||
iconName: "space_bar"
|
||||
title: I18n.tr("Spacing")
|
||||
settingKey: "barSpacing"
|
||||
visible: (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled
|
||||
visible: !dankBarTab.appearanceOnly && (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled
|
||||
|
||||
SettingsSliderRow {
|
||||
id: edgeSpacingSlider
|
||||
@@ -911,10 +957,11 @@ Item {
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "appearance"
|
||||
iconName: "opacity"
|
||||
title: I18n.tr("Transparency")
|
||||
settingKey: "barTransparency"
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
|
||||
SettingsSliderRow {
|
||||
id: barTransparencySlider
|
||||
@@ -973,10 +1020,12 @@ Item {
|
||||
|
||||
SettingsSliderCard {
|
||||
id: fontScaleSliderCard
|
||||
tab: "appearance"
|
||||
settingKey: "barFontScale"
|
||||
iconName: "text_fields"
|
||||
title: I18n.tr("Font Scale")
|
||||
description: I18n.tr("Scale DankBar font sizes independently")
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
minimum: 50
|
||||
maximum: 200
|
||||
value: Math.round((selectedBarConfig?.fontScale ?? 1.0) * 100)
|
||||
@@ -998,10 +1047,12 @@ Item {
|
||||
|
||||
SettingsSliderCard {
|
||||
id: iconScaleSliderCard
|
||||
tab: "appearance"
|
||||
settingKey: "barIconScale"
|
||||
iconName: "interests"
|
||||
title: I18n.tr("Icon Scale")
|
||||
description: I18n.tr("Scale DankBar icon sizes independently")
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
minimum: 50
|
||||
maximum: 200
|
||||
value: Math.round((selectedBarConfig?.iconScale ?? 1.0) * 100)
|
||||
@@ -1021,13 +1072,18 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
WorkspaceAppearanceCard {
|
||||
visible: dankBarTab.appearanceOnly
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "appearance"
|
||||
iconName: "rounded_corner"
|
||||
title: I18n.tr("Corners & Background")
|
||||
settingKey: "barCorners"
|
||||
collapsible: true
|
||||
expanded: false
|
||||
visible: selectedBarConfig?.enabled
|
||||
expanded: true
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: SettingsData.frameEnabled
|
||||
@@ -1144,7 +1200,7 @@ Item {
|
||||
iconName: "fit_screen"
|
||||
title: I18n.tr("Maximize Detection")
|
||||
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
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
maximizeDetection: checked
|
||||
@@ -1152,10 +1208,11 @@ Item {
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
tab: "appearance"
|
||||
iconName: "filter_b_and_w"
|
||||
title: I18n.tr("System Tray Icon Tint")
|
||||
settingKey: "trayIconTint"
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Choose monochrome or a theme color tint for system tray icons")
|
||||
@@ -1251,9 +1308,11 @@ Item {
|
||||
}
|
||||
|
||||
SettingsToggleCard {
|
||||
tab: "appearance"
|
||||
settingKey: "barBorder"
|
||||
iconName: "border_style"
|
||||
title: I18n.tr("Border")
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled && !dankBarTab.connectedFrameModeActive
|
||||
checked: selectedBarConfig?.borderEnabled ?? false
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
borderEnabled: checked
|
||||
@@ -1344,9 +1403,11 @@ Item {
|
||||
}
|
||||
|
||||
SettingsToggleCard {
|
||||
tab: "appearance"
|
||||
settingKey: "barWidgetOutline"
|
||||
iconName: "highlight"
|
||||
title: I18n.tr("Widget Outline")
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
checked: selectedBarConfig?.widgetOutlineEnabled ?? false
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
widgetOutlineEnabled: checked
|
||||
@@ -1437,7 +1498,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsControlledByFrame {
|
||||
visible: dankBarTab.connectedFrameModeActive
|
||||
visible: dankBarTab.appearanceOnly && dankBarTab.connectedFrameModeActive
|
||||
parentModal: dankBarTab.parentModal
|
||||
settingLabel: I18n.tr("Bar shadow, border, and corners")
|
||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||
@@ -1445,12 +1506,13 @@ Item {
|
||||
|
||||
SettingsCard {
|
||||
id: shadowCard
|
||||
tab: "appearance"
|
||||
iconName: "layers"
|
||||
title: I18n.tr("Shadow Override", "bar shadow settings card")
|
||||
settingKey: "barShadow"
|
||||
collapsible: true
|
||||
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 isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom"
|
||||
@@ -1512,6 +1574,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsDropdownRow {
|
||||
tab: "appearance"
|
||||
visible: shadowCard.shadowActive
|
||||
text: I18n.tr("Direction Source", "bar shadow direction source")
|
||||
description: I18n.tr("Choose how this bar resolves shadow direction")
|
||||
@@ -1545,6 +1608,7 @@ Item {
|
||||
}
|
||||
|
||||
SettingsDropdownRow {
|
||||
tab: "appearance"
|
||||
visible: shadowCard.shadowActive && shadowCard.directionSource === "manual"
|
||||
text: I18n.tr("Manual Direction", "bar manual shadow direction")
|
||||
description: I18n.tr("Use a fixed shadow direction for this bar")
|
||||
@@ -1680,7 +1744,7 @@ Item {
|
||||
iconName: "mouse"
|
||||
title: I18n.tr("Scroll Wheel")
|
||||
description: I18n.tr("Control workspaces and columns by scrolling on the bar")
|
||||
visible: selectedBarConfig?.enabled
|
||||
visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
checked: selectedBarConfig?.scrollEnabled ?? true
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
scrollEnabled: checked
|
||||
|
||||
@@ -1640,7 +1640,7 @@ Item {
|
||||
SettingsControlledByFrame {
|
||||
visible: themeColorsTab.connectedFrameModeActive
|
||||
parentModal: themeColorsTab.parentModal
|
||||
settingLabel: I18n.tr("Surface Opacity")
|
||||
settingLabel: I18n.tr("Transparency")
|
||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||
}
|
||||
|
||||
@@ -1648,7 +1648,7 @@ Item {
|
||||
tab: "theme"
|
||||
tags: ["surface", "popup", "transparency", "opacity", "modal"]
|
||||
settingKey: "popupTransparency"
|
||||
text: I18n.tr("Surface Opacity")
|
||||
text: I18n.tr("Transparency")
|
||||
description: I18n.tr("Controls opacity of all popouts, modals, and their content layers")
|
||||
visible: !themeColorsTab.connectedFrameModeActive
|
||||
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 {
|
||||
tab: "theme"
|
||||
tags: ["modal", "darken", "background", "overlay"]
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
StyledRect {
|
||||
@@ -12,6 +13,7 @@ StyledRect {
|
||||
|
||||
property string tab: ""
|
||||
property var tags: []
|
||||
property string settingKey: ""
|
||||
|
||||
property string title: ""
|
||||
property string description: ""
|
||||
@@ -29,6 +31,34 @@ StyledRect {
|
||||
radius: Theme.cornerRadius
|
||||
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 {
|
||||
id: contentColumn
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
StyledRect {
|
||||
@@ -12,6 +13,7 @@ StyledRect {
|
||||
|
||||
property string tab: ""
|
||||
property var tags: []
|
||||
property string settingKey: ""
|
||||
|
||||
property string title: ""
|
||||
property string description: ""
|
||||
@@ -28,6 +30,34 @@ StyledRect {
|
||||
radius: Theme.cornerRadius
|
||||
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 {
|
||||
id: mainColumn
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -25,12 +25,14 @@ Item {
|
||||
}
|
||||
|
||||
property bool hasMultipleBars: SettingsData.barConfigs.length > 1
|
||||
property int pluginCatalogRevision: 0
|
||||
|
||||
DankTooltipV2 {
|
||||
id: sharedTooltip
|
||||
}
|
||||
|
||||
property var baseWidgetDefinitions: {
|
||||
pluginCatalogRevision;
|
||||
var coreWidgets = [
|
||||
{
|
||||
"id": "layout",
|
||||
@@ -274,6 +276,30 @@ Item {
|
||||
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: [
|
||||
{
|
||||
"id": "launcherButton",
|
||||
|
||||
@@ -17,6 +17,8 @@ Item {
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property var parentModal: null
|
||||
property bool pageActive: true
|
||||
property bool componentReady: false
|
||||
property var windowRulesIncludeStatus: ({
|
||||
"exists": false,
|
||||
"included": false,
|
||||
@@ -32,6 +34,13 @@ Item {
|
||||
property string expandedExternalId: ""
|
||||
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: ({
|
||||
"appId": I18n.tr("App ID"),
|
||||
"title": I18n.tr("Title"),
|
||||
@@ -182,12 +191,15 @@ Item {
|
||||
function loadWindowRules() {
|
||||
const compositor = CompositorService.compositor;
|
||||
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "mango") {
|
||||
checkingInclude = false;
|
||||
windowRules = [];
|
||||
externalRules = [];
|
||||
return;
|
||||
}
|
||||
|
||||
checkingInclude = true;
|
||||
Proc.runCommand("load-windowrules", ["dms", "config", "windowrules", "list", compositor], (output, exitCode) => {
|
||||
checkingInclude = false;
|
||||
if (exitCode !== 0) {
|
||||
windowRules = [];
|
||||
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() {
|
||||
if (readOnly) {
|
||||
showHyprlandReadOnlyWarning();
|
||||
@@ -320,7 +294,6 @@ Item {
|
||||
return;
|
||||
if (CompositorService.isMango)
|
||||
MangoService.reloadConfig();
|
||||
checkWindowRulesIncludeStatus();
|
||||
loadWindowRules();
|
||||
});
|
||||
}
|
||||
@@ -372,10 +345,12 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) {
|
||||
checkWindowRulesIncludeStatus();
|
||||
loadWindowRules();
|
||||
}
|
||||
componentReady = true;
|
||||
Qt.callLater(() => {
|
||||
SettingsSearchService.registerCard("windowRules", headerSection, flickable);
|
||||
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango)
|
||||
loadWindowRules();
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,9 @@ Item {
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
topPadding: 4
|
||||
|
||||
topPadding: Theme.spacingXL
|
||||
bottomPadding: Theme.spacingXL
|
||||
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
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 {
|
||||
width: parent.width
|
||||
iconName: "label"
|
||||
|
||||
@@ -7,6 +7,7 @@ Item {
|
||||
property color rippleColor: Theme.primary
|
||||
property real cornerRadius: 0
|
||||
property bool enableRipple: typeof SettingsData !== "undefined" ? (SettingsData.enableRippleEffects ?? true) : true
|
||||
property int animationDuration: Theme.expressiveDurations.expressiveDefaultSpatial
|
||||
|
||||
property real _rippleX: 0
|
||||
property real _rippleY: 0
|
||||
@@ -58,18 +59,18 @@ Item {
|
||||
property: "rippleRadius"
|
||||
from: 0
|
||||
to: root._rippleMaxRadius
|
||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
||||
duration: root.animationDuration
|
||||
easing.bezierCurve: Theme.expressiveCurves.standardDecel
|
||||
}
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: Math.round(Theme.expressiveDurations.expressiveDefaultSpatial * 0.6)
|
||||
duration: Math.round(root.animationDuration * 0.6)
|
||||
}
|
||||
DankAnim {
|
||||
target: rippleFx
|
||||
property: "rippleOpacity"
|
||||
to: 0
|
||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
||||
duration: root.animationDuration
|
||||
easing.bezierCurve: Theme.expressiveCurves.standard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,20 @@ CATEGORY_KEYWORDS = {
|
||||
"Time & Weather": ["clock", "forecast", "date"],
|
||||
"Keyboard Shortcuts": ["keys", "bindings", "hotkey"],
|
||||
"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"],
|
||||
"Network": ["connectivity", "online"],
|
||||
"System": ["os", "linux"],
|
||||
@@ -82,14 +95,6 @@ CATEGORY_KEYWORDS = {
|
||||
"Displays": ["monitor", "screen", "resolution"],
|
||||
"Desktop Widgets": ["conky", "desktop clock"],
|
||||
"Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"],
|
||||
"Window Rules": [
|
||||
"window",
|
||||
"rules",
|
||||
"matching",
|
||||
"floating",
|
||||
"fullscreen",
|
||||
"opacity",
|
||||
],
|
||||
"Locale": ["locale", "language", "country"],
|
||||
"Greeter": ["login", "greetd", "display manager"],
|
||||
"Multiplexers": ["tmux", "zellij", "terminal"],
|
||||
@@ -104,8 +109,13 @@ TAB_INDEX_MAP = {
|
||||
"TimeWeatherTab.qml": 1,
|
||||
"KeybindsTab.qml": 2,
|
||||
"DankBarTab.qml": 3,
|
||||
"CompositorTab.qml": 4,
|
||||
"CompositorLayoutTab.qml": 4,
|
||||
"WorkspacesTab.qml": 4,
|
||||
"WindowRulesTab.qml": 4,
|
||||
"DockTab.qml": 5,
|
||||
"DankBarAppearanceTab.qml": 6,
|
||||
"WorkspaceAppearanceCard.qml": 6,
|
||||
"NetworkTab.qml": 7,
|
||||
"PrinterTab.qml": 8,
|
||||
"LauncherTab.qml": 9,
|
||||
@@ -127,7 +137,6 @@ TAB_INDEX_MAP = {
|
||||
"GammaControlTab.qml": 25,
|
||||
"DisplayWidgetsTab.qml": 26,
|
||||
"DesktopWidgetsTab.qml": 27,
|
||||
"WindowRulesTab.qml": 28,
|
||||
"AudioTab.qml": 29,
|
||||
"LocaleTab.qml": 30,
|
||||
"GreeterTab.qml": 31,
|
||||
@@ -143,8 +152,9 @@ TAB_CATEGORY_MAP = {
|
||||
1: "Time & Weather",
|
||||
2: "Keyboard Shortcuts",
|
||||
3: "Dank Bar",
|
||||
4: "Workspaces",
|
||||
4: "Compositor",
|
||||
5: "Dock",
|
||||
6: "Dank Bar",
|
||||
7: "Network",
|
||||
8: "System",
|
||||
9: "Launcher",
|
||||
@@ -166,7 +176,6 @@ TAB_CATEGORY_MAP = {
|
||||
25: "Displays",
|
||||
26: "Displays",
|
||||
27: "Desktop Widgets",
|
||||
28: "Window Rules",
|
||||
29: "Audio",
|
||||
30: "Locale",
|
||||
31: "Greeter",
|
||||
@@ -302,9 +311,9 @@ def extract_property(block, prop_name):
|
||||
|
||||
def find_settings_components(content, filename):
|
||||
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
|
||||
|
||||
for component in SEARCHABLE_COMPONENTS:
|
||||
@@ -321,6 +330,11 @@ def find_settings_components(content, filename):
|
||||
if not setting_key:
|
||||
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")
|
||||
text_raw = extract_property(block, "text")
|
||||
label = None
|
||||
@@ -489,7 +503,7 @@ def extract_settings_index(root_dir):
|
||||
seen_keys = set()
|
||||
|
||||
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
|
||||
|
||||
with open(qml_file, "r", encoding="utf-8") as f:
|
||||
@@ -502,6 +516,24 @@ def extract_settings_index(root_dir):
|
||||
seen_keys.add(key)
|
||||
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
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user