aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/Prefs.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/Prefs.qml')
-rw-r--r--desktop/Prefs.qml69
1 files changed, 69 insertions, 0 deletions
diff --git a/desktop/Prefs.qml b/desktop/Prefs.qml
new file mode 100644
index 0000000..dd2fdfc
--- /dev/null
+++ b/desktop/Prefs.qml
@@ -0,0 +1,69 @@
+// 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.
+
+pragma Singleton
+
+import Quickshell
+import Quickshell.Io
+import QtQuick
+import "island/Controls.js" as Controls
+
+// Preferences that must survive a reboot, in one JSON file under the state
+// home. Only the shell writes it, which is why it is not in ~/.config.
+//
+// Nothing is written until the user changes something. A file that fails to
+// parse reads as the defaults and stays on disk as it is, so a hand edit
+// with a typo is not replaced by a panel that was merely opened.
+Singleton {
+ id: root
+
+ readonly property string path:
+ (Quickshell.env("XDG_STATE_HOME") || Quickshell.env("HOME") + "/.local/state")
+ + "/quickshell/desktop.json"
+
+ // Visible control tiles, in display order.
+ property var controls: Controls.defaults()
+
+ // The whole parsed object, so a write keeps keys this version does not
+ // know about.
+ property var data: ({})
+
+ function setControls(list) {
+ root.controls = Controls.normalize(list);
+ const out = Object.assign({}, root.data, { controls: root.controls });
+ root.data = out;
+ file.setText(JSON.stringify(out, null, 2) + "\n");
+ }
+
+ function reparse() {
+ let parsed = {};
+ try {
+ const v = JSON.parse(file.text());
+ if (v && typeof v === "object" && !Array.isArray(v)) parsed = v;
+ } catch (e) {}
+ root.data = parsed;
+ const next = "controls" in parsed ? Controls.normalize(parsed.controls) : Controls.defaults();
+ // The watch also fires on our own write; compare so it is a no-op.
+ if (JSON.stringify(next) !== JSON.stringify(root.controls)) root.controls = next;
+ }
+
+ FileView {
+ id: file
+ path: root.path
+ atomicWrites: true
+ watchChanges: true
+ printErrors: false
+ onFileChanged: reload()
+ onLoaded: root.reparse()
+ // Missing file: the defaults, and nothing written.
+ onLoadFailed: { root.data = {}; root.controls = Controls.defaults(); }
+ }
+}