aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/Settings.qml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-17 10:01:40 +0200
committerDanilo M. <danix@danix.xyz>2026-10-17 10:01:40 +0200
commit74fde3e0dae5a40c522ada14cf04945f3c806730 (patch)
tree3b6d669b6aa0e0d78ada8e93dbe12fc159b393e0 /desktop/Settings.qml
parent60f8ab4b63e6232818c259c111e502830a0c9af0 (diff)
downloadquickshell-74fde3e0dae5a40c522ada14cf04945f3c806730.tar.gz
quickshell-74fde3e0dae5a40c522ada14cf04945f3c806730.zip
desktop: settings search, and an About landing page
The landing page's tiles repeated the sidebar. It now says what the shell is and where things live, which is what a landing page is for. The sidebar gains a search. A page is a Component loaded only when opened, so its contents cannot be indexed without loading every page; instead a module lists keywords for the settings buried in its page, and the search matches those and the label. An entry matched by a keyword shows which one, Enter opens the first match, Escape clears and then steps out as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/Settings.qml')
-rw-r--r--desktop/Settings.qml149
1 files changed, 113 insertions, 36 deletions
diff --git a/desktop/Settings.qml b/desktop/Settings.qml
index af41fe4..52ca549 100644
--- a/desktop/Settings.qml
+++ b/desktop/Settings.qml
@@ -16,7 +16,7 @@ import QtQuick
// 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.
+// searchable list of them down the left and an About page as the landing.
Scope {
id: root
@@ -30,6 +30,24 @@ Scope {
if (root.modules[i].settings) out.push(root.modules[i]);
return out;
}
+ property string query: ""
+
+ // The pages the search keeps, as {mod, hit}: hit is the keyword that
+ // matched when the label did not, shown under the entry.
+ readonly property var found: {
+ const q = root.query.trim().toLowerCase();
+ const out = [];
+ for (const m of root.pages) {
+ if (q === "" || m.label.toLowerCase().includes(q) || m.name.includes(q)) {
+ out.push({ mod: m, hit: "" });
+ continue;
+ }
+ const k = Array.from(m.keywords).find(w => w.toLowerCase().includes(q));
+ if (k !== undefined) out.push({ mod: m, hit: k });
+ }
+ return out;
+ }
+
// Null is the landing page.
readonly property QtObject current: root.pages.find(m => m.name === root.page) ?? null
@@ -40,7 +58,7 @@ Scope {
if (root.current) root.page = "";
else root.close();
}
- function close() { root.open = false; root.page = ""; }
+ function close() { root.open = false; root.page = ""; root.query = ""; }
LazyLoader {
active: root.open
@@ -57,35 +75,82 @@ Scope {
focus: true
Keys.onEscapePressed: root.back()
+ // Search: filters the list below by label and by each
+ // module's keywords, the settings buried in its page. Enter
+ // opens the first match; Escape clears, then steps out.
+ Rectangle {
+ id: search
+ anchors { top: parent.top; left: parent.left; margins: 12 }
+ width: 200
+ height: 34
+ radius: 8
+ color: Theme.surface
+
+ TextInput {
+ id: searchIn
+ anchors { fill: parent; leftMargin: 10; rightMargin: 10 }
+ verticalAlignment: TextInput.AlignVCenter
+ clip: true
+ selectByMouse: true
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ text: root.query
+ onTextEdited: root.query = text
+ onAccepted: if (root.found.length) root.page = root.found[0].mod.name
+ Keys.onEscapePressed: event => {
+ if (root.query !== "") root.query = "";
+ else event.accepted = false;
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ visible: parent.text === ""
+ text: "Search settings"
+ color: Theme.subtext
+ font: parent.font
+ }
+ }
+ }
+
ListView {
id: side
- anchors { top: parent.top; left: parent.left; bottom: parent.bottom; margins: 12 }
+ anchors { top: search.bottom; left: parent.left; bottom: parent.bottom; margins: 12 }
width: 200
spacing: 4
- model: root.pages
+ model: root.found
delegate: Rectangle {
id: entry
- required property QtObject modelData
- readonly property bool selected: root.current === modelData
+ required property var modelData
+ readonly property bool selected: root.current === modelData.mod
width: side.width
- height: 36
+ height: modelData.hit ? 46 : 36
radius: 8
color: selected ? Qt.alpha(Theme.accent, 0.22)
: area.containsMouse ? Qt.alpha(Theme.text, 0.06) : "transparent"
- Row {
+ Text {
+ id: entryIcon
anchors { left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter }
- spacing: 10
+ text: entry.modelData.mod.icon
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize }
+ color: Theme.text
+ }
+ Column {
+ anchors { left: entryIcon.right; leftMargin: 10; right: parent.right; rightMargin: 8
+ verticalCenter: parent.verticalCenter }
Text {
- text: entry.modelData.icon
- font { family: Theme.iconFamily; pixelSize: Theme.fontSize }
+ text: entry.modelData.mod.label
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
color: Theme.text
}
+ // Which buried setting matched, when the label did not.
Text {
- text: entry.modelData.label
- font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
- color: Theme.text
+ visible: entry.modelData.hit !== ""
+ width: parent.width
+ elide: Text.ElideRight
+ text: entry.modelData.hit
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 }
+ color: Theme.subtext
}
}
@@ -94,11 +159,19 @@ Scope {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
- onClicked: root.page = entry.modelData.name
+ onClicked: root.page = entry.modelData.mod.name
}
}
}
+ Text {
+ anchors { top: search.bottom; left: parent.left; margins: 12; leftMargin: 22 }
+ visible: root.found.length === 0
+ text: "Nothing matches"
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ color: Theme.subtext
+ }
+
Rectangle {
id: rule
anchors { top: parent.top; bottom: parent.bottom; left: side.right; leftMargin: 12 }
@@ -106,37 +179,41 @@ Scope {
color: Qt.alpha(Theme.text, 0.12)
}
- // The landing page: every settings page as a tile, as the
- // drawer's grid shows its modules. Where back returns to.
- Item {
- anchors { top: parent.top; right: parent.right; bottom: parent.bottom; left: rule.right; margins: 16 }
+ // The landing page, where back returns to: what this shell is.
+ Column {
+ anchors { top: parent.top; right: parent.right; left: rule.right; margins: 16 }
visible: root.current === null
+ spacing: 12
Text {
- id: landingTitle
height: 44
verticalAlignment: Text.AlignVCenter
- text: "Settings"
+ text: "About"
font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 2; bold: true }
color: Theme.text
}
- Flow {
- id: landing
- anchors { top: landingTitle.bottom; topMargin: 12; left: parent.left; right: parent.right }
- spacing: 10
-
- Repeater {
- model: root.pages
+ Text {
+ width: Math.min(parent.width, 560)
+ wrapMode: Text.Wrap
+ textFormat: Text.PlainText
+ lineHeight: 1.25
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ color: Theme.text
+ text: "A desktop shell for Hyprland, built on Quickshell.\n\n"
+ + "SUPER alone opens the drawer, the quick area: notifications, sound, "
+ + "network, bluetooth, mail, VMs, performance, the clipboard and the "
+ + "rest of what changes during the day. The island at the top of the "
+ + "main screen carries the clock, volume, tray, keyboard layout and "
+ + "calendar, and SUPER+Home opens the assistant.\n\n"
+ + "This window holds what is set once and left alone. Pick a page on "
+ + "the left, or search for a setting by name."
+ }
- Tile {
- required property QtObject modelData
- width: (landing.width - 2 * landing.spacing) / 3
- icon: modelData.icon
- label: modelData.label
- onClicked: root.page = modelData.name
- }
- }
+ Text {
+ text: "GPLv2. Colours follow the wallpaper through unified-desktop-theme."
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ color: Theme.subtext
}
}