aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/Settings.qml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-17 09:54:18 +0200
committerDanilo M. <danix@danix.xyz>2026-10-17 09:54:18 +0200
commit66049c965139d821747ea76960b1e91316f8110a (patch)
tree45ac7a387ea02c1cc8d26e104f6e69a96d3880ed /desktop/Settings.qml
parent4513a506fc575f880db80f05bb3a9cdf7dc100e0 (diff)
downloadquickshell-66049c965139d821747ea76960b1e91316f8110a.tar.gz
quickshell-66049c965139d821747ea76960b1e91316f8110a.zip
desktop: a settings window, and autostart as its first page
The drawer's tiles were turning into a full settings app. The drawer stays the quick area, live state and things toggled often; pages set once and left alone go to a settings window, a normal Hyprland toplevel listing them down the left. A module joins with settings: true, which also drops its tile. A gear tile and ipc call settings open|toggle reach it. Autostart edits hypr-theme's autostart.lua directly, line by line: an hl.exec_cmd line is an entry, behind -- it is disabled, everything else passes through. Edits refuse when their line moved since the parse, and writes are atomic because Hyprland reloads on every change. A wrong entry only shows at the next login, so adding is guided rather than a free text field: pick an installed app or type a command whose program must resolve on PATH before Add enables, then a workspace and silent, composed into a [workspace X silent] rule. Try runs the line through hyprctl dispatch so the rule applies as at login. Existing rows get no Try: they ran at login, and a second start stacks an instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/Settings.qml')
-rw-r--r--desktop/Settings.qml126
1 files changed, 126 insertions, 0 deletions
diff --git a/desktop/Settings.qml b/desktop/Settings.qml
new file mode 100644
index 0000000..a397105
--- /dev/null
+++ b/desktop/Settings.qml
@@ -0,0 +1,126 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import Quickshell
+import QtQuick
+
+// The settings window: a normal toplevel, not a layer surface, so Hyprland
+// tiles or floats it like any app. The drawer is the quick area; things set
+// once and left alone live here. It hosts the pages of the modules that
+// declare `settings: true`, from the same module list as the drawer, with a
+// list of them down the left.
+Scope {
+ id: root
+
+ property list<QtObject> modules
+ property bool open: false
+ property string page: ""
+
+ readonly property var pages: {
+ const out = [];
+ for (let i = 0; i < root.modules.length; i++)
+ if (root.modules[i].settings) out.push(root.modules[i]);
+ return out;
+ }
+ readonly property QtObject current: root.pages.find(m => m.name === root.page) ?? root.pages[0] ?? null
+
+ // An unknown or empty name opens the first page.
+ function show(name) { root.page = name; root.open = true; }
+ function close() { root.open = false; }
+
+ LazyLoader {
+ active: root.open
+
+ FloatingWindow {
+ title: "Settings"
+ implicitWidth: 900
+ implicitHeight: 640
+ color: Theme.base
+ onClosed: root.close()
+
+ Item {
+ anchors.fill: parent
+ focus: true
+ Keys.onEscapePressed: root.close()
+
+ ListView {
+ id: side
+ anchors { top: parent.top; left: parent.left; bottom: parent.bottom; margins: 12 }
+ width: 200
+ spacing: 4
+ model: root.pages
+
+ delegate: Rectangle {
+ id: entry
+ required property QtObject modelData
+ readonly property bool selected: root.current === modelData
+ width: side.width
+ height: 36
+ radius: 8
+ color: selected ? Qt.alpha(Theme.accent, 0.22)
+ : area.containsMouse ? Qt.alpha(Theme.text, 0.06) : "transparent"
+
+ Row {
+ anchors { left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter }
+ spacing: 10
+ Text {
+ text: entry.modelData.icon
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize }
+ color: Theme.text
+ }
+ Text {
+ text: entry.modelData.label
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ color: Theme.text
+ }
+ }
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: root.page = entry.modelData.name
+ }
+ }
+ }
+
+ Rectangle {
+ id: rule
+ anchors { top: parent.top; bottom: parent.bottom; left: side.right; leftMargin: 12 }
+ width: 1
+ color: Qt.alpha(Theme.text, 0.12)
+ }
+
+ Loader {
+ id: pageLoader
+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom; left: rule.right; margins: 16 }
+ sourceComponent: root.current?.page ?? null
+
+ // The Page's back arrow and dismiss close the window;
+ // go() moves to another settings page by name.
+ onLoaded: {
+ if (item && item.back) item.back.connect(root.close);
+ if (item && item.dismiss) item.dismiss.connect(root.close);
+ if (item && item.go) item.go.connect(name => root.page = name);
+ }
+
+ Binding {
+ target: pageLoader.item
+ property: "icon"
+ value: root.current ? root.current.icon : ""
+ when: pageLoader.item !== null
+ }
+ }
+ }
+ }
+ }
+}