summaryrefslogtreecommitdiffstats
path: root/desktop/modules/hyprland/OptionsPage.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/modules/hyprland/OptionsPage.qml')
-rw-r--r--desktop/modules/hyprland/OptionsPage.qml267
1 files changed, 267 insertions, 0 deletions
diff --git a/desktop/modules/hyprland/OptionsPage.qml b/desktop/modules/hyprland/OptionsPage.qml
new file mode 100644
index 0000000..b9d806e
--- /dev/null
+++ b/desktop/modules/hyprland/OptionsPage.qml
@@ -0,0 +1,267 @@
+// 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 QtQuick
+import "../.."
+import "Hypr.js" as H
+
+// One row per option: its name, its value (in the accent when it differs
+// from Hyprland's default, colours as swatches), and what it does. Filters
+// for everything, what the config sets, and what differs from the default.
+// Clicking a row opens it: the whole description, the default and range,
+// and an editor shaped by the type: true/false and an enum's names as
+// choices, anything else as a Lua expression started from the value.
+Column {
+ id: page
+
+ required property QtObject mod
+ property string filter: "all"
+ property string query: ""
+ property string editing: ""
+ property string draft: ""
+ property string error: ""
+ property bool busy: false
+
+ spacing: 10
+
+ readonly property var all: page.mod.descs.map(d => {
+ const o = page.mod.values[d.name];
+ return { d: d, o: o, set: !!(o && o.set), changed: H.changed(d, o), over: page.mod.overridden.includes(d.name) };
+ })
+ readonly property int setCount: page.all.filter(r => r.set).length
+ readonly property int changedCount: page.all.filter(r => r.changed).length
+ readonly property var shown: {
+ const q = page.query.trim().toLowerCase();
+ return page.all.filter(r => (page.filter === "all" || (page.filter === "set" ? r.set : r.changed))
+ && (q === "" || r.d.name.toLowerCase().includes(q) || r.d.description.toLowerCase().includes(q)));
+ }
+
+ function toggle(r) {
+ page.error = "";
+ if (page.editing === r.d.name) { page.editing = ""; return; }
+ page.editing = r.d.name;
+ page.draft = r.o ? H.lua(r.o) : "";
+ }
+
+ function apply(name, expr) {
+ if (page.busy) return;
+ page.busy = true;
+ page.error = "";
+ page.mod.set(name, expr, err => {
+ page.busy = false;
+ page.error = err;
+ if (err === "") page.draft = expr;
+ });
+ }
+
+ component Small: Text {
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ elide: Text.ElideRight
+ }
+
+ component OptionRow: Column {
+ id: or
+ required property var row
+ readonly property bool open: page.editing === or.row.d.name
+ readonly property string kind: H.kind(or.row.o)
+ readonly property var choices: or.kind === "bool"
+ ? [{ name: "true", v: true }, { name: "false", v: false }]
+ : or.kind === "int" ? H.choices(or.row.d) : []
+ objectName: "opt:" + or.row.d.name
+ width: page.width
+ spacing: 6
+
+ Item {
+ width: parent.width
+ height: 30
+
+ Rectangle {
+ anchors.fill: parent
+ radius: 6
+ color: rowArea.containsMouse || or.open ? Qt.alpha(Theme.accent, 0.12) : "transparent"
+ }
+
+ Text {
+ x: 8
+ width: parent.width * 0.36 - 16
+ anchors.verticalCenter: parent.verticalCenter
+ elide: Text.ElideRight
+ textFormat: Text.StyledText
+ // The section faded, the option itself bright.
+ readonly property int cut: or.row.d.name.lastIndexOf(":") + 1
+ text: "<font color='" + Theme.subtext + "'>" + or.row.d.name.slice(0, cut) + "</font>" + or.row.d.name.slice(cut)
+ color: Theme.text
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 3 }
+ }
+
+ Row {
+ x: parent.width * 0.36
+ width: parent.width * 0.26 - 12
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: 4
+ clip: true
+ Repeater {
+ model: H.colors(or.row.o)
+ Rectangle {
+ required property string modelData
+ anchors.verticalCenter: parent.verticalCenter
+ width: 10; height: 10; radius: 2
+ color: modelData
+ }
+ }
+ Text {
+ text: H.show(or.row.d, or.row.o)
+ color: or.row.changed ? Theme.accent : Theme.subtext
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 3; bold: or.row.changed }
+ }
+ }
+
+ Small {
+ x: parent.width * 0.62
+ width: parent.width * 0.38 - 8
+ anchors.verticalCenter: parent.verticalCenter
+ text: (or.row.over ? "● " : "") + or.row.d.description
+ }
+
+ MouseArea {
+ id: rowArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: page.toggle(or.row)
+ }
+ }
+
+ Column {
+ visible: or.open
+ x: 8
+ width: parent.width - 16
+ spacing: 6
+ bottomPadding: 8
+
+ Text {
+ width: parent.width
+ wrapMode: Text.Wrap
+ text: or.row.d.description
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ Small {
+ width: parent.width
+ wrapMode: Text.Wrap
+ text: "Default " + H.show(or.row.d, { [or.kind || "str"]: or.row.d.default })
+ + (or.row.d.min !== undefined && or.row.d.min !== null ? ", from " + or.row.d.min + " to " + or.row.d.max : "")
+ + (or.row.over ? ". Set by keystone.lua."
+ : or.row.set ? ". Set in your config: a change here overrides it from keystone.lua." : ".")
+ }
+
+ // true/false, or an enum's names.
+ Flow {
+ visible: or.choices.length > 0
+ width: parent.width
+ spacing: 6
+ Repeater {
+ model: or.choices
+ Chip {
+ required property var modelData
+ text: modelData.name
+ selected: or.row.o && or.row.o[or.kind] === modelData.v
+ onClicked: if (!selected) page.apply(or.row.d.name, String(modelData.v))
+ }
+ }
+ }
+
+ Item {
+ visible: or.choices.length === 0 && or.kind !== ""
+ width: parent.width
+ height: 32
+ Field {
+ anchors { left: parent.left; right: applyBtn.left; rightMargin: 8 }
+ mono: true
+ text: page.draft
+ onEdited: t => page.draft = t
+ onAccepted: page.apply(or.row.d.name, page.draft)
+ }
+ Button {
+ id: applyBtn
+ anchors { right: parent.right; verticalCenter: parent.verticalCenter }
+ text: page.busy ? "Checking…" : "Apply"
+ enabled: !page.busy && page.draft.trim() !== ""
+ onClicked: page.apply(or.row.d.name, page.draft)
+ }
+ }
+
+ Small {
+ width: parent.width
+ visible: page.error !== ""
+ wrapMode: Text.Wrap
+ color: Theme.red
+ text: page.error
+ }
+
+ Button {
+ visible: or.row.over
+ text: "Reset, drop the keystone.lua line"
+ onClicked: page.mod.reset(or.row.d.name)
+ }
+ }
+ }
+
+ Small {
+ width: parent.width
+ wrapMode: Text.Wrap
+ text: page.all.length + " options, live from Hyprland. Your config sets " + page.setCount + ", and "
+ + page.changedCount + " differ from the default. Click one to see more, or change it."
+ }
+
+ Item {
+ width: parent.width
+ height: 32
+ Field {
+ id: search
+ width: parent.width * 0.3
+ text: page.query
+ placeholder: "Search options"
+ onEdited: t => page.query = t
+ }
+ Row {
+ anchors { left: search.right; leftMargin: 12; verticalCenter: parent.verticalCenter }
+ spacing: 6
+ Chip { text: "Everything · " + page.all.length; selected: page.filter === "all"; onClicked: page.filter = "all" }
+ Chip { text: "Set in your config · " + page.setCount; selected: page.filter === "set"; onClicked: page.filter = "set" }
+ Chip { text: "Changed from default · " + page.changedCount; selected: page.filter === "changed"; onClicked: page.filter = "changed" }
+ }
+ }
+
+ Item {
+ width: parent.width
+ height: 20
+ Small { x: 8; text: "Option" }
+ Small { x: parent.width * 0.36; text: "Value" }
+ Small { x: parent.width * 0.62; text: "What it does" }
+ }
+
+ Repeater {
+ model: page.shown
+ OptionRow {
+ required property var modelData
+ row: modelData
+ }
+ }
+
+ Small {
+ width: parent.width
+ wrapMode: Text.Wrap
+ text: "Values in the accent differ from Hyprland's default; faded ones are the default. Colours show as swatches. "
+ + "● marks an option set from this page, in sections/keystone.lua."
+ }
+}