aboutsummaryrefslogtreecommitdiffstats
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/Module.qml4
-rw-r--r--desktop/README.md12
-rw-r--r--desktop/Settings.qml149
-rw-r--r--desktop/modules/autostart/AutostartModule.qml2
-rw-r--r--desktop/modules/git/GitModule.qml5
5 files changed, 134 insertions, 38 deletions
diff --git a/desktop/Module.qml b/desktop/Module.qml
index 5217d3f..448fa7b 100644
--- a/desktop/Module.qml
+++ b/desktop/Module.qml
@@ -42,6 +42,10 @@ QtObject {
// things set once and left alone. Such a module has no tile by default.
property bool settings: false
+ // Words the settings window's search matches besides the label: the
+ // settings buried inside the page, which is not loaded until opened.
+ property list<string> keywords: []
+
// Whether the drawer's grid shows a tile. False for a module reached
// some other way: the user page opens from the drawer's header row.
property bool tiled: !settings
diff --git a/desktop/README.md b/desktop/README.md
index 187def6..ed30620 100644
--- a/desktop/README.md
+++ b/desktop/README.md
@@ -36,8 +36,14 @@ gear tile opens it, and so does IPC:
qs -p ./desktop ipc call settings toggle
`open` takes a page name; an unknown or empty one opens the landing page,
-a tile per settings page. A page's back arrow and Escape return there;
-Escape on the landing page closes the window.
+a short About. A page's back arrow and Escape return there; Escape on the
+landing page closes the window.
+
+The search above the list matches each page's label and its module's
+`keywords`, the settings buried inside the page; an entry matched by a
+keyword shows which one. Enter opens the first match, Escape clears. Pages
+load only when opened, so their live contents (a git alias, an autostart
+command) are not searched, only the keywords.
## The modules
@@ -72,6 +78,8 @@ drawer needs to show it:
- `page`: a `Component` for the full-height page behind the tile, or null.
- `settings`: the page lives in the settings window, not the drawer, and the
module has no tile.
+- `keywords`: for a settings page, the words its search should find besides
+ the label.
- `activate()`: what a tile with no page does when clicked.
- `alwaysActive`: declares whether the module's service is meant to run while
the drawer is closed. The drawer does not read it, the module implements its
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
}
}
diff --git a/desktop/modules/autostart/AutostartModule.qml b/desktop/modules/autostart/AutostartModule.qml
index db11b28..ef4c005 100644
--- a/desktop/modules/autostart/AutostartModule.qml
+++ b/desktop/modules/autostart/AutostartModule.qml
@@ -20,6 +20,8 @@ Module {
// nf-fa-rocket, present in Inconsolata Nerd Font.
icon: "\uf135"
settings: true
+ keywords: ["login", "startup", "launch", "exec", "daemon", "session",
+ "workspace", "silent", "scratchpad", "autostart.lua", "hyprland"]
page: Component {
Page {
diff --git a/desktop/modules/git/GitModule.qml b/desktop/modules/git/GitModule.qml
index 8a2b6dd..aaa72a9 100644
--- a/desktop/modules/git/GitModule.qml
+++ b/desktop/modules/git/GitModule.qml
@@ -21,6 +21,11 @@ Module {
// nf-dev-git, present in Inconsolata Nerd Font.
icon: ""
settings: true
+ keywords: ["gitconfig", "user", "name", "email", "signingkey", "signing", "gpg",
+ "alias", "core", "editor", "pager", "commit", "gpgsign", "init",
+ "defaultBranch", "branch", "pull", "push", "fetch", "rebase", "merge",
+ "diff", "log", "color", "credential", "lfs", "rerere", "tag", "status",
+ "safe", "directory"]
page: Component {
Page {