Krolyxon
Commit ed7f69d

Branches

Refs →

auroraed7f69d

aurora: settings app in cards with an icon nav

Author
krolyxon
Date
2026-11-07 19:17 +0530
Parent
13428ef
Commit
ed7f69d3c09ce96d77b7c16a644b67a505e12bb7

+1350 −1017

17 files changed

  1. M .config/quickshell/aurora/AudioPage.qml +101 −73
  2. M .config/quickshell/aurora/BatteryInfo.qml +14 −8
  3. M .config/quickshell/aurora/BtPage.qml +93 −77
  4. M .config/quickshell/aurora/GpuSwitch.qml +9 −5
  5. A .config/quickshell/aurora/Group.qml +41 −0
  6. M .config/quickshell/aurora/NetPage.qml +152 −135
  7. M .config/quickshell/aurora/SetAbout.qml +96 −68
  8. M .config/quickshell/aurora/SetAppearance.qml +134 −105
  9. M .config/quickshell/aurora/SetDisplay.qml +68 −49
  10. M .config/quickshell/aurora/SetIndex.qml +17 −17
  11. M .config/quickshell/aurora/SetInput.qml +51 −36
  12. M .config/quickshell/aurora/SetKeys.qml +53 −31
  13. M .config/quickshell/aurora/SetPower.qml +97 −81
  14. M .config/quickshell/aurora/SetShell.qml +82 −57
  15. M .config/quickshell/aurora/SetWindows.qml +72 −60
  16. M .config/quickshell/aurora/SettingsApp.qml +269 −215
  17. M .config/quickshell/aurora/Theme.qml +1 −0
M .config/quickshell/aurora/AudioPage.qml View
@@ -5,6 +5,7 @@ import Quickshell.Services.Pipewire
55 // Control panel > sound (also the settings app's sound section): output / input levels, devices, per-app volume.
66 Flickable {
77 id: flick
8+ property bool cards: false // the settings app: groups on cards
89 clip: true
910 contentWidth: width
1011 contentHeight: col.implicitHeight
@@ -42,86 +43,113 @@ Flickable {
4243 ColumnLayout {
4344 id: col
4445 width: flick.width
45− spacing: 6
46+ spacing: flick.cards ? 22 : 6
4647
47− SectionLabel { text: "levels"; keywords: "volume mute microphone mic" }
48− SliderRow {
49− Layout.fillWidth: true
50− Layout.topMargin: 4
51− icon: Theme.volIcon(flick.out ? flick.out.volume : 0, flick.out ? flick.out.muted : true)
52− value: flick.out ? flick.out.volume : 0
53− muted: flick.out ? flick.out.muted : false
54− onMoved: v => { if (flick.out) flick.out.volume = v }
55− onIconClicked: if (flick.out) flick.out.muted = !flick.out.muted
56− }
57− SliderRow {
58− Layout.fillWidth: true
59− Layout.topMargin: 6
60− icon: flick.inp && !flick.inp.muted ? Theme.iMic : Theme.iMicOff
61− value: flick.inp ? flick.inp.volume : 0
62− muted: flick.inp ? flick.inp.muted : false
63− onMoved: v => { if (flick.inp) flick.inp.volume = v }
64− onIconClicked: if (flick.inp) flick.inp.muted = !flick.inp.muted
65− }
66−
67− SectionLabel { text: "output"; keywords: "speakers headphones hdmi default device sink"; count: String(flick.sinks.length); Layout.topMargin: 14 }
68− Repeater {
69− model: flick.sinks
70− delegate: ListRow {
71− required property var modelData
72− icon: flick.sinkIcon(modelData)
73− title: flick.label(modelData)
74− subtitle: current ? "default" : ""
75− current: Pipewire.defaultAudioSink === modelData
76− trailing: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
77− onClicked: Pipewire.preferredDefaultAudioSink = modelData
48+ Group {
49+ title: "levels"
50+ keywords: "volume mute microphone mic"
51+ flat: !flick.cards
52+ spacing: 6
53+ gap: 6
54+ SliderRow {
55+ Layout.fillWidth: true
56+ Layout.topMargin: 4
57+ icon: Theme.volIcon(flick.out ? flick.out.volume : 0, flick.out ? flick.out.muted : true)
58+ value: flick.out ? flick.out.volume : 0
59+ muted: flick.out ? flick.out.muted : false
60+ onMoved: v => { if (flick.out) flick.out.volume = v }
61+ onIconClicked: if (flick.out) flick.out.muted = !flick.out.muted
7862 }
79− }
80−
81− SectionLabel { text: "input"; keywords: "microphone mic default device source"; count: String(flick.sources.length); Layout.topMargin: 14 }
82− Repeater {
83− model: flick.sources
84− delegate: ListRow {
85− required property var modelData
86− icon: Theme.iMic
87− title: flick.label(modelData)
88− subtitle: current ? "default" : ""
89− current: Pipewire.defaultAudioSource === modelData
90− trailing: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
91− onClicked: Pipewire.preferredDefaultAudioSource = modelData
63+ SliderRow {
64+ Layout.fillWidth: true
65+ Layout.topMargin: 6
66+ icon: flick.inp && !flick.inp.muted ? Theme.iMic : Theme.iMicOff
67+ value: flick.inp ? flick.inp.volume : 0
68+ muted: flick.inp ? flick.inp.muted : false
69+ onMoved: v => { if (flick.inp) flick.inp.volume = v }
70+ onIconClicked: if (flick.inp) flick.inp.muted = !flick.inp.muted
9271 }
9372 }
94−
95− SectionLabel { text: "apps"; keywords: "mixer per-app app volume streams"; count: String(flick.streams.length); Layout.topMargin: 14 }
96− Lb {
97− visible: flick.streams.length === 0
98− Layout.leftMargin: 6
99− text: "no app is playing"
100− color: Theme.mute
73+ Group {
74+ title: "output"
75+ keywords: "speakers headphones hdmi default device sink"
76+ count: String(flick.sinks.length)
77+ flat: !flick.cards
78+ spacing: 6
79+ gap: 6
80+ Layout.topMargin: flick.cards ? 0 : 14
81+ Repeater {
82+ model: flick.sinks
83+ delegate: ListRow {
84+ required property var modelData
85+ icon: flick.sinkIcon(modelData)
86+ title: flick.label(modelData)
87+ subtitle: current ? "default" : ""
88+ current: Pipewire.defaultAudioSink === modelData
89+ trailing: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
90+ onClicked: Pipewire.preferredDefaultAudioSink = modelData
91+ }
92+ }
10193 }
102− Repeater {
103− model: flick.streams
104− delegate: ColumnLayout {
105− required property var modelData
106− Layout.fillWidth: true
107− Layout.topMargin: 4
108− spacing: 6
109− Lb {
110− Layout.leftMargin: 6
111− Layout.fillWidth: true
112− elide: Text.ElideRight
113− px: 10
114− text: (modelData.properties && modelData.properties["application.name"])
115− ? modelData.properties["application.name"] : flick.label(modelData)
94+ Group {
95+ title: "input"
96+ keywords: "microphone mic default device source"
97+ count: String(flick.sources.length)
98+ flat: !flick.cards
99+ spacing: 6
100+ gap: 6
101+ Layout.topMargin: flick.cards ? 0 : 14
102+ Repeater {
103+ model: flick.sources
104+ delegate: ListRow {
105+ required property var modelData
106+ icon: Theme.iMic
107+ title: flick.label(modelData)
108+ subtitle: current ? "default" : ""
109+ current: Pipewire.defaultAudioSource === modelData
110+ trailing: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
111+ onClicked: Pipewire.preferredDefaultAudioSource = modelData
116112 }
117− SliderRow {
113+ }
114+ }
115+ Group {
116+ title: "apps"
117+ keywords: "mixer per-app app volume streams"
118+ count: String(flick.streams.length)
119+ flat: !flick.cards
120+ spacing: 6
121+ gap: 6
122+ Layout.topMargin: flick.cards ? 0 : 14
123+ Lb {
124+ visible: flick.streams.length === 0
125+ Layout.leftMargin: 6
126+ text: "no app is playing"
127+ color: Theme.mute
128+ }
129+ Repeater {
130+ model: flick.streams
131+ delegate: ColumnLayout {
132+ required property var modelData
118133 Layout.fillWidth: true
119− implicitHeight: 44
120− icon: Theme.volIcon(modelData.audio.volume, modelData.audio.muted)
121− value: modelData.audio.volume
122− muted: modelData.audio.muted
123− onMoved: v => modelData.audio.volume = v
124− onIconClicked: modelData.audio.muted = !modelData.audio.muted
134+ Layout.topMargin: 4
135+ spacing: 6
136+ Lb {
137+ Layout.leftMargin: 6
138+ Layout.fillWidth: true
139+ elide: Text.ElideRight
140+ px: 10
141+ text: (modelData.properties && modelData.properties["application.name"])
142+ ? modelData.properties["application.name"] : flick.label(modelData)
143+ }
144+ SliderRow {
145+ Layout.fillWidth: true
146+ implicitHeight: 44
147+ icon: Theme.volIcon(modelData.audio.volume, modelData.audio.muted)
148+ value: modelData.audio.volume
149+ muted: modelData.audio.muted
150+ onMoved: v => modelData.audio.volume = v
151+ onIconClicked: modelData.audio.muted = !modelData.audio.muted
152+ }
125153 }
126154 }
127155 }
M .config/quickshell/aurora/BatteryInfo.qml View
@@ -3,8 +3,10 @@ import QtQuick.Layouts
33 import Quickshell.Services.UPower
44
55 // Battery block (SysPage, settings app > Power): charge in Doto, state, a segment bar (accent when low), details.
6−ColumnLayout {
6+// cards: on a card (the settings app); the panel shows it flat.
7+Group {
78 id: root
9+ property bool cards: false
810 readonly property var bat: UPower.displayDevice
911 readonly property bool hasBat: bat !== null && bat.isPresent
1012 readonly property real batPct: hasBat ? (bat.percentage > 1 ? bat.percentage / 100 : bat.percentage) : 0
@@ -22,12 +24,15 @@ ColumnLayout {
2224 }
2325 }
2426 visible: hasBat
25− Layout.fillWidth: true
26− spacing: 12
27+ title: "battery"
28+ keywords: "charge level percentage health time left"
29+ flat: !cards
30+ spacing: cards ? 10 : 12
31+ gap: 12
2732
28− SectionLabel { text: "battery"; keywords: "charge level percentage health time left" }
2933 RowLayout {
30− Layout.leftMargin: 6
34+ Layout.leftMargin: root.cards ? 18 : 6
35+ Layout.topMargin: root.cards ? 12 : 0
3136 spacing: 14
3237 Num {
3338 text: Math.round(root.batPct * 100)
@@ -43,15 +48,16 @@ ColumnLayout {
4348 Layout.fillWidth: true
4449 Layout.preferredWidth: 100
4550 Layout.preferredHeight: 6
46− Layout.leftMargin: 6
47− Layout.rightMargin: 6
51+ Layout.leftMargin: root.cards ? 18 : 6
52+ Layout.rightMargin: root.cards ? 18 : 6
4853 n: 24
4954 f: root.batPct
5055 on: root.low ? Theme.err : Theme.fill
5156 }
5257 Lb {
5358 Layout.fillWidth: true
54− Layout.leftMargin: 6
59+ Layout.leftMargin: root.cards ? 18 : 6
60+ Layout.bottomMargin: root.cards ? 12 : 0
5561 wrapMode: Text.Wrap
5662 lineHeight: 1.5
5763 px: 10
M .config/quickshell/aurora/BtPage.qml View
@@ -7,7 +7,8 @@ import Quickshell.Bluetooth
77 ColumnLayout {
88 id: page
99 property bool active: false
10− spacing: 10
10+ property bool cards: false // the settings app: groups on cards
11+ spacing: cards ? 22 : 10
1112
1213 readonly property var adapter: Bluetooth.defaultAdapter
1314
@@ -53,96 +54,111 @@ ColumnLayout {
5354 }
5455 readonly property var devices: list()
5556
56− RowLayout {
57− Layout.fillWidth: true
58− Layout.leftMargin: 6
59− spacing: 8
60− Lb {
57+ // the radio: a card of its own in the settings app
58+ Group {
59+ flat: !page.cards
60+ RowLayout {
6161 Layout.fillWidth: true
62− elide: Text.ElideRight
63− text: !page.adapter ? "no bluetooth adapter"
64− : (!page.adapter.enabled ? "bluetooth · off"
65− : (page.adapter.discovering ? "bluetooth · searching" : "bluetooth · " + (page.adapter.name || "on")))
66− color: page.adapter && page.adapter.enabled ? Theme.fg : Theme.grey
67− }
68− IconBtn {
69− visible: page.adapter !== null
70− icon: Theme.iSearch
71− active: page.adapter ? page.adapter.discovering : false
72− onClicked: if (page.adapter && page.adapter.enabled) page.adapter.discovering = !page.adapter.discovering
73− }
74− IconBtn {
75− visible: page.adapter !== null
76− icon: page.adapter && page.adapter.enabled ? Theme.iBt : Theme.iBtOff
77− active: page.adapter ? page.adapter.enabled : false
78− onClicked: {
79− if (!page.adapter) return
80− page.adapter.enabled = !page.adapter.enabled
81− if (page.adapter.enabled && page.active) page.adapter.discovering = true
62+ Layout.leftMargin: page.cards ? 18 : 6
63+ Layout.preferredHeight: page.cards ? 56 : -1
64+ spacing: 8
65+ Lb {
66+ Layout.fillWidth: true
67+ elide: Text.ElideRight
68+ text: !page.adapter ? "no bluetooth adapter"
69+ : (!page.adapter.enabled ? "bluetooth · off"
70+ : (page.adapter.discovering ? "bluetooth · searching" : "bluetooth · " + (page.adapter.name || "on")))
71+ color: page.adapter && page.adapter.enabled ? Theme.fg : Theme.grey
72+ }
73+ IconBtn {
74+ visible: page.adapter !== null
75+ icon: Theme.iSearch
76+ active: page.adapter ? page.adapter.discovering : false
77+ onClicked: if (page.adapter && page.adapter.enabled) page.adapter.discovering = !page.adapter.discovering
78+ }
79+ IconBtn {
80+ visible: page.adapter !== null
81+ icon: page.adapter && page.adapter.enabled ? Theme.iBt : Theme.iBtOff
82+ active: page.adapter ? page.adapter.enabled : false
83+ onClicked: {
84+ if (!page.adapter) return
85+ page.adapter.enabled = !page.adapter.enabled
86+ if (page.adapter.enabled && page.active) page.adapter.discovering = true
87+ }
8288 }
8389 }
8490 }
8591
86− SectionLabel { text: "devices"; keywords: "pair pairing connect forget headphones headset earbuds speaker controller"; count: page.adapter && page.adapter.enabled ? String(page.devices.length) : "" }
87−
88− Flickable {
89− id: flick
90− Layout.fillWidth: true
92+ Group {
93+ title: "devices"
94+ keywords: "pair pairing connect forget headphones headset earbuds speaker controller"
95+ count: page.adapter && page.adapter.enabled ? String(page.devices.length) : ""
96+ flat: !page.cards
97+ spacing: 10
9198 Layout.fillHeight: true
92− Layout.preferredHeight: col.implicitHeight
93− clip: true
94− contentWidth: width
95− contentHeight: col.implicitHeight
96− boundsBehavior: Flickable.StopAtBounds
99+ // on a card: only as tall as the list, it scrolls inside once the page is full
100+ Layout.maximumHeight: page.cards ? implicitHeight : Infinity
97101
98− ColumnLayout {
99− id: col
100− width: flick.width
101− spacing: 2
102+ Flickable {
103+ id: flick
104+ Layout.fillWidth: true
105+ Layout.fillHeight: true
106+ Layout.preferredHeight: col.implicitHeight
107+ clip: true
108+ contentWidth: width
109+ contentHeight: col.implicitHeight
110+ boundsBehavior: Flickable.StopAtBounds
102111
103− Repeater {
104− model: page.adapter && page.adapter.enabled ? page.devices : []
105− delegate: ListRow {
106− id: row
107− required property var modelData
108− readonly property var dev: modelData
109− property bool pendingConnect: false
112+ ColumnLayout {
113+ id: col
114+ width: flick.width
115+ spacing: 2
110116
111− function afterPair() {
112− if (pendingConnect && page.paired(dev)) { pendingConnect = false; dev.connect() }
113− }
117+ Repeater {
118+ model: page.adapter && page.adapter.enabled ? page.devices : []
119+ delegate: ListRow {
120+ id: row
121+ required property var modelData
122+ readonly property var dev: modelData
123+ property bool pendingConnect: false
114124
115− icon: page.devIcon(dev)
116− title: dev.name || dev.address
117− current: dev.connected
118− subtitle: {
119− if (dev.state === BluetoothDeviceState.Connecting) return "connecting…"
120− if (dev.pairing) return "pairing…"
121− if (dev.connected)
122− return "connected" + (dev.batteryAvailable ? " · " + Math.round(dev.battery * 100) + "%" : "")
123− return page.paired(dev) ? "paired" : "available"
124− }
125− onClicked: {
126− if (dev.connected) dev.disconnect()
127− else if (page.paired(dev)) dev.connect()
128− else { pendingConnect = true; dev.trusted = true; dev.pair() }
129− }
125+ function afterPair() {
126+ if (pendingConnect && page.paired(dev)) { pendingConnect = false; dev.connect() }
127+ }
130128
131− // property name differs between Quickshell versions
132− Connections {
133− target: row.dev
134− ignoreUnknownSignals: true
135− function onBondedChanged() { row.afterPair() }
136− function onPairedChanged() { row.afterPair() }
137− }
138− IconBtn {
139− visible: page.paired(row.dev)
140− icon: Theme.iTrash
141− implicitHeight: 32
142− onClicked: row.dev.forget()
129+ icon: page.devIcon(dev)
130+ title: dev.name || dev.address
131+ current: dev.connected
132+ subtitle: {
133+ if (dev.state === BluetoothDeviceState.Connecting) return "connecting…"
134+ if (dev.pairing) return "pairing…"
135+ if (dev.connected)
136+ return "connected" + (dev.batteryAvailable ? " · " + Math.round(dev.battery * 100) + "%" : "")
137+ return page.paired(dev) ? "paired" : "available"
138+ }
139+ onClicked: {
140+ if (dev.connected) dev.disconnect()
141+ else if (page.paired(dev)) dev.connect()
142+ else { pendingConnect = true; dev.trusted = true; dev.pair() }
143+ }
144+
145+ // property name differs between Quickshell versions
146+ Connections {
147+ target: row.dev
148+ ignoreUnknownSignals: true
149+ function onBondedChanged() { row.afterPair() }
150+ function onPairedChanged() { row.afterPair() }
151+ }
152+ IconBtn {
153+ visible: page.paired(row.dev)
154+ icon: Theme.iTrash
155+ implicitHeight: 32
156+ onClicked: row.dev.forget()
157+ }
143158 }
144159 }
145160 }
146161 }
147162 }
163+ Item { visible: page.cards; Layout.fillHeight: true }
148164 }
M .config/quickshell/aurora/GpuSwitch.qml View
@@ -1,9 +1,10 @@
11 import QtQuick
22 import QtQuick.Layouts
33
4−// envycontrol mode pills with the two-click arm (SysPage, settings app > Power)
5−ColumnLayout {
4+// envycontrol mode pills with the two-click arm (SysPage, settings app > Power). cards: on a card (the settings app).
5+Group {
66 id: root
7+ property bool cards: false
78 // two-click arm before switching (needs a reboot to apply)
89 property string armedGpu: ""
910 Timer { id: disarm; interval: 3000; onTriggered: root.armedGpu = "" }
@@ -14,9 +15,11 @@ ColumnLayout {
1415 Gpu.set(m)
1516 }
1617 visible: Gpu.mode !== ""
17− Layout.fillWidth: true
18+ title: "graphics · envycontrol"
19+ keywords: "gpu graphics mode nvidia hybrid integrated dgpu prime"
20+ flat: !cards
1821 spacing: 10
19− SectionLabel { text: "graphics · envycontrol"; keywords: "gpu graphics mode nvidia hybrid integrated dgpu prime" }
22+ gap: 10
2023 RowLayout {
2124 Layout.fillWidth: true
2225 spacing: 6
@@ -37,7 +40,8 @@ ColumnLayout {
3740 Lb {
3841 visible: Gpu.pending !== "" || Gpu.message !== "" || Gpu.busy
3942 Layout.fillWidth: true
40− Layout.leftMargin: 6
43+ Layout.leftMargin: root.cards ? 18 : 6
44+ Layout.bottomMargin: root.cards ? 8 : 0
4145 wrapMode: Text.Wrap
4246 px: 10
4347 color: Gpu.message !== "" ? Theme.err : Theme.live
A .config/quickshell/aurora/Group.qml View
@@ -0,0 +1,41 @@
1+import QtQuick
2+import QtQuick.Layouts
3+
4+// A settings group: the caps title, then its rows on a rounded s1 card (Android / Nothing OS settings).
5+// flat = the title and rows without the card (pages shared with the control panel, which already is a card).
6+// SetIndex reads a literal `title:` (+ `keywords:`) as the group, like a SectionLabel's `text:`.
7+ColumnLayout {
8+ id: g
9+ property string title: ""
10+ property string keywords: "" // synonyms for the settings search (SetIndex reads them from the source)
11+ property string count: ""
12+ property bool flat: false
13+ property alias gap: body.spacing
14+ default property alias rows: body.data
15+
16+ readonly property int pad: flat ? 0 : 8
17+
18+ Layout.fillWidth: true
19+ Layout.fillHeight: false // a layout's default is true; a page sets it for a list that scrolls inside
20+ spacing: flat ? 4 : 10
21+
22+ SectionLabel {
23+ visible: g.title !== ""
24+ Layout.leftMargin: g.flat ? 0 : 14
25+ text: g.title
26+ count: g.count
27+ }
28+ Rectangle {
29+ Layout.fillWidth: true
30+ Layout.fillHeight: g.Layout.fillHeight // only when the group itself fills (a list that scrolls inside)
31+ implicitHeight: body.implicitHeight + 2 * g.pad
32+ radius: 28 + g.pad
33+ color: g.flat ? "transparent" : Theme.s1
34+ ColumnLayout {
35+ id: body
36+ anchors.fill: parent
37+ anchors.margins: g.pad
38+ spacing: 4
39+ }
40+ }
41+}
M .config/quickshell/aurora/NetPage.qml View
@@ -6,18 +6,19 @@ import QtQuick.Layouts
66 ColumnLayout {
77 id: page
88 property bool active: false
9+ property bool cards: false // the settings app: groups on cards
910 onActiveChanged: Net.active = active
1011 // the settings app creates this page already active and destroys it when leaving the section
1112 Component.onCompleted: if (active) Net.active = true
1213 Component.onDestruction: if (active) Net.active = false
13− spacing: 10
14+ spacing: cards ? 22 : 10
1415
1516 // wired / usb tethering (read-only status, plus a disconnect button)
16− ColumnLayout {
17+ Group {
1718 visible: Net.wiredDevs.length > 0
18− Layout.fillWidth: true
19− spacing: 4
20− SectionLabel { text: "wired"; keywords: "ethernet lan cable usb tethering phone" }
19+ flat: !page.cards
20+ title: "wired"
21+ keywords: "ethernet lan cable usb tethering phone"
2122 Repeater {
2223 model: Net.wiredDevs
2324 delegate: ListRow {
@@ -43,157 +44,173 @@ ColumnLayout {
4344 }
4445 }
4546
46− RowLayout {
47− Layout.fillWidth: true
48− Layout.leftMargin: 6
49− spacing: 8
50− Lb {
47+ // the radio: a card of its own in the settings app (Android's "use Wi-Fi")
48+ Group {
49+ flat: !page.cards
50+ gap: 10
51+ RowLayout {
5152 Layout.fillWidth: true
52− elide: Text.ElideRight
53− text: !Net.wifiEnabled ? "wi-fi · off"
54− : (Net.connected !== "" ? "wi-fi · " + Net.connected : "wi-fi · not connected")
55− color: Net.connected !== "" && Net.wifiEnabled ? Theme.fg : Theme.grey
56− }
57− IconBtn {
58− icon: Theme.iRefresh
59− active: Net.scanning
60− onClicked: Net.rescan()
61− }
62− IconBtn {
63− icon: Net.wifiEnabled ? Theme.iWifi : Theme.iWifiOff
64− active: Net.wifiEnabled
65− onClicked: Net.setWifi(!Net.wifiEnabled)
53+ Layout.leftMargin: page.cards ? 18 : 6
54+ Layout.preferredHeight: page.cards ? 56 : -1
55+ spacing: 8
56+ Lb {
57+ Layout.fillWidth: true
58+ elide: Text.ElideRight
59+ text: !Net.wifiEnabled ? "wi-fi · off"
60+ : (Net.connected !== "" ? "wi-fi · " + Net.connected : "wi-fi · not connected")
61+ color: Net.connected !== "" && Net.wifiEnabled ? Theme.fg : Theme.grey
62+ }
63+ IconBtn {
64+ icon: Theme.iRefresh
65+ active: Net.scanning
66+ onClicked: Net.rescan()
67+ }
68+ IconBtn {
69+ icon: Net.wifiEnabled ? Theme.iWifi : Theme.iWifiOff
70+ active: Net.wifiEnabled
71+ onClicked: Net.setWifi(!Net.wifiEnabled)
72+ }
6673 }
67− }
6874
69− Lb {
70− visible: Net.message !== ""
71− Layout.fillWidth: true
72− Layout.leftMargin: 6
73− text: Net.message
74− wrapMode: Text.WordWrap
75− px: 10
76− color: Theme.err
75+ Lb {
76+ visible: Net.message !== ""
77+ Layout.fillWidth: true
78+ Layout.leftMargin: page.cards ? 18 : 6
79+ Layout.bottomMargin: page.cards ? 10 : 0
80+ text: Net.message
81+ wrapMode: Text.WordWrap
82+ px: 10
83+ color: Theme.err
84+ }
7785 }
7886
79− SectionLabel { text: Net.busy ? "working…" : "networks"; count: Net.busy || !Net.wifiEnabled ? "" : String(Net.networks.length) }
80−
81− Flickable {
82− id: flick
83− Layout.fillWidth: true
87+ Group {
88+ title: Net.busy ? "working…" : "networks"
89+ count: Net.busy || !Net.wifiEnabled ? "" : String(Net.networks.length)
90+ flat: !page.cards
91+ spacing: 10
8492 Layout.fillHeight: true
85− Layout.preferredHeight: list.implicitHeight
86− clip: true
87− contentWidth: width
88− contentHeight: list.implicitHeight
89− boundsBehavior: Flickable.StopAtBounds
93+ // on a card: only as tall as the list, it scrolls inside once the page is full
94+ Layout.maximumHeight: page.cards ? implicitHeight : Infinity
95+
96+ Flickable {
97+ id: flick
98+ Layout.fillWidth: true
99+ Layout.fillHeight: true
100+ Layout.preferredHeight: list.implicitHeight
101+ clip: true
102+ contentWidth: width
103+ contentHeight: list.implicitHeight
104+ boundsBehavior: Flickable.StopAtBounds
90105
91− ColumnLayout {
92− id: list
93− width: flick.width
94− spacing: 2
106+ ColumnLayout {
107+ id: list
108+ width: flick.width
109+ spacing: 2
95110
96− Repeater {
97− model: Net.wifiEnabled ? Net.networks : []
98− delegate: ColumnLayout {
99− id: item
100− required property var modelData
101− Layout.fillWidth: true
102− spacing: 4
111+ Repeater {
112+ model: Net.wifiEnabled ? Net.networks : []
113+ delegate: ColumnLayout {
114+ id: item
115+ required property var modelData
116+ Layout.fillWidth: true
117+ spacing: 4
103118
104− ListRow {
105− id: row
106− icon: Theme.wifiIcon(item.modelData.signal)
107− title: item.modelData.ssid
108− readonly property bool portal: item.modelData.inUse && Net.portal && Net.portalNet === item.modelData.ssid
109− subtitle: portal ? "sign in required"
110− : item.modelData.inUse ? "connected"
111− : (item.modelData.saved ? "saved"
112− : (item.modelData.security === "" ? "open" : item.modelData.security))
113− trailing: item.modelData.signal + "%"
114− current: item.modelData.inUse
115− onClicked: Net.connectTo(item.modelData)
119+ ListRow {
120+ id: row
121+ icon: Theme.wifiIcon(item.modelData.signal)
122+ title: item.modelData.ssid
123+ readonly property bool portal: item.modelData.inUse && Net.portal && Net.portalNet === item.modelData.ssid
124+ subtitle: portal ? "sign in required"
125+ : item.modelData.inUse ? "connected"
126+ : (item.modelData.saved ? "saved"
127+ : (item.modelData.security === "" ? "open" : item.modelData.security))
128+ trailing: item.modelData.signal + "%"
129+ current: item.modelData.inUse
130+ onClicked: Net.connectTo(item.modelData)
116131
117− IconBtn {
118− visible: row.portal
119− icon: Theme.iLogin
120− implicitHeight: 32
121− onClicked: Net.openPortal()
122− }
123− IconBtn {
124− visible: item.modelData.inUse
125− icon: Theme.iClose
126− implicitHeight: 32
127− onClicked: Net.disconnect()
128− }
129− IconBtn {
130− visible: item.modelData.saved
131− icon: Theme.iTrash
132− implicitHeight: 32
133− onClicked: Net.forget(item.modelData.ssid)
132+ IconBtn {
133+ visible: row.portal
134+ icon: Theme.iLogin
135+ implicitHeight: 32
136+ onClicked: Net.openPortal()
137+ }
138+ IconBtn {
139+ visible: item.modelData.inUse
140+ icon: Theme.iClose
141+ implicitHeight: 32
142+ onClicked: Net.disconnect()
143+ }
144+ IconBtn {
145+ visible: item.modelData.saved
146+ icon: Theme.iTrash
147+ implicitHeight: 32
148+ onClicked: Net.forget(item.modelData.ssid)
149+ }
134150 }
135− }
136151
137− // password for a new secured network: a pill with white dots and the accent caret
138− Rectangle {
139− id: pwBox
140− visible: Net.askFor === item.modelData.ssid
141− Layout.fillWidth: true
142− Layout.preferredHeight: visible ? 52 : 0
143− radius: height / 2
144− color: Theme.s2
145− border.width: 1
146− border.color: pw.activeFocus ? Theme.fill : Theme.s3
147− onVisibleChanged: if (visible) { pw.text = ""; pw.forceActiveFocus() }
152+ // password for a new secured network: a pill with white dots and the accent caret
153+ Rectangle {
154+ id: pwBox
155+ visible: Net.askFor === item.modelData.ssid
156+ Layout.fillWidth: true
157+ Layout.preferredHeight: visible ? 52 : 0
158+ radius: height / 2
159+ color: Theme.s2
160+ border.width: 1
161+ border.color: pw.activeFocus ? Theme.fill : Theme.s3
162+ onVisibleChanged: if (visible) { pw.text = ""; pw.forceActiveFocus() }
148163
149− TextInput {
150− id: pw
151− anchors.left: parent.left
152− anchors.leftMargin: 22
153− anchors.right: go.left
154− anchors.rightMargin: 10
155− anchors.verticalCenter: parent.verticalCenter
156− echoMode: TextInput.Password
157− passwordCharacter: "•"
158− color: Theme.fg
159− selectionColor: Theme.fill
160− selectedTextColor: Theme.fillFg
161− font.family: Theme.mono
162− font.pixelSize: 15
163− font.letterSpacing: 2
164− clip: true
165− cursorDelegate: Rectangle {
166− width: 2
167− color: Theme.live
168− visible: pw.activeFocus
169− Timer {
170− running: pw.activeFocus
171− interval: 500
172− repeat: true
173− onTriggered: parent.opacity = 1 - parent.opacity
164+ TextInput {
165+ id: pw
166+ anchors.left: parent.left
167+ anchors.leftMargin: 22
168+ anchors.right: go.left
169+ anchors.rightMargin: 10
170+ anchors.verticalCenter: parent.verticalCenter
171+ echoMode: TextInput.Password
172+ passwordCharacter: "•"
173+ color: Theme.fg
174+ selectionColor: Theme.fill
175+ selectedTextColor: Theme.fillFg
176+ font.family: Theme.mono
177+ font.pixelSize: 15
178+ font.letterSpacing: 2
179+ clip: true
180+ cursorDelegate: Rectangle {
181+ width: 2
182+ color: Theme.live
183+ visible: pw.activeFocus
184+ Timer {
185+ running: pw.activeFocus
186+ interval: 500
187+ repeat: true
188+ onTriggered: parent.opacity = 1 - parent.opacity
189+ }
190+ }
191+ onAccepted: Net.connect(item.modelData.ssid, text)
192+ Lb {
193+ visible: !pw.text
194+ anchors.verticalCenter: parent.verticalCenter
195+ text: "password"
196+ color: Theme.mute
174197 }
175198 }
176− onAccepted: Net.connect(item.modelData.ssid, text)
177− Lb {
178− visible: !pw.text
199+ IconBtn {
200+ id: go
201+ anchors.right: parent.right
202+ anchors.rightMargin: 6
179203 anchors.verticalCenter: parent.verticalCenter
180− text: "password"
181− color: Theme.mute
204+ implicitHeight: 40
205+ icon: Theme.iCheck
206+ active: pw.text !== ""
207+ onClicked: Net.connect(item.modelData.ssid, pw.text)
182208 }
183209 }
184− IconBtn {
185− id: go
186− anchors.right: parent.right
187− anchors.rightMargin: 6
188− anchors.verticalCenter: parent.verticalCenter
189− implicitHeight: 40
190− icon: Theme.iCheck
191− active: pw.text !== ""
192− onClicked: Net.connect(item.modelData.ssid, pw.text)
193− }
194210 }
195211 }
196212 }
197213 }
198214 }
215+ Item { visible: page.cards; Layout.fillHeight: true }
199216 }
M .config/quickshell/aurora/SetAbout.qml View
@@ -43,90 +43,118 @@ Flickable {
4343 ColumnLayout {
4444 id: col
4545 width: flick.width
46− spacing: 8
46+ spacing: 22
4747
48− RowLayout {
49− spacing: 14
50− Text {
51− text: Theme.iArch
52− color: Theme.fill
53− font.family: Theme.ui
54− font.pixelSize: 56
55− }
56− ColumnLayout {
57− spacing: 2
48+ // the machine at a glance, like Android's "about phone" header
49+ Group {
50+ RowLayout {
51+ Layout.margins: 14
52+ Layout.leftMargin: 22
53+ spacing: 18
5854 Text {
59− text: flick.info.length ? flick.info[0][1] : ""
60− color: Theme.fg
55+ text: Theme.iArch
56+ color: Theme.fill
6157 font.family: Theme.ui
62− font.pixelSize: 21
63− font.bold: true
58+ font.pixelSize: 56
6459 }
65− Text {
66− text: Quickshell.env("USER") + "@" + (flick.info.length > 2 ? flick.info[2][1] : "")
67− + " · up " + Theme.fmtTime(SysStats.uptime)
68− color: Theme.fgDim
69− font.family: Theme.ui
70− font.pixelSize: 15
60+ ColumnLayout {
61+ spacing: 2
62+ Text {
63+ text: flick.info.length ? flick.info[0][1] : ""
64+ color: Theme.fg
65+ font.family: Theme.ui
66+ font.pixelSize: 21
67+ font.bold: true
68+ }
69+ Text {
70+ text: Quickshell.env("USER") + "@" + (flick.info.length > 2 ? flick.info[2][1] : "")
71+ + " · up " + Theme.fmtTime(SysStats.uptime)
72+ color: Theme.fgDim
73+ font.family: Theme.ui
74+ font.pixelSize: 15
75+ }
7176 }
7277 }
7378 }
7479
75− SectionLabel { text: "system"; keywords: "kernel cpu gpu host hostname distro packages version uptime"; Layout.topMargin: 6 }
76− Repeater {
77− model: flick.info
78− delegate: RowLayout {
79− required property var modelData
80− Layout.fillWidth: true
81− spacing: 8
82− Text {
83− Layout.preferredWidth: 150
84− text: modelData[0]
85− color: Theme.fill
86− font.family: Theme.ui
87− font.pixelSize: 15
88− }
89− Text {
80+ Group {
81+ title: "system"
82+ keywords: "kernel cpu gpu host hostname distro packages version uptime"
83+ gap: 0
84+ Repeater {
85+ model: flick.info
86+ delegate: RowLayout {
87+ required property var modelData
9088 Layout.fillWidth: true
91− Layout.leftMargin: 18
92− text: modelData[1]
93− color: Theme.fg
94− elide: Text.ElideRight
95− font.family: Theme.ui
96− font.pixelSize: 15
89+ Layout.preferredHeight: 40
90+ spacing: 8
91+ Text {
92+ Layout.preferredWidth: 150
93+ Layout.leftMargin: 18
94+ text: modelData[0]
95+ color: Theme.fill
96+ font.family: Theme.ui
97+ font.pixelSize: 15
98+ }
99+ Text {
100+ Layout.fillWidth: true
101+ Layout.leftMargin: 18
102+ text: modelData[1]
103+ color: Theme.fg
104+ elide: Text.ElideRight
105+ font.family: Theme.ui
106+ font.pixelSize: 15
107+ }
97108 }
98109 }
99110 }
100111
101− SectionLabel { text: "memory & storage"; keywords: "ram disk space drives usage"; Layout.topMargin: 6 }
102− StatBar {
103− label: "memory"
104− frac: SysStats.memTotal > 0 ? SysStats.memUsed / SysStats.memTotal : 0
105− valueText: flick.size(SysStats.memUsed) + " / " + flick.size(SysStats.memTotal)
106− }
107− Repeater {
108− model: SysStats.disks
109− delegate: StatBar {
110− required property var modelData
111− label: modelData.label
112− frac: modelData.used / modelData.size
113− fill: frac > 0.9 ? Theme.err : Theme.fill
114− valueText: flick.size(modelData.used) + " / " + flick.size(modelData.size)
112+ Group {
113+ title: "memory & storage"
114+ keywords: "ram disk space drives usage"
115+ gap: 14
116+ StatBar {
117+ Layout.leftMargin: 18
118+ Layout.rightMargin: 18
119+ Layout.topMargin: 12
120+ label: "memory"
121+ frac: SysStats.memTotal > 0 ? SysStats.memUsed / SysStats.memTotal : 0
122+ valueText: flick.size(SysStats.memUsed) + " / " + flick.size(SysStats.memTotal)
123+ }
124+ Repeater {
125+ model: SysStats.disks
126+ delegate: StatBar {
127+ required property var modelData
128+ Layout.leftMargin: 18
129+ Layout.rightMargin: 18
130+ label: modelData.label
131+ frac: modelData.used / modelData.size
132+ fill: frac > 0.9 ? Theme.err : Theme.fill
133+ valueText: flick.size(modelData.used) + " / " + flick.size(modelData.size)
134+ }
115135 }
136+ Item { Layout.preferredHeight: 2 }
116137 }
117138
118− SectionLabel { text: "credits"; keywords: "license glyph-rice fonts"; Layout.topMargin: 6 }
119− Text {
120− Layout.fillWidth: true
121− Layout.leftMargin: 18
122− wrapMode: Text.WordWrap
123− lineHeight: 1.3
124− color: Theme.fgDim
125− font.family: Theme.ui
126− font.pixelSize: 14
127− text: "The dot-matrix shaders and some components started as ports from glyph-rice by Nebelung-cloud "
128− + "(GPL-3.0). Fonts: Doto, Space Grotesk, Space Mono (SIL OFL). Colours: matugen (Material You). Nothing, Nothing OS and Glyph are trademarks of Nothing Technology Ltd; "
129− + "this is a fan project, not affiliated with Nothing."
139+ Group {
140+ title: "credits"
141+ keywords: "license glyph-rice fonts"
142+ Text {
143+ Layout.fillWidth: true
144+ Layout.leftMargin: 18
145+ Layout.rightMargin: 18
146+ Layout.topMargin: 10
147+ Layout.bottomMargin: 10
148+ wrapMode: Text.WordWrap
149+ lineHeight: 1.3
150+ color: Theme.fgDim
151+ font.family: Theme.ui
152+ font.pixelSize: 14
153+ text: "The dot-matrix shaders and some components started as ports from glyph-rice by Nebelung-cloud "
154+ + "(GPL-3.0). Fonts: Doto, Space Grotesk, Space Mono (SIL OFL). Colours: matugen (Material You). Nothing, Nothing OS and Glyph are trademarks of Nothing Technology Ltd; "
155+ + "this is a fan project, not affiliated with Nothing."
156+ }
130157 }
158+
131159 }
132160 }
M .config/quickshell/aurora/SetAppearance.qml View
@@ -15,130 +15,159 @@ Flickable {
1515 ColumnLayout {
1616 id: col
1717 width: flick.width
18− spacing: 10
18+ spacing: 22
1919
20− SectionLabel { text: "wallpaper"; keywords: "background image picture" }
21− Text {
22− Layout.fillWidth: true
23− Layout.leftMargin: 18
24− text: Wall.current !== "" ? Wall.current.replace(Wall.home, "~") : "-"
25− elide: Text.ElideLeft
26− color: Theme.fgDim
27− font.family: Theme.ui
28− font.pixelSize: 14
29− }
30− RowLayout {
31− Layout.fillWidth: true
32− spacing: 6
33− IconBtn {
20+ Group {
21+ title: "wallpaper"
22+ keywords: "background image picture"
23+ gap: 10
24+ Text {
3425 Layout.fillWidth: true
35− icon: Theme.iImage
36− label: "open picker"
37− keywords: "wallpaper picker choose background"
38− onClicked: flick.overlayRequested("wall")
26+ Layout.leftMargin: 18
27+ Layout.rightMargin: 18
28+ Layout.topMargin: 8
29+ text: Wall.current !== "" ? Wall.current.replace(Wall.home, "~") : "-"
30+ elide: Text.ElideLeft
31+ color: Theme.fgDim
32+ font.family: Theme.ui
33+ font.pixelSize: 14
3934 }
40− IconBtn {
35+ RowLayout {
4136 Layout.fillWidth: true
42− icon: Theme.iShuffle
43− label: "random from folder"
44− keywords: "shuffle random wallpaper background"
45− onClicked: Wall.random()
37+ spacing: 6
38+ IconBtn {
39+ Layout.fillWidth: true
40+ icon: Theme.iImage
41+ label: "open picker"
42+ keywords: "wallpaper picker choose background"
43+ onClicked: flick.overlayRequested("wall")
44+ }
45+ IconBtn {
46+ Layout.fillWidth: true
47+ icon: Theme.iShuffle
48+ label: "random from folder"
49+ keywords: "shuffle random wallpaper background"
50+ onClicked: Wall.random()
51+ }
4652 }
47− }
48− Text {
49− text: "random picks from"
50− color: Theme.fg
51− font.family: Theme.ui
52− font.pixelSize: 15
53− }
54− Flow {
55− Layout.fillWidth: true
56− spacing: 4
57− Repeater {
58− model: [""].concat(Wall.folders)
59− delegate: IconBtn {
60− required property string modelData
61− implicitHeight: 26
62− label: modelData === "" ? "all" : modelData
63− active: Settings.wallFolder === modelData
64− onClicked: Settings.wallFolder = modelData
53+ Text {
54+ Layout.leftMargin: 18
55+ Layout.topMargin: 4
56+ text: "random picks from"
57+ color: Theme.fg
58+ font.family: Theme.ui
59+ font.pixelSize: 15
60+ }
61+ Flow {
62+ Layout.fillWidth: true
63+ Layout.leftMargin: 14
64+ Layout.rightMargin: 14
65+ Layout.bottomMargin: 8
66+ spacing: 4
67+ Repeater {
68+ model: [""].concat(Wall.folders)
69+ delegate: IconBtn {
70+ required property string modelData
71+ implicitHeight: 26
72+ label: modelData === "" ? "all" : modelData
73+ active: Settings.wallFolder === modelData
74+ onClicked: Settings.wallFolder = modelData
75+ }
6576 }
6677 }
6778 }
6879
69− SectionLabel { text: "wallpaper look"; keywords: "wallpaper style halftone" }
70− Choice {
71− label: "drawn as"
72− keywords: "wallpaper style look"
73− options: [{ value: "dots", label: "mono dots" }, { value: "colour", label: "colour dots" }, { value: "photo", label: "photo" }]
74− value: Settings.wallLook
75− onPicked: v => Settings.wallLook = v
80+ Group {
81+ title: "wallpaper look"
82+ keywords: "wallpaper style halftone"
83+ Choice {
84+ label: "drawn as"
85+ keywords: "wallpaper style look"
86+ options: [{ value: "dots", label: "mono dots" }, { value: "colour", label: "colour dots" }, { value: "photo", label: "photo" }]
87+ value: Settings.wallLook
88+ onPicked: v => Settings.wallLook = v
89+ }
90+ Knob { label: "dot size"; keywords: "pitch grid"; from: 6; to: 24; value: Settings.wallPitch; unit: "px"; onMoved: v => Settings.wallPitch = v }
91+ Knob { label: "brightness"; keywords: "wallpaper dim dark"; from: 20; to: 100; value: Settings.wallBright; unit: "%"; onMoved: v => Settings.wallBright = v }
92+ Switch { label: "dot grid"; hint: "on the desktop when there is no wallpaper"; checked: Settings.dotGrid; onToggled: on => Settings.dotGrid = on }
7693 }
77− Knob { label: "dot size"; keywords: "pitch grid"; from: 6; to: 24; value: Settings.wallPitch; unit: "px"; onMoved: v => Settings.wallPitch = v }
78− Knob { label: "brightness"; keywords: "wallpaper dim dark"; from: 20; to: 100; value: Settings.wallBright; unit: "%"; onMoved: v => Settings.wallBright = v }
79− Switch { label: "dot grid"; hint: "on the desktop when there is no wallpaper"; checked: Settings.dotGrid; onToggled: on => Settings.dotGrid = on }
8094
81− SectionLabel { text: "desktop"; keywords: "home screen" }
82− Switch { label: "24-hour clock"; keywords: "time format 12-hour am pm"; checked: Settings.clock24; onToggled: on => Settings.clock24 = on }
83− Switch { label: "blinking colon"; keywords: "clock blink"; checked: Settings.blink; onToggled: on => Settings.blink = on }
84− Switch { label: "animations"; keywords: "motion transitions reduce"; hint: "panels slide and fade, dots light up in waves"; checked: Settings.motion; onToggled: on => Settings.motion = on }
85− Choice {
86− label: "app icons"
87− keywords: "dock icons"
88− options: [{ value: "dots", label: "dots" }, { value: "mono", label: "mono" }, { value: "colour", label: "colour" }]
89− value: Settings.appIcons
90− onPicked: v => Settings.appIcons = v
95+ Group {
96+ title: "desktop"
97+ keywords: "home screen"
98+ Switch { label: "24-hour clock"; keywords: "time format 12-hour am pm"; checked: Settings.clock24; onToggled: on => Settings.clock24 = on }
99+ Switch { label: "blinking colon"; keywords: "clock blink"; checked: Settings.blink; onToggled: on => Settings.blink = on }
100+ Switch { label: "animations"; keywords: "motion transitions reduce"; hint: "panels slide and fade, dots light up in waves"; checked: Settings.motion; onToggled: on => Settings.motion = on }
101+ Choice {
102+ label: "app icons"
103+ keywords: "dock icons"
104+ options: [{ value: "dots", label: "dots" }, { value: "mono", label: "mono" }, { value: "colour", label: "colour" }]
105+ value: Settings.appIcons
106+ onPicked: v => Settings.appIcons = v
107+ }
91108 }
92109
93− SectionLabel {
94− text: "colours"
110+ Group {
111+ title: "colours"
95112 keywords: "colors theme scheme accent palette matugen material tonal-spot vibrant expressive fidelity content fruit-salad rainbow neutral monochrome"
96− }
97− Text {
98− Layout.fillWidth: true
99− Layout.leftMargin: 18
100− text: "matugen scheme from the wallpaper, accent = its primary"
101− elide: Text.ElideRight
102− color: Theme.fgDim
103− font.family: Theme.ui
104− font.pixelSize: 14
105− }
106− Flow {
107− Layout.fillWidth: true
108− spacing: 4
109− Repeater {
110− model: ["tonal-spot", "vibrant", "expressive", "fidelity", "content", "fruit-salad", "rainbow", "neutral", "monochrome"]
111− delegate: IconBtn {
112− required property string modelData
113− implicitHeight: 26
114− label: modelData
115− active: Settings.matuScheme === "scheme-" + modelData
116− onClicked: Settings.matuScheme = "scheme-" + modelData
113+ gap: 10
114+ Text {
115+ Layout.fillWidth: true
116+ Layout.leftMargin: 18
117+ Layout.rightMargin: 18
118+ Layout.topMargin: 8
119+ text: "matugen scheme from the wallpaper, accent = its primary"
120+ elide: Text.ElideRight
121+ color: Theme.fgDim
122+ font.family: Theme.ui
123+ font.pixelSize: 14
124+ }
125+ Flow {
126+ Layout.fillWidth: true
127+ Layout.leftMargin: 14
128+ Layout.rightMargin: 14
129+ spacing: 4
130+ Repeater {
131+ model: ["tonal-spot", "vibrant", "expressive", "fidelity", "content", "fruit-salad", "rainbow", "neutral", "monochrome"]
132+ delegate: IconBtn {
133+ required property string modelData
134+ implicitHeight: 26
135+ label: modelData
136+ active: Settings.matuScheme === "scheme-" + modelData
137+ onClicked: Settings.matuScheme = "scheme-" + modelData
138+ }
117139 }
118140 }
141+ Knob { label: "contrast"; keywords: "colours colors"; from: -1; to: 1; decimals: 2; value: Settings.matuContrast; onMoved: v => Settings.matuContrast = v }
119142 }
120− Knob { label: "contrast"; keywords: "colours colors"; from: -1; to: 1; decimals: 2; value: Settings.matuContrast; onMoved: v => Settings.matuContrast = v }
121143
122− SectionLabel { text: "bar"; keywords: "status bar sizes" }
123− Choice {
124− label: "bar layout"
125− keywords: "vertical horizontal orientation side left top position"
126− options: [{ value: "horizontal", label: "horizontal" }, { value: "vertical", label: "vertical" }]
127− value: Settings.barLayout
128− onPicked: v => Settings.barLayout = v
129− }
130− Choice {
131− label: "bar shape"
132− keywords: "floating pill attached edge strip rounded"
133− options: [{ value: false, label: "attached" }, { value: true, label: "floating pill" }]
134− value: Settings.barFloat
135− onPicked: v => Settings.barFloat = v
144+ Group {
145+ title: "bar"
146+ keywords: "status bar sizes"
147+ Choice {
148+ label: "bar layout"
149+ keywords: "vertical horizontal orientation side left top position"
150+ options: [{ value: "horizontal", label: "horizontal" }, { value: "vertical", label: "vertical" }]
151+ value: Settings.barLayout
152+ onPicked: v => Settings.barLayout = v
153+ }
154+ Choice {
155+ label: "bar shape"
156+ keywords: "floating pill attached edge strip rounded"
157+ options: [{ value: false, label: "attached" }, { value: true, label: "floating pill" }]
158+ value: Settings.barFloat
159+ onPicked: v => Settings.barFloat = v
160+ }
161+ Knob { visible: !Theme.barVertical; label: "bar height"; keywords: "top bar size"; from: 32; to: 56; value: Settings.barSize; onMoved: v => Settings.barSize = v }
162+ Knob { visible: Theme.barVertical; label: "bar width"; keywords: "vertical side bar size"; from: 48; to: 80; value: Settings.barWidth; onMoved: v => Settings.barWidth = v }
136163 }
137− Knob { visible: !Theme.barVertical; label: "bar height"; keywords: "top bar size"; from: 32; to: 56; value: Settings.barSize; onMoved: v => Settings.barSize = v }
138− Knob { visible: Theme.barVertical; label: "bar width"; keywords: "vertical side bar size"; from: 48; to: 80; value: Settings.barWidth; onMoved: v => Settings.barWidth = v }
139164
140− SectionLabel { text: "transparency"; keywords: "transparent opacity alpha translucent" }
141− Knob { label: "bar opacity"; keywords: "transparent"; from: 0.3; to: 1; decimals: 2; value: Settings.barAlpha; onMoved: v => Settings.barAlpha = v }
142− Knob { label: "panel opacity"; keywords: "transparent control panel"; from: 0.3; to: 1; decimals: 2; value: Settings.panelAlpha; onMoved: v => Settings.panelAlpha = v }
165+ Group {
166+ title: "transparency"
167+ keywords: "transparent opacity alpha translucent"
168+ Knob { label: "bar opacity"; keywords: "transparent"; from: 0.3; to: 1; decimals: 2; value: Settings.barAlpha; onMoved: v => Settings.barAlpha = v }
169+ Knob { label: "panel opacity"; keywords: "transparent control panel"; from: 0.3; to: 1; decimals: 2; value: Settings.panelAlpha; onMoved: v => Settings.panelAlpha = v }
170+ }
171+ Item { Layout.preferredHeight: 20 }
143172 }
144173 }
M .config/quickshell/aurora/SetDisplay.qml View
@@ -140,19 +140,20 @@ Flickable {
140140 ColumnLayout {
141141 id: col
142142 width: flick.width
143− spacing: 8
143+ spacing: 22
144144
145145 // keep / revert bar after a monitor change
146146 Rectangle {
147147 Layout.fillWidth: true
148148 visible: flick.countdown > 0
149− implicitHeight: 40
149+ implicitHeight: 56
150+ radius: height / 2
150151 color: Theme.s2
151152 border.width: 1
152153 border.color: Theme.fill
153154 RowLayout {
154155 anchors.fill: parent
155− anchors.margins: 6
156+ anchors.margins: 10
156157 Text {
157158 Layout.fillWidth: true
158159 Layout.leftMargin: 18
@@ -176,39 +177,46 @@ Flickable {
176177 font.pixelSize: 14
177178 }
178179
179− SectionLabel { text: "layout"; keywords: "monitors screens" }
180− Choice {
181− label: "screens"
182− keywords: "monitors extend external laptop mirror"
183− options: [{ value: "extend", label: "extend" }, { value: "laptop", label: "laptop only" },
184− { value: "external", label: "external only" }]
185− value: flick.layout
186− onPicked: x => flick.setLayout(x)
187− }
188− Text {
189− Layout.fillWidth: true
190− Layout.leftMargin: 18
191− text: "applies whenever a monitor is plugged in. external only turns the laptop panel back on when the last external screen is unplugged."
192− color: Theme.fgDim
193− wrapMode: Text.WordWrap
194− font.family: Theme.ui
195− font.pixelSize: 14
180+ Group {
181+ title: "layout"
182+ keywords: "monitors screens"
183+ Choice {
184+ label: "screens"
185+ keywords: "monitors extend external laptop mirror"
186+ options: [{ value: "extend", label: "extend" }, { value: "laptop", label: "laptop only" },
187+ { value: "external", label: "external only" }]
188+ value: flick.layout
189+ onPicked: x => flick.setLayout(x)
190+ }
191+ Text {
192+ Layout.fillWidth: true
193+ Layout.leftMargin: 18
194+ Layout.rightMargin: 18
195+ Layout.bottomMargin: 10
196+ text: "applies whenever a monitor is plugged in. external only turns the laptop panel back on when the last external screen is unplugged."
197+ color: Theme.fgDim
198+ wrapMode: Text.WordWrap
199+ font.family: Theme.ui
200+ font.pixelSize: 14
201+ }
196202 }
197203
198204 Repeater {
199205 model: flick.monitors
200− delegate: ColumnLayout {
206+ // one card per monitor
207+ delegate: Group {
201208 id: mon
202209 required property var modelData
203210 readonly property var m: modelData
204211 readonly property string curMode: m.width + "x" + m.height
205− Layout.fillWidth: true
206− spacing: 6
212+ title: mon.m.name
213+ gap: 8
207214
208− SectionLabel { text: mon.m.name }
209215 Text {
210216 Layout.fillWidth: true
211217 Layout.leftMargin: 18
218+ Layout.rightMargin: 18
219+ Layout.topMargin: 10
212220 text: mon.m.description + (mon.m.disabled ? " · disabled"
213221 : " · " + mon.curMode + " @ " + mon.m.refreshRate.toFixed(2) + "Hz · scale " + mon.m.scale
214222 + " · at " + mon.m.x + "," + mon.m.y)
@@ -219,6 +227,8 @@ Flickable {
219227 }
220228 Text {
221229 visible: !mon.m.disabled
230+ Layout.leftMargin: 18
231+ Layout.topMargin: 4
222232 text: "mode"
223233 color: Theme.fg
224234 font.family: Theme.ui
@@ -226,6 +236,8 @@ Flickable {
226236 }
227237 Flow {
228238 Layout.fillWidth: true
239+ Layout.leftMargin: 14
240+ Layout.rightMargin: 14
229241 visible: !mon.m.disabled
230242 spacing: 4
231243 Repeater {
@@ -264,31 +276,38 @@ Flickable {
264276 }
265277 }
266278
267− SectionLabel { text: "brightness & colour"; keywords: "backlight color" }
268− Knob {
269− visible: Bright.available
270− label: "laptop panel"
271− keywords: "brightness backlight screen"
272− unit: "%"
273− from: 1
274− to: 100
275− value: Bright.percent
276− onMoved: x => Bright.setPercent(x)
277− }
278− Switch {
279− visible: Toggles.hasNightLight
280− label: "night light"
281− keywords: "blue light warm redshift gammastep"
282− hint: "gammastep's warm colours (its schedule comes from ~/.config/gammastep)"
283− checked: Toggles.nightLight
284− onToggled: on => Toggles.setNightLight(on)
285− }
286− Text {
287− visible: !Toggles.hasNightLight
288− text: "night light: gammastep isn't running"
289− color: Theme.fgDim
290− font.family: Theme.ui
291− font.pixelSize: 14
279+ Group {
280+ title: "brightness & colour"
281+ keywords: "backlight color"
282+ Knob {
283+ visible: Bright.available
284+ label: "laptop panel"
285+ keywords: "brightness backlight screen"
286+ unit: "%"
287+ from: 1
288+ to: 100
289+ value: Bright.percent
290+ onMoved: x => Bright.setPercent(x)
291+ }
292+ Switch {
293+ visible: Toggles.hasNightLight
294+ label: "night light"
295+ keywords: "blue light warm redshift gammastep"
296+ hint: "gammastep's warm colours (its schedule comes from ~/.config/gammastep)"
297+ checked: Toggles.nightLight
298+ onToggled: on => Toggles.setNightLight(on)
299+ }
300+ Text {
301+ visible: !Toggles.hasNightLight
302+ Layout.leftMargin: 18
303+ Layout.topMargin: 12
304+ Layout.bottomMargin: 12
305+ text: "night light: gammastep isn't running"
306+ color: Theme.fgDim
307+ font.family: Theme.ui
308+ font.pixelSize: 14
309+ }
292310 }
311+ Item { Layout.preferredHeight: 12 }
293312 }
294313 }
M .config/quickshell/aurora/SetIndex.qml View
@@ -4,34 +4,34 @@ import Quickshell
44 import Quickshell.Io
55
66 // The settings app's sections and its search index. Like Android indexes each preference XML, the rows are parsed out
7−// of the page files themselves: SectionLabel (a group) and Switch / Knob / Choice / Field / Timeout with a literal
8−// `label:`, plus their `hint:`, `keywords:` (synonyms, read only here) and a Choice's option labels. IconBtn only with
9−// `keywords:` (opt-in). Non-literal labels aren't indexed. `extras` covers rows without a label (wi-fi, bluetooth power).
7+// of the page files themselves: Group / SectionLabel (a group: its literal `title:` / `text:`) and Switch / Knob /
8+// Choice / Field / Timeout with a literal `label:`, plus their `hint:`, `keywords:` (synonyms, read only here) and a
9+// Choice's option labels. IconBtn only with `keywords:` (opt-in). Non-literal labels aren't indexed. `extras` covers rows without a label (wi-fi, bluetooth power).
1010 // Editing a page hot-reloads the shell, which re-parses it.
1111 Singleton {
1212 id: idx
1313
1414 // files: parsed in this order (a page's composite rows like BatteryInfo / GpuSwitch count as the page)
1515 readonly property var sections: [
16− { id: "network", word: "network", caption: "network · wi-fi, wired, tethering", files: ["NetPage.qml"],
16+ { id: "network", icon: Theme.iWifi, word: "network", caption: "network · wi-fi, wired, tethering", files: ["NetPage.qml"],
1717 keywords: "wifi internet ethernet" },
18− { id: "bluetooth", word: "bluetooth", caption: "bluetooth · devices, pairing", files: ["BtPage.qml"], keywords: "bt" },
19− { id: "sound", word: "sound", caption: "sound · levels, devices, apps", files: ["AudioPage.qml"],
18+ { id: "bluetooth", icon: Theme.iBt, word: "bluetooth", caption: "bluetooth · devices, pairing", files: ["BtPage.qml"], keywords: "bt" },
19+ { id: "sound", icon: Theme.iVolHigh, word: "sound", caption: "sound · levels, devices, apps", files: ["AudioPage.qml"],
2020 keywords: "audio volume speakers microphone" },
21− { id: "appearance", word: "look", caption: "look · wallpaper, home screen, colours, bar", files: ["SetAppearance.qml"],
21+ { id: "appearance", icon: Theme.iPalette, word: "look", caption: "look · wallpaper, home screen, colours, bar", files: ["SetAppearance.qml"],
2222 keywords: "appearance theme style" },
23− { id: "shell", word: "shell", caption: "shell · home screen, dock, osd, notifications", files: ["SetShell.qml"],
23+ { id: "shell", icon: Theme.iDashboard, word: "shell", caption: "shell · home screen, dock, osd, notifications", files: ["SetShell.qml"],
2424 keywords: "aurora" },
25− { id: "windows", word: "windows", caption: "windows · hyprland gaps, borders, effects, layout", files: ["SetWindows.qml"],
25+ { id: "windows", icon: Theme.iWindows, word: "windows", caption: "windows · hyprland gaps, borders, effects, layout", files: ["SetWindows.qml"],
2626 keywords: "hyprland tiling" },
27− { id: "input", word: "input", caption: "input · keyboard, mouse, touchpad", files: ["SetInput.qml"], keywords: "" },
28− { id: "keybinds", word: "keys", caption: "keys · hyprland binds", files: [],
27+ { id: "input", icon: Theme.iMouse, word: "input", caption: "input · keyboard, mouse, touchpad", files: ["SetInput.qml"], keywords: "" },
28+ { id: "keybinds", icon: Theme.iKeyboard, word: "keys", caption: "keys · hyprland binds", files: [],
2929 keywords: "keybinds shortcuts hotkeys bindings" },
30− { id: "display", word: "display", caption: "display · monitors, layout, night light", files: ["SetDisplay.qml"],
30+ { id: "display", icon: Theme.iMonitor, word: "display", caption: "display · monitors, layout, night light", files: ["SetDisplay.qml"],
3131 keywords: "screen monitor resolution" },
32− { id: "power", word: "power", caption: "power · battery, profiles, idle", files: ["BatteryInfo.qml", "SetPower.qml", "GpuSwitch.qml"],
32+ { id: "power", icon: Theme.iBolt, word: "power", caption: "power · battery, profiles, idle", files: ["BatteryInfo.qml", "SetPower.qml", "GpuSwitch.qml"],
3333 keywords: "energy sleep" },
34− { id: "about", word: "about", caption: "about · this system", files: ["SetAbout.qml"], keywords: "info version" }
34+ { id: "about", icon: Theme.iArch, word: "about", caption: "about · this system", files: ["SetAbout.qml"], keywords: "info version" }
3535 ]
3636
3737 // rows the parser can't see. group = the SectionLabel to land on ("" = the top of the section)
@@ -113,11 +113,11 @@ Singleton {
113113 return { top: top, inner: inner }
114114 }
115115 function parse(file, t) {
116− var out = [], group = "", groupKw = "", re = /\b(SectionLabel|Switch|Knob|Choice|Field|Timeout|IconBtn)\s*\{/g, m
116+ var out = [], group = "", groupKw = "", re = /\b(Group|SectionLabel|Switch|Knob|Choice|Field|Timeout|IconBtn)\s*\{/g, m
117117 while ((m = re.exec(t)) !== null) {
118118 var b = block(t, re.lastIndex), kw = lit(b.top, "keywords")
119− if (m[1] === "SectionLabel") {
120− group = lit(b.top, "text") || ""
119+ if (m[1] === "Group" || m[1] === "SectionLabel") {
120+ group = lit(b.top, m[1] === "Group" ? "title" : "text") || ""
121121 groupKw = group !== "" ? kw || "" : ""
122122 if (group !== "") out.push({ kind: "group", group: group, label: group, hint: "", keywords: groupKw, context: "" })
123123 continue
M .config/quickshell/aurora/SetInput.qml View
@@ -15,7 +15,7 @@ Flickable {
1515 ColumnLayout {
1616 id: col
1717 width: flick.width
18− spacing: 8
18+ spacing: 22
1919
2020 Text {
2121 Layout.fillWidth: true
@@ -28,47 +28,61 @@ Flickable {
2828 font.pixelSize: 14
2929 }
3030
31− SectionLabel { text: "keyboard"; keywords: "xkb typing" }
32− Field { label: "layouts"; keywords: "keyboard layout language kb_layout"; placeholder: "us,de"; value: flick.v("input:kb_layout", ""); onSubmitted: x => HyprConf.set("input:kb_layout", x) }
33− Field { label: "variants"; keywords: "keyboard variant dvorak colemak"; placeholder: "(none)"; value: flick.v("input:kb_variant", ""); onSubmitted: x => HyprConf.set("input:kb_variant", x) }
34− Field { label: "xkb options"; keywords: "caps lock compose kb_options"; placeholder: "grp:alt_shift_toggle"; value: flick.v("input:kb_options", ""); onSubmitted: x => HyprConf.set("input:kb_options", x) }
35− Knob { label: "repeat rate"; keywords: "key repeat speed"; unit: "/s"; from: 10; to: 100; value: flick.v("input:repeat_rate", 25); onMoved: x => HyprConf.set("input:repeat_rate", x) }
36− Knob { label: "repeat delay"; keywords: "key repeat"; unit: "ms"; from: 150; to: 1000; value: flick.v("input:repeat_delay", 600); onMoved: x => HyprConf.set("input:repeat_delay", Math.round(x / 10) * 10) }
37−
38− SectionLabel { text: "mouse"; keywords: "pointer" }
39− Knob { label: "sensitivity"; keywords: "mouse speed pointer"; from: -1; to: 1; decimals: 2; value: flick.v("input:sensitivity", 0); onMoved: x => HyprConf.set("input:sensitivity", x) }
40− Choice {
41− label: "acceleration"
42− keywords: "mouse accel"
43− options: [{ value: "", label: "default" }, { value: "flat", label: "flat" }, { value: "adaptive", label: "adaptive" }]
44− value: flick.v("input:accel_profile", "")
45− onPicked: x => HyprConf.set("input:accel_profile", x)
31+ Group {
32+ title: "keyboard"
33+ keywords: "xkb typing"
34+ Field { label: "layouts"; keywords: "keyboard layout language kb_layout"; placeholder: "us,de"; value: flick.v("input:kb_layout", ""); onSubmitted: x => HyprConf.set("input:kb_layout", x) }
35+ Field { label: "variants"; keywords: "keyboard variant dvorak colemak"; placeholder: "(none)"; value: flick.v("input:kb_variant", ""); onSubmitted: x => HyprConf.set("input:kb_variant", x) }
36+ Field { label: "xkb options"; keywords: "caps lock compose kb_options"; placeholder: "grp:alt_shift_toggle"; value: flick.v("input:kb_options", ""); onSubmitted: x => HyprConf.set("input:kb_options", x) }
37+ Knob { label: "repeat rate"; keywords: "key repeat speed"; unit: "/s"; from: 10; to: 100; value: flick.v("input:repeat_rate", 25); onMoved: x => HyprConf.set("input:repeat_rate", x) }
38+ Knob { label: "repeat delay"; keywords: "key repeat"; unit: "ms"; from: 150; to: 1000; value: flick.v("input:repeat_delay", 600); onMoved: x => HyprConf.set("input:repeat_delay", Math.round(x / 10) * 10) }
4639 }
47− Choice {
48− label: "focus follows mouse"
49− keywords: "focus hover"
50− options: [{ value: 0, label: "off" }, { value: 1, label: "always" }, { value: 2, label: "loose" }, { value: 3, label: "hover only" }]
51− value: flick.v("input:follow_mouse", -1)
52− onPicked: x => HyprConf.set("input:follow_mouse", x)
40+
41+ Group {
42+ title: "mouse"
43+ keywords: "pointer"
44+ Knob { label: "sensitivity"; keywords: "mouse speed pointer"; from: -1; to: 1; decimals: 2; value: flick.v("input:sensitivity", 0); onMoved: x => HyprConf.set("input:sensitivity", x) }
45+ Choice {
46+ label: "acceleration"
47+ keywords: "mouse accel"
48+ options: [{ value: "", label: "default" }, { value: "flat", label: "flat" }, { value: "adaptive", label: "adaptive" }]
49+ value: flick.v("input:accel_profile", "")
50+ onPicked: x => HyprConf.set("input:accel_profile", x)
51+ }
52+ Choice {
53+ label: "focus follows mouse"
54+ keywords: "focus hover"
55+ options: [{ value: 0, label: "off" }, { value: 1, label: "always" }, { value: 2, label: "loose" }, { value: 3, label: "hover only" }]
56+ value: flick.v("input:follow_mouse", -1)
57+ onPicked: x => HyprConf.set("input:follow_mouse", x)
58+ }
59+ Switch { label: "natural scroll"; keywords: "reverse scrolling"; checked: flick.v("input:natural_scroll", false); onToggled: on => HyprConf.set("input:natural_scroll", on) }
60+ Switch { label: "left handed"; keywords: "swap buttons"; checked: flick.v("input:left_handed", false); onToggled: on => HyprConf.set("input:left_handed", on) }
5361 }
54− Switch { label: "natural scroll"; keywords: "reverse scrolling"; checked: flick.v("input:natural_scroll", false); onToggled: on => HyprConf.set("input:natural_scroll", on) }
55− Switch { label: "left handed"; keywords: "swap buttons"; checked: flick.v("input:left_handed", false); onToggled: on => HyprConf.set("input:left_handed", on) }
5662
57− SectionLabel { text: "touchpad"; keywords: "trackpad" }
58− Switch { label: "natural scroll"; keywords: "reverse scrolling"; checked: flick.v("input:touchpad:natural_scroll", false); onToggled: on => HyprConf.set("input:touchpad:natural_scroll", on) }
59− Switch { label: "tap to click"; keywords: "tap"; checked: flick.v("input:touchpad:tap_to_click", false); onToggled: on => HyprConf.set("input:touchpad:tap_to_click", on) }
60− Switch { label: "disable while typing"; keywords: "palm rejection dwt"; checked: flick.v("input:touchpad:disable_while_typing", false); onToggled: on => HyprConf.set("input:touchpad:disable_while_typing", on) }
61− Knob { label: "scroll speed"; keywords: "scroll factor"; from: 0.1; to: 3; decimals: 2; value: flick.v("input:touchpad:scroll_factor", 1); onMoved: x => HyprConf.set("input:touchpad:scroll_factor", x) }
63+ Group {
64+ title: "touchpad"
65+ keywords: "trackpad"
66+ Switch { label: "natural scroll"; keywords: "reverse scrolling"; checked: flick.v("input:touchpad:natural_scroll", false); onToggled: on => HyprConf.set("input:touchpad:natural_scroll", on) }
67+ Switch { label: "tap to click"; keywords: "tap"; checked: flick.v("input:touchpad:tap_to_click", false); onToggled: on => HyprConf.set("input:touchpad:tap_to_click", on) }
68+ Switch { label: "disable while typing"; keywords: "palm rejection dwt"; checked: flick.v("input:touchpad:disable_while_typing", false); onToggled: on => HyprConf.set("input:touchpad:disable_while_typing", on) }
69+ Knob { label: "scroll speed"; keywords: "scroll factor"; from: 0.1; to: 3; decimals: 2; value: flick.v("input:touchpad:scroll_factor", 1); onMoved: x => HyprConf.set("input:touchpad:scroll_factor", x) }
70+ }
6271
63− SectionLabel { text: "gestures"; keywords: "swipe touchpad fingers" }
64− Switch { label: "invert swipe"; hint: "reverse the 3-finger workspace swipe"; checked: flick.v("gestures:workspace_swipe_invert", true); onToggled: on => HyprConf.set("gestures:workspace_swipe_invert", on) }
65− Knob { label: "swipe distance"; unit: "px"; from: 100; to: 1000; value: flick.v("gestures:workspace_swipe_distance", 300); onMoved: x => HyprConf.set("gestures:workspace_swipe_distance", Math.round(x / 10) * 10) }
72+ Group {
73+ title: "gestures"
74+ keywords: "swipe touchpad fingers"
75+ Switch { label: "invert swipe"; hint: "reverse the 3-finger workspace swipe"; checked: flick.v("gestures:workspace_swipe_invert", true); onToggled: on => HyprConf.set("gestures:workspace_swipe_invert", on) }
76+ Knob { label: "swipe distance"; unit: "px"; from: 100; to: 1000; value: flick.v("gestures:workspace_swipe_distance", 300); onMoved: x => HyprConf.set("gestures:workspace_swipe_distance", Math.round(x / 10) * 10) }
77+ }
6678
67− SectionLabel { text: "cursor"; keywords: "mouse pointer" }
68− Knob { label: "hide when idle"; keywords: "cursor inactive timeout"; unit: "s"; from: 0; to: 30; value: flick.v("cursor:inactive_timeout", 0); onMoved: x => HyprConf.set("cursor:inactive_timeout", x) }
69− Switch { label: "hide while typing"; keywords: "cursor"; checked: flick.v("cursor:hide_on_key_press", false); onToggled: on => HyprConf.set("cursor:hide_on_key_press", on) }
79+ Group {
80+ title: "cursor"
81+ keywords: "mouse pointer"
82+ Knob { label: "hide when idle"; keywords: "cursor inactive timeout"; unit: "s"; from: 0; to: 30; value: flick.v("cursor:inactive_timeout", 0); onMoved: x => HyprConf.set("cursor:inactive_timeout", x) }
83+ Switch { label: "hide while typing"; keywords: "cursor"; checked: flick.v("cursor:hide_on_key_press", false); onToggled: on => HyprConf.set("cursor:hide_on_key_press", on) }
84+ }
7085
71− Item { Layout.preferredHeight: 6 }
7286 RowLayout {
7387 Layout.fillWidth: true
7488 Text {
@@ -82,5 +96,6 @@ Flickable {
8296 }
8397 IconBtn { icon: Theme.iRefresh; label: "reset to config"; keywords: "reset defaults restore hyprland"; onClicked: HyprConf.resetAll(["input:", "cursor:", "gestures:"]) }
8498 }
99+ Item { Layout.preferredHeight: 12 }
85100 }
86101 }
M .config/quickshell/aurora/SetKeys.qml View
@@ -7,7 +7,7 @@ import Quickshell.Io
77 // (`hyprctl binds` only shows "__lua <n>" for Lua binds). Comments above binds become the groups.
88 ColumnLayout {
99 id: page
10− spacing: 8
10+ spacing: 16
1111 readonly property string file: Quickshell.env("HOME") + "/.config/hypr/source/keybinds.lua"
1212 property var binds: [] // [{group, keys, action}]
1313 property string query: ""
@@ -99,49 +99,71 @@ ColumnLayout {
9999 Layout.fillWidth: true
100100 Layout.fillHeight: true
101101 clip: true
102− spacing: 2
102+ spacing: 0
103103 model: page.shown
104104 boundsBehavior: Flickable.StopAtBounds
105− delegate: RowLayout {
105+ // a group header, or a bind on its group's card: s1 rows whose outer corners round where the group starts / ends
106+ delegate: Item {
107+ id: d
106108 required property var modelData
109+ required property int index
107110 readonly property bool isHeader: modelData.header !== undefined
111+ readonly property bool first: index === 0 || page.shown[index - 1].header !== undefined
112+ readonly property bool last: index === page.shown.length - 1 || page.shown[index + 1].header !== undefined
108113 width: ListView.view.width
109− spacing: 10
114+ height: isHeader ? (index === 0 ? 0 : 22) + 30 : 44 + (first ? 8 : 0) + (last ? 8 : 0)
110115 SectionLabel {
111− visible: parent.isHeader
112− Layout.topMargin: 6
113− text: parent.isHeader ? parent.modelData.header : ""
116+ visible: d.isHeader
117+ anchors.left: parent.left
118+ anchors.leftMargin: 14
119+ anchors.right: parent.right
120+ anchors.bottom: parent.bottom
121+ anchors.bottomMargin: 10
122+ text: d.isHeader ? d.modelData.header : ""
114123 }
115124 Rectangle {
116− visible: !parent.isHeader
117− Layout.preferredWidth: 240
118− Layout.leftMargin: 18
119− implicitHeight: 30
120− radius: 15
121− color: Theme.s2
122− Lb {
123− anchors.fill: parent
124− anchors.leftMargin: 14
125− anchors.rightMargin: 10
126− verticalAlignment: Text.AlignVCenter
127− text: modelData.keys || ""
128− color: Theme.fill
129− elide: Text.ElideRight
130− px: 10
125+ visible: !d.isHeader
126+ anchors.fill: parent
127+ color: Theme.s1
128+ topLeftRadius: d.first ? 36 : 0
129+ topRightRadius: d.first ? 36 : 0
130+ bottomLeftRadius: d.last ? 36 : 0
131+ bottomRightRadius: d.last ? 36 : 0
132+ RowLayout {
133+ x: 26
134+ y: (d.first ? 8 : 0) + (44 - height) / 2
135+ width: parent.width - 52
136+ spacing: 10
137+ Rectangle {
138+ Layout.preferredWidth: 240
139+ implicitHeight: 30
140+ radius: 15
141+ color: Theme.s2
142+ Lb {
143+ anchors.fill: parent
144+ anchors.leftMargin: 14
145+ anchors.rightMargin: 10
146+ verticalAlignment: Text.AlignVCenter
147+ text: d.modelData.keys || ""
148+ color: Theme.fill
149+ elide: Text.ElideRight
150+ px: 10
151+ }
152+ }
153+ Text {
154+ Layout.fillWidth: true
155+ text: d.modelData.action || ""
156+ color: Theme.fg
157+ elide: Text.ElideRight
158+ font.family: Theme.ui
159+ font.pixelSize: 14
160+ }
131161 }
132162 }
133− Text {
134− visible: !parent.isHeader
135− Layout.fillWidth: true
136− text: modelData.action || ""
137− color: Theme.fg
138− elide: Text.ElideRight
139− font.family: Theme.ui
140− font.pixelSize: 14
141− }
142163 }
143164 }
144165 Text {
166+ Layout.leftMargin: 18
145167 text: page.binds.length + " binds · " + page.file.replace(Quickshell.env("HOME"), "~")
146168 color: Theme.fgDim
147169 font.family: Theme.ui
M .config/quickshell/aurora/SetPower.qml View
@@ -65,95 +65,110 @@ Flickable {
6565 ColumnLayout {
6666 id: col
6767 width: flick.width
68− spacing: 8
68+ spacing: 22
6969
70− BatteryInfo {}
70+ BatteryInfo { cards: true }
7171
72− SectionLabel { text: "battery care"; keywords: "battery health"; Layout.topMargin: 6; visible: Battery.hasBat }
73− Choice {
74− visible: Battery.supported
75− label: "charge limit"
76− keywords: "conservation 80% battery health longevity"
77− options: [{ value: "off", label: "off" }, { value: "conservation", label: "conservation" }]
78− value: Settings.chargeMode === "off" ? "off" : "conservation"
79− onPicked: v => Settings.chargeMode = v
80− }
81− Text {
82− visible: Battery.supported
83− Layout.fillWidth: true
84− wrapMode: Text.WordWrap
85− text: Settings.chargeMode !== "off"
86− ? "the firmware stops charging at ~80%, even when aurora isn't running. Conservation mode is "
87− + (Battery.capOn ? "on" : "off") + "."
88− : "charges to 100%"
89− color: Theme.fgDim
90− font.family: Theme.ui
91− font.pixelSize: 13
92− }
93− Knob {
72+ Group {
73+ title: "battery care"
74+ keywords: "battery health"
9475 visible: Battery.hasBat
95− label: "warn at"
96− keywords: "low battery warning"
97− from: 5; to: 40; unit: "%"
98− value: Settings.batLow
99− onMoved: v => Settings.batLow = v
100− }
101− Knob {
102− visible: Battery.hasBat
103− label: "critical at"
104− keywords: "critical battery"
105− from: 2; to: 15; unit: "%"
106− value: Settings.batCritical
107− onMoved: v => Settings.batCritical = v
108− }
109− Choice {
110− visible: Battery.hasBat
111− label: "when critical"
112− keywords: "low battery action"
113− options: [{ value: "none", label: "warn" }, { value: "suspend", label: "suspend" },
114− { value: "poweroff", label: "power off" }]
115− value: Settings.batCriticalAction
116− onPicked: v => Settings.batCriticalAction = v
76+ Choice {
77+ visible: Battery.supported
78+ label: "charge limit"
79+ keywords: "conservation 80% battery health longevity"
80+ options: [{ value: "off", label: "off" }, { value: "conservation", label: "conservation" }]
81+ value: Settings.chargeMode === "off" ? "off" : "conservation"
82+ onPicked: v => Settings.chargeMode = v
83+ }
84+ Text {
85+ visible: Battery.supported
86+ Layout.fillWidth: true
87+ Layout.leftMargin: 18
88+ Layout.rightMargin: 18
89+ Layout.bottomMargin: 6
90+ wrapMode: Text.WordWrap
91+ text: Settings.chargeMode !== "off"
92+ ? "the firmware stops charging at ~80%, even when aurora isn't running. Conservation mode is "
93+ + (Battery.capOn ? "on" : "off") + "."
94+ : "charges to 100%"
95+ color: Theme.fgDim
96+ font.family: Theme.ui
97+ font.pixelSize: 13
98+ }
99+ Knob {
100+ visible: Battery.hasBat
101+ label: "warn at"
102+ keywords: "low battery warning"
103+ from: 5; to: 40; unit: "%"
104+ value: Settings.batLow
105+ onMoved: v => Settings.batLow = v
106+ }
107+ Knob {
108+ visible: Battery.hasBat
109+ label: "critical at"
110+ keywords: "critical battery"
111+ from: 2; to: 15; unit: "%"
112+ value: Settings.batCritical
113+ onMoved: v => Settings.batCritical = v
114+ }
115+ Choice {
116+ visible: Battery.hasBat
117+ label: "when critical"
118+ keywords: "low battery action"
119+ options: [{ value: "none", label: "warn" }, { value: "suspend", label: "suspend" },
120+ { value: "poweroff", label: "power off" }]
121+ value: Settings.batCriticalAction
122+ onPicked: v => Settings.batCriticalAction = v
123+ }
117124 }
118125
119− Choice {
120− Layout.topMargin: 6
121− visible: Battery.hasProfiles
122− label: "power profile"
126+ Group {
127+ title: "power profile"
123128 keywords: "performance balanced saver power-profiles-daemon"
124− options: flick.profiles.concat(Battery.hasBat ? [{ value: "auto", label: "auto" }] : [])
125− value: Battery.profile
126− onPicked: v => Battery.setProfile(v)
127− }
128− Choice {
129− visible: Battery.hasProfiles && Settings.powerAuto
130− label: "on AC"
131− keywords: "plugged in charger power profile"
132− options: flick.profiles
133− value: Settings.powerAc
134− onPicked: v => Settings.powerAc = v
135− }
136− Choice {
137− visible: Battery.hasProfiles && Settings.powerAuto
138− label: "on battery"
139− keywords: "unplugged power profile"
140− options: flick.profiles
141− value: Settings.powerBat
142− onPicked: v => Settings.powerBat = v
129+ visible: Battery.hasProfiles
130+ Choice {
131+ visible: Battery.hasProfiles
132+ label: "power profile"
133+ keywords: "performance balanced saver power-profiles-daemon"
134+ options: flick.profiles.concat(Battery.hasBat ? [{ value: "auto", label: "auto" }] : [])
135+ value: Battery.profile
136+ onPicked: v => Battery.setProfile(v)
137+ }
138+ Choice {
139+ visible: Battery.hasProfiles && Settings.powerAuto
140+ label: "on AC"
141+ keywords: "plugged in charger power profile"
142+ options: flick.profiles
143+ value: Settings.powerAc
144+ onPicked: v => Settings.powerAc = v
145+ }
146+ Choice {
147+ visible: Battery.hasProfiles && Settings.powerAuto
148+ label: "on battery"
149+ keywords: "unplugged power profile"
150+ options: flick.profiles
151+ value: Settings.powerBat
152+ onPicked: v => Settings.powerBat = v
153+ }
143154 }
144− GpuSwitch { Layout.topMargin: 6 }
145155
146− SectionLabel { text: "when idle"; keywords: "idle timeout sleep hypridle"; Layout.topMargin: 6 }
147− Timeout { label: "dim the screen"; keywords: "idle dim timeout"; key: "idleDim"; value: Idle.dim }
148− Timeout { label: "screen off"; keywords: "dpms display off idle timeout"; key: "idleOff"; value: Idle.screenOff }
149− Timeout { label: "lock"; keywords: "lock screen hyprlock idle timeout"; key: "idleLock"; value: Idle.lock }
150− Timeout { label: "suspend"; keywords: "sleep idle timeout"; key: "idleSuspend"; value: Idle.suspend; max: 3600 }
151− Switch {
152− label: "caffeine"
153− keywords: "stay awake keep awake inhibit idle"
154− hint: "keep the screen awake until turned off (not saved)"
155− checked: Toggles.caffeine
156− onToggled: on => Toggles.caffeine = on
156+ GpuSwitch { cards: true }
157+
158+ Group {
159+ title: "when idle"
160+ keywords: "idle timeout sleep hypridle"
161+ Timeout { label: "dim the screen"; keywords: "idle dim timeout"; key: "idleDim"; value: Idle.dim }
162+ Timeout { label: "screen off"; keywords: "dpms display off idle timeout"; key: "idleOff"; value: Idle.screenOff }
163+ Timeout { label: "lock"; keywords: "lock screen hyprlock idle timeout"; key: "idleLock"; value: Idle.lock }
164+ Timeout { label: "suspend"; keywords: "sleep idle timeout"; key: "idleSuspend"; value: Idle.suspend; max: 3600 }
165+ Switch {
166+ label: "caffeine"
167+ keywords: "stay awake keep awake inhibit idle"
168+ hint: "keep the screen awake until turned off (not saved)"
169+ checked: Toggles.caffeine
170+ onToggled: on => Toggles.caffeine = on
171+ }
157172 }
158173 Text {
159174 Layout.fillWidth: true
@@ -164,5 +179,6 @@ Flickable {
164179 font.family: Theme.ui
165180 font.pixelSize: 13
166181 }
182+ Item { Layout.preferredHeight: 12 }
167183 }
168184 }
M .config/quickshell/aurora/SetShell.qml View
@@ -12,76 +12,100 @@ Flickable {
1212 ColumnLayout {
1313 id: col
1414 width: flick.width
15− spacing: 8
15+ spacing: 22
1616
17− SectionLabel { text: "desktop"; keywords: "home screen widgets" }
18− Switch { label: "clock"; keywords: "desktop clock widget date"; hint: "Doto clock, date, stats and what's playing on the desktop"; checked: Settings.desktopClock; onToggled: on => Settings.desktopClock = on }
19− Switch { label: "visualizer"; keywords: "cava spectrum music audio"; hint: "cava as columns of dots along the bottom while music plays"; checked: Settings.desktopViz; onToggled: on => Settings.desktopViz = on }
20− Knob { label: "visualizer bars"; keywords: "cava spectrum"; from: 16; to: 128; value: Settings.vizBars; onMoved: v => Settings.vizBars = v }
21−
22− SectionLabel { text: "dock"; keywords: "taskbar launcher pinned apps" }
23− Switch { label: "dock"; keywords: "taskbar launcher"; hint: "autohides at the bottom edge"; checked: Settings.dock; onToggled: on => Settings.dock = on }
24− Text {
25− text: "pinned apps (pin more from the dock's right-click)"
26− color: Theme.fgDim
27− font.family: Theme.ui
28− font.pixelSize: 14
17+ Group {
18+ title: "desktop"
19+ keywords: "home screen widgets"
20+ Switch { label: "clock"; keywords: "desktop clock widget date"; hint: "Doto clock, date, stats and what's playing on the desktop"; checked: Settings.desktopClock; onToggled: on => Settings.desktopClock = on }
21+ Switch { label: "visualizer"; keywords: "cava spectrum music audio"; hint: "cava as columns of dots along the bottom while music plays"; checked: Settings.desktopViz; onToggled: on => Settings.desktopViz = on }
22+ Knob { label: "visualizer bars"; keywords: "cava spectrum"; from: 16; to: 128; value: Settings.vizBars; onMoved: v => Settings.vizBars = v }
2923 }
30− Flow {
31− Layout.fillWidth: true
32− spacing: 4
33− Repeater {
34− model: Settings.dockPinned
35− delegate: IconBtn {
36− required property string modelData
37− implicitHeight: 26
38− icon: Theme.iClose
39− iconSize: 11
40− label: modelData
41− onClicked: Settings.togglePin(modelData)
24+
25+ Group {
26+ title: "dock"
27+ keywords: "taskbar launcher pinned apps"
28+ Switch { label: "dock"; keywords: "taskbar launcher"; hint: "autohides at the bottom edge"; checked: Settings.dock; onToggled: on => Settings.dock = on }
29+ Text {
30+ Layout.leftMargin: 18
31+ Layout.topMargin: 4
32+ text: "pinned apps (pin more from the dock's right-click)"
33+ color: Theme.fgDim
34+ font.family: Theme.ui
35+ font.pixelSize: 14
36+ }
37+ Flow {
38+ Layout.fillWidth: true
39+ Layout.leftMargin: 14
40+ Layout.rightMargin: 14
41+ Layout.bottomMargin: 8
42+ spacing: 4
43+ Repeater {
44+ model: Settings.dockPinned
45+ delegate: IconBtn {
46+ required property string modelData
47+ implicitHeight: 26
48+ icon: Theme.iClose
49+ iconSize: 11
50+ label: modelData
51+ onClicked: Settings.togglePin(modelData)
52+ }
4253 }
4354 }
4455 }
4556
46− SectionLabel { text: "on-screen display"; keywords: "osd popup indicator" }
47− Knob { label: "osd timeout"; keywords: "on-screen display duration volume brightness popup"; unit: "ms"; from: 500; to: 4000; value: Settings.osdTimeout; onMoved: v => Settings.osdTimeout = Math.round(v / 100) * 100 }
48−
49− SectionLabel { text: "network" }
50− Switch { label: "captive portals"; keywords: "hotel campus wifi login sign in hotspot"; hint: "open the login page in the browser when a network needs one"; checked: Settings.portalAutoOpen; onToggled: on => Settings.portalAutoOpen = on }
57+ Group {
58+ title: "on-screen display"
59+ keywords: "osd popup indicator"
60+ Knob { label: "osd timeout"; keywords: "on-screen display duration volume brightness popup"; unit: "ms"; from: 500; to: 4000; value: Settings.osdTimeout; onMoved: v => Settings.osdTimeout = Math.round(v / 100) * 100 }
61+ }
5162
52− SectionLabel { text: "bar" }
53− Switch { label: "privacy indicators"; keywords: "microphone mic camera webcam screen sharing recording"; hint: "show in the bar when an app records the mic, uses the camera or shares the screen"; checked: Settings.privacyIndicators; onToggled: on => Settings.privacyIndicators = on }
63+ Group {
64+ title: "network"
65+ Switch { label: "captive portals"; keywords: "hotel campus wifi login sign in hotspot"; hint: "open the login page in the browser when a network needs one"; checked: Settings.portalAutoOpen; onToggled: on => Settings.portalAutoOpen = on }
66+ }
5467
55− SectionLabel { text: "weather"; keywords: "forecast temperature" }
56− Field {
57− label: "location"
58− keywords: "city place weather"
59− placeholder: "by IP · or a city · or lat,lon"
60− value: Settings.weatherLoc
61− onSubmitted: v => Settings.weatherLoc = v.trim()
68+ Group {
69+ title: "bar"
70+ Switch { label: "privacy indicators"; keywords: "microphone mic camera webcam screen sharing recording"; hint: "show in the bar when an app records the mic, uses the camera or shares the screen"; checked: Settings.privacyIndicators; onToggled: on => Settings.privacyIndicators = on }
6271 }
63− Text {
64− Layout.fillWidth: true
65− Layout.leftMargin: 18
66− text: Weather.ok ? (Weather.place !== "" ? Weather.place + ": " : "") + Math.round(Weather.now.temp) + "°, " + Weather.name(Weather.now.code)
67− + (Weather.error !== "" ? " (" + Weather.error + ")" : "")
68− : Weather.busy ? "looking outside..." : Weather.error || "no weather yet"
69− color: Weather.error !== "" ? Theme.err : Theme.fgDim
70− font.family: Theme.ui
71− font.pixelSize: 13
72+
73+ Group {
74+ title: "weather"
75+ keywords: "forecast temperature"
76+ Field {
77+ label: "location"
78+ keywords: "city place weather"
79+ placeholder: "by IP · or a city · or lat,lon"
80+ value: Settings.weatherLoc
81+ onSubmitted: v => Settings.weatherLoc = v.trim()
82+ }
83+ Text {
84+ Layout.fillWidth: true
85+ Layout.leftMargin: 18
86+ Layout.bottomMargin: 8
87+ text: Weather.ok ? (Weather.place !== "" ? Weather.place + ": " : "") + Math.round(Weather.now.temp) + "°, " + Weather.name(Weather.now.code)
88+ + (Weather.error !== "" ? " (" + Weather.error + ")" : "")
89+ : Weather.busy ? "looking outside..." : Weather.error || "no weather yet"
90+ color: Weather.error !== "" ? Theme.err : Theme.fgDim
91+ font.family: Theme.ui
92+ font.pixelSize: 13
93+ }
7294 }
7395
74− SectionLabel { text: "notifications"; keywords: "popups toasts alerts" }
75− Switch { label: "do not disturb"; keywords: "dnd silence quiet focus"; hint: "hide popups and mute the sound (critical ones still show)"; checked: Notifs.dnd; onToggled: on => Notifs.dnd = on }
76− Switch { label: "sound"; keywords: "notification chime"; checked: Settings.notifSound; onToggled: on => Settings.notifSound = on }
77− RowLayout {
78− Layout.fillWidth: true
79− spacing: 8
80− Knob { label: "sound volume"; keywords: "notification chime loudness"; from: 0; to: 1; decimals: 2; value: Settings.notifVolume; onMoved: v => Settings.notifVolume = v }
81− IconBtn { icon: Theme.iPlay; implicitHeight: 26; onClicked: Notifs.playSound() }
96+ Group {
97+ title: "notifications"
98+ keywords: "popups toasts alerts"
99+ Switch { label: "do not disturb"; keywords: "dnd silence quiet focus"; hint: "hide popups and mute the sound (critical ones still show)"; checked: Notifs.dnd; onToggled: on => Notifs.dnd = on }
100+ Switch { label: "sound"; keywords: "notification chime"; checked: Settings.notifSound; onToggled: on => Settings.notifSound = on }
101+ RowLayout {
102+ Layout.fillWidth: true
103+ spacing: 8
104+ Knob { label: "sound volume"; keywords: "notification chime loudness"; from: 0; to: 1; decimals: 2; value: Settings.notifVolume; onMoved: v => Settings.notifVolume = v }
105+ IconBtn { Layout.rightMargin: 14; icon: Theme.iPlay; implicitHeight: 26; onClicked: Notifs.playSound() }
106+ }
82107 }
83108
84− Item { Layout.preferredHeight: 6 }
85109 RowLayout {
86110 Layout.fillWidth: true
87111 Text {
@@ -95,5 +119,6 @@ Flickable {
95119 }
96120 IconBtn { icon: Theme.iRefresh; label: "reset shell settings"; keywords: "reset defaults restore"; onClicked: Settings.reset() }
97121 }
122+ Item { Layout.preferredHeight: 12 }
98123 }
99124 }
M .config/quickshell/aurora/SetWindows.qml View
@@ -16,7 +16,7 @@ Flickable {
1616 ColumnLayout {
1717 id: col
1818 width: flick.width
19− spacing: 8
19+ spacing: 22
2020
2121 Text {
2222 Layout.fillWidth: true
@@ -29,75 +29,86 @@ Flickable {
2929 font.pixelSize: 14
3030 }
3131
32− SectionLabel { text: "gaps & borders"; keywords: "spacing margins" }
33− Knob { label: "inner gaps"; keywords: "spacing padding gaps_in"; unit: "px"; from: 0; to: 30; value: flick.v("general:gaps_in", 0); onMoved: x => HyprConf.set("general:gaps_in", x) }
34− Knob { label: "outer gaps"; keywords: "spacing margin gaps_out"; unit: "px"; from: 0; to: 40; value: flick.v("general:gaps_out", 0); onMoved: x => HyprConf.set("general:gaps_out", x) }
35− Knob { label: "border"; keywords: "border size thickness outline"; unit: "px"; from: 0; to: 8; value: flick.v("general:border_size", 0); onMoved: x => HyprConf.set("general:border_size", x) }
36− Knob { label: "rounding"; keywords: "corners radius rounded"; unit: "px"; from: 0; to: 20; value: flick.v("decoration:rounding", 0); onMoved: x => HyprConf.set("decoration:rounding", x) }
37− Switch { label: "resize on border"; hint: "drag window edges to resize"; checked: flick.v("general:resize_on_border", false); onToggled: on => HyprConf.set("general:resize_on_border", on) }
38−
39− SectionLabel { text: "opacity"; keywords: "transparency transparent alpha" }
40− Knob { label: "focused"; keywords: "active window opacity"; from: 0.3; to: 1; decimals: 2; value: flick.v("decoration:active_opacity", 1); onMoved: x => HyprConf.set("decoration:active_opacity", x) }
41− Knob { label: "unfocused"; keywords: "inactive window opacity"; from: 0.3; to: 1; decimals: 2; value: flick.v("decoration:inactive_opacity", 1); onMoved: x => HyprConf.set("decoration:inactive_opacity", x) }
42− Switch { label: "dim unfocused"; keywords: "inactive windows darken"; checked: flick.v("decoration:dim_inactive", false); onToggled: on => HyprConf.set("decoration:dim_inactive", on) }
32+ Group {
33+ title: "gaps & borders"
34+ keywords: "spacing margins"
35+ Knob { label: "inner gaps"; keywords: "spacing padding gaps_in"; unit: "px"; from: 0; to: 30; value: flick.v("general:gaps_in", 0); onMoved: x => HyprConf.set("general:gaps_in", x) }
36+ Knob { label: "outer gaps"; keywords: "spacing margin gaps_out"; unit: "px"; from: 0; to: 40; value: flick.v("general:gaps_out", 0); onMoved: x => HyprConf.set("general:gaps_out", x) }
37+ Knob { label: "border"; keywords: "border size thickness outline"; unit: "px"; from: 0; to: 8; value: flick.v("general:border_size", 0); onMoved: x => HyprConf.set("general:border_size", x) }
38+ Knob { label: "rounding"; keywords: "corners radius rounded"; unit: "px"; from: 0; to: 20; value: flick.v("decoration:rounding", 0); onMoved: x => HyprConf.set("decoration:rounding", x) }
39+ Switch { label: "resize on border"; hint: "drag window edges to resize"; checked: flick.v("general:resize_on_border", false); onToggled: on => HyprConf.set("general:resize_on_border", on) }
40+ }
4341
44− SectionLabel { text: "effects" }
45− Switch { label: "blur"; keywords: "frosted glass"; hint: "behind transparent windows and layers"; checked: flick.v("decoration:blur:enabled", false); onToggled: on => HyprConf.set("decoration:blur:enabled", on) }
46− Knob { label: "blur size"; from: 1; to: 15; value: flick.v("decoration:blur:size", 1); onMoved: x => HyprConf.set("decoration:blur:size", x) }
47− Knob { label: "blur passes"; from: 1; to: 6; value: flick.v("decoration:blur:passes", 1); onMoved: x => HyprConf.set("decoration:blur:passes", x) }
48− Switch { label: "blur x-ray"; hint: "floating windows blur the wallpaper, not what's under them"; checked: flick.v("decoration:blur:xray", false); onToggled: on => HyprConf.set("decoration:blur:xray", on) }
49− Switch { label: "shadows"; keywords: "drop shadow"; checked: flick.v("decoration:shadow:enabled", false); onToggled: on => HyprConf.set("decoration:shadow:enabled", on) }
50− Switch { label: "animations"; keywords: "window motion transitions"; checked: flick.v("animations:enabled", false); onToggled: on => HyprConf.set("animations:enabled", on) }
42+ Group {
43+ title: "opacity"
44+ keywords: "transparency transparent alpha"
45+ Knob { label: "focused"; keywords: "active window opacity"; from: 0.3; to: 1; decimals: 2; value: flick.v("decoration:active_opacity", 1); onMoved: x => HyprConf.set("decoration:active_opacity", x) }
46+ Knob { label: "unfocused"; keywords: "inactive window opacity"; from: 0.3; to: 1; decimals: 2; value: flick.v("decoration:inactive_opacity", 1); onMoved: x => HyprConf.set("decoration:inactive_opacity", x) }
47+ Switch { label: "dim unfocused"; keywords: "inactive windows darken"; checked: flick.v("decoration:dim_inactive", false); onToggled: on => HyprConf.set("decoration:dim_inactive", on) }
48+ }
5149
52− SectionLabel { text: "layout" }
53− Choice {
54− label: "tiling layout"
55− keywords: "tiling mode"
56− options: [{ value: "master", label: "master" }, { value: "dwindle", label: "dwindle" }]
57− value: flick.v("general:layout", "")
58− onPicked: x => HyprConf.set("general:layout", x)
50+ Group {
51+ title: "effects"
52+ Switch { label: "blur"; keywords: "frosted glass"; hint: "behind transparent windows and layers"; checked: flick.v("decoration:blur:enabled", false); onToggled: on => HyprConf.set("decoration:blur:enabled", on) }
53+ Knob { label: "blur size"; from: 1; to: 15; value: flick.v("decoration:blur:size", 1); onMoved: x => HyprConf.set("decoration:blur:size", x) }
54+ Knob { label: "blur passes"; from: 1; to: 6; value: flick.v("decoration:blur:passes", 1); onMoved: x => HyprConf.set("decoration:blur:passes", x) }
55+ Switch { label: "blur x-ray"; hint: "floating windows blur the wallpaper, not what's under them"; checked: flick.v("decoration:blur:xray", false); onToggled: on => HyprConf.set("decoration:blur:xray", on) }
56+ Switch { label: "shadows"; keywords: "drop shadow"; checked: flick.v("decoration:shadow:enabled", false); onToggled: on => HyprConf.set("decoration:shadow:enabled", on) }
57+ Switch { label: "animations"; keywords: "window motion transitions"; checked: flick.v("animations:enabled", false); onToggled: on => HyprConf.set("animations:enabled", on) }
5958 }
60− ColumnLayout {
61− Layout.fillWidth: true
62− visible: flick.v("general:layout", "") === "master"
63− spacing: 8
64− Knob { label: "master width"; keywords: "mfact ratio split size"; from: 0.1; to: 0.9; decimals: 2; value: flick.v("master:mfact", 0.55); onMoved: x => HyprConf.set("master:mfact", x) }
59+
60+ Group {
61+ title: "layout"
6562 Choice {
66− label: "new window"
67− keywords: "placement"
68− options: [{ value: "master", label: "master" }, { value: "slave", label: "stack" }, { value: "inherit", label: "inherit" }]
69− value: flick.v("master:new_status", "")
70− onPicked: x => HyprConf.set("master:new_status", x)
63+ label: "tiling layout"
64+ keywords: "tiling mode"
65+ options: [{ value: "master", label: "master" }, { value: "dwindle", label: "dwindle" }]
66+ value: flick.v("general:layout", "")
67+ onPicked: x => HyprConf.set("general:layout", x)
7168 }
72− Choice {
73− label: "master side"
74− keywords: "orientation"
75− options: [{ value: "left", label: "left" }, { value: "right", label: "right" }, { value: "top", label: "top" },
76− { value: "bottom", label: "bottom" }, { value: "center", label: "center" }]
77− value: flick.v("master:orientation", "")
78− onPicked: x => HyprConf.set("master:orientation", x)
69+ ColumnLayout {
70+ Layout.fillWidth: true
71+ visible: flick.v("general:layout", "") === "master"
72+ spacing: 4
73+ Knob { label: "master width"; keywords: "mfact ratio split size"; from: 0.1; to: 0.9; decimals: 2; value: flick.v("master:mfact", 0.55); onMoved: x => HyprConf.set("master:mfact", x) }
74+ Choice {
75+ label: "new window"
76+ keywords: "placement"
77+ options: [{ value: "master", label: "master" }, { value: "slave", label: "stack" }, { value: "inherit", label: "inherit" }]
78+ value: flick.v("master:new_status", "")
79+ onPicked: x => HyprConf.set("master:new_status", x)
80+ }
81+ Choice {
82+ label: "master side"
83+ keywords: "orientation"
84+ options: [{ value: "left", label: "left" }, { value: "right", label: "right" }, { value: "top", label: "top" },
85+ { value: "bottom", label: "bottom" }, { value: "center", label: "center" }]
86+ value: flick.v("master:orientation", "")
87+ onPicked: x => HyprConf.set("master:orientation", x)
88+ }
89+ Switch { label: "new windows on top of the stack"; checked: flick.v("master:new_on_top", false); onToggled: on => HyprConf.set("master:new_on_top", on) }
90+ }
91+ ColumnLayout {
92+ Layout.fillWidth: true
93+ visible: flick.v("general:layout", "") === "dwindle"
94+ spacing: 4
95+ Switch { label: "preserve split"; hint: "keep the split direction when windows move"; checked: flick.v("dwindle:preserve_split", false); onToggled: on => HyprConf.set("dwindle:preserve_split", on) }
96+ Switch { label: "smart split"; hint: "split by where the cursor is"; checked: flick.v("dwindle:smart_split", false); onToggled: on => HyprConf.set("dwindle:smart_split", on) }
7997 }
80− Switch { label: "new windows on top of the stack"; checked: flick.v("master:new_on_top", false); onToggled: on => HyprConf.set("master:new_on_top", on) }
81− }
82− ColumnLayout {
83− Layout.fillWidth: true
84− visible: flick.v("general:layout", "") === "dwindle"
85− spacing: 8
86− Switch { label: "preserve split"; hint: "keep the split direction when windows move"; checked: flick.v("dwindle:preserve_split", false); onToggled: on => HyprConf.set("dwindle:preserve_split", on) }
87− Switch { label: "smart split"; hint: "split by where the cursor is"; checked: flick.v("dwindle:smart_split", false); onToggled: on => HyprConf.set("dwindle:smart_split", on) }
8898 }
8999
90− SectionLabel { text: "fullscreen & focus" }
91− Choice {
92− label: "focus under fullscreen"
93− options: [{ value: 0, label: "ignore" }, { value: 1, label: "take over" }, { value: 2, label: "exit fullscreen" }]
94− value: flick.v("misc:on_focus_under_fullscreen", -1)
95− onPicked: x => HyprConf.set("misc:on_focus_under_fullscreen", x)
100+ Group {
101+ title: "fullscreen & focus"
102+ Choice {
103+ label: "focus under fullscreen"
104+ options: [{ value: 0, label: "ignore" }, { value: 1, label: "take over" }, { value: 2, label: "exit fullscreen" }]
105+ value: flick.v("misc:on_focus_under_fullscreen", -1)
106+ onPicked: x => HyprConf.set("misc:on_focus_under_fullscreen", x)
107+ }
108+ Switch { label: "closing keeps fullscreen"; hint: "the next window takes over when the fullscreen one closes (dwm monocle)"; checked: flick.v("misc:exit_window_retains_fullscreen", false); onToggled: on => HyprConf.set("misc:exit_window_retains_fullscreen", on) }
109+ Switch { label: "focus on activate"; hint: "apps asking for attention get focused"; checked: flick.v("misc:focus_on_activate", false); onToggled: on => HyprConf.set("misc:focus_on_activate", on) }
96110 }
97− Switch { label: "closing keeps fullscreen"; hint: "the next window takes over when the fullscreen one closes (dwm monocle)"; checked: flick.v("misc:exit_window_retains_fullscreen", false); onToggled: on => HyprConf.set("misc:exit_window_retains_fullscreen", on) }
98− Switch { label: "focus on activate"; hint: "apps asking for attention get focused"; checked: flick.v("misc:focus_on_activate", false); onToggled: on => HyprConf.set("misc:focus_on_activate", on) }
99111
100− Item { Layout.preferredHeight: 6 }
101112 RowLayout {
102113 Layout.fillWidth: true
103114 Text {
@@ -116,5 +127,6 @@ Flickable {
116127 onClicked: HyprConf.resetAll(["general:", "decoration:", "animations:", "master:", "dwindle:", "misc:"])
117128 }
118129 }
130+ Item { Layout.preferredHeight: 12 }
119131 }
120132 }
M .config/quickshell/aurora/SettingsApp.qml View
@@ -3,8 +3,9 @@ import QtQuick.Layouts
33 import Quickshell
44
55 // The settings app: a normal Hyprland window (class org.quickshell, title "aurora settings"), opened maximized by a
6−// window rule. The sections as a column of Doto words (the selected one large with the live dot),
7−// the page on the right under a caption, key hints at the bottom. One Loader, so only the shown section runs.
6+// window rule. Android-style: on the left the search pill, the sections (icon, name, caption; the shown one as a tonal
7+// pill) and key hints; on the right the page under its Doto title, its settings grouped on cards (`Group`).
8+// One Loader, so only the shown section runs.
89 // Tab / Shift+Tab, Up/Down (outside text fields) or Ctrl+J/K = sections, Esc closes.
910 // Ctrl+F (or the search button) searches every setting (SetIndex): the results replace the page, Enter / click opens
1011 // the section, scrolls to the row and flashes it.
@@ -32,6 +33,9 @@ FloatingWindow {
3233 for (var i = 0; i < sections.length; i++) if (sections[i].id === section) return i
3334 return 0
3435 }
36+ onIndexChanged: navList.positionViewAtIndex(index, ListView.Contain)
37+ function secName(s) { return s.word.charAt(0).toUpperCase() + s.word.slice(1) }
38+ function secSub(s) { return s.caption.replace(/^[^·]*·\s*/, "") }
3539 function cycle(d) { sectionRequested(sections[(index + d + sections.length) % sections.length].id) }
3640 // typing in a field: arrows belong to it
3741 function inText() { var a = win.activeFocusItem; return a !== null && a.cursorPosition !== undefined }
@@ -83,14 +87,18 @@ FloatingWindow {
8387 if (e.section !== section) sectionRequested(e.section)
8488 landTimer.restart()
8589 }
86− // the page's items in document order: past the group's SectionLabel, the first one with this label
90+ // the page's items in document order: past the group's Group card (or SectionLabel), the first one with this label
8791 function findRow(e) {
8892 var hit = null, header = null, past = e.group === ""
8993 function walk(it) {
9094 var kids = it.children
9195 for (var i = 0; i < kids.length && !hit; i++) {
9296 var c = kids[i]
93− if (e.group !== "" && c.actionClicked !== undefined && c.text === e.group) {
97+ if (e.group !== "" && c.flat !== undefined && c.gap !== undefined && c.title === e.group) {
98+ past = true
99+ header = c
100+ if (e.kind === "group") { hit = c; return }
101+ } else if (e.group !== "" && c.actionClicked !== undefined && c.text === e.group && c.parent !== header) {
94102 past = true
95103 header = c
96104 if (e.kind === "group") { hit = c; return }
@@ -169,167 +177,195 @@ FloatingWindow {
169177 anchors.fill: parent
170178 Keys.forwardTo: [root]
171179
172− // ---- sections ----
173− Column {
174− id: words
175− x: Math.round(Math.min(150, win.width * 0.07))
176− anchors.verticalCenter: parent.verticalCenter
177− spacing: 12
178− Repeater {
180+ // ---- the nav: search pill, sections, key hints ----
181+ Item {
182+ id: nav
183+ x: 40
184+ y: 40
185+ width: 340
186+ height: parent.height - 80
187+
188+ Rectangle {
189+ id: pill
190+ width: parent.width
191+ height: 52
192+ radius: height / 2
193+ color: Theme.s2
194+ border.width: 1
195+ border.color: searchInput.activeFocus ? Theme.fill : Theme.s2
196+ MouseArea {
197+ anchors.fill: parent
198+ cursorShape: Qt.IBeamCursor
199+ onClicked: win.startSearch()
200+ }
201+ Icon {
202+ id: lens
203+ x: 20
204+ anchors.verticalCenter: parent.verticalCenter
205+ text: Theme.iSearch
206+ color: Theme.grey
207+ font.pixelSize: 20
208+ }
209+ TextInput {
210+ id: searchInput
211+ anchors.left: lens.right
212+ anchors.leftMargin: 14
213+ anchors.right: parent.right
214+ anchors.rightMargin: 20
215+ anchors.verticalCenter: parent.verticalCenter
216+ font.family: Theme.ui
217+ font.pixelSize: 16
218+ color: Theme.fg
219+ selectionColor: Theme.fill
220+ selectedTextColor: Theme.fillFg
221+ clip: true
222+ cursorDelegate: Rectangle {
223+ width: 2
224+ color: Theme.live
225+ visible: searchInput.activeFocus
226+ Timer { running: searchInput.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
227+ }
228+ Text {
229+ visible: !searchInput.text
230+ text: "search settings"
231+ font: searchInput.font
232+ color: Theme.mute
233+ }
234+ onTextChanged: { win.query = text; win.pick = 0 }
235+ // a click in the pill searches; leaving it empty ends the search
236+ onActiveFocusChanged: {
237+ if (activeFocus) win.searching = true
238+ else if (win.searching && text.trim() === "") win.stopSearch()
239+ }
240+ // handled here, so the window's Tab / Ctrl+J/K / Esc don't fire
241+ Keys.onPressed: e => {
242+ var ctrl = e.modifiers & Qt.ControlModifier, n = win.results.length
243+ if (e.key === Qt.Key_Escape) win.stopSearch()
244+ else if (e.key === Qt.Key_Return || e.key === Qt.Key_Enter) { if (n) win.go(win.results[win.pick]) }
245+ else if (e.key === Qt.Key_Down || e.key === Qt.Key_Tab || (ctrl && e.key === Qt.Key_J)) { if (n) win.pick = (win.pick + 1) % n }
246+ else if (e.key === Qt.Key_Up || e.key === Qt.Key_Backtab || (ctrl && e.key === Qt.Key_K)) { if (n) win.pick = (win.pick + n - 1) % n }
247+ else if (ctrl && e.key === Qt.Key_F) selectAll()
248+ else return
249+ e.accepted = true
250+ }
251+ }
252+ }
253+
254+ ListView {
255+ id: navList
256+ anchors.top: pill.bottom
257+ anchors.topMargin: 20
258+ anchors.bottom: hints.top
259+ anchors.bottomMargin: 20
260+ width: parent.width
261+ clip: true
262+ spacing: 4
263+ boundsBehavior: Flickable.StopAtBounds
179264 model: win.sections
180− Row {
265+ delegate: Rectangle {
181266 id: w
182267 required property var modelData
183268 required property int index
184269 readonly property bool on: win.index === index
185− spacing: 20
270+ width: ListView.view.width
271+ height: 64
272+ radius: height / 2
273+ color: on ? Theme.container : wm.hovered ? Theme.s1 : "transparent"
274+ Behavior on color { enabled: Theme.motion; ColorAnimation { duration: Theme.t } }
186275 // while searching, sections without a match fade
187276 opacity: !win.searching || win.query.trim() === "" || win.hitSections[modelData.id] ? 1 : 0.3
188277 Behavior on opacity { enabled: Theme.motion; NumberAnimation { duration: Theme.t } }
189278 Rectangle {
279+ id: ic
280+ x: 10
190281 anchors.verticalCenter: parent.verticalCenter
191− visible: w.on
192− width: 20
193− height: 20
194− radius: 10
195− color: Theme.live
282+ width: 44
283+ height: 44
284+ radius: 22
285+ color: w.on ? Theme.fill : Theme.s2
286+ Icon {
287+ anchors.centerIn: parent
288+ text: w.modelData.icon
289+ font.pixelSize: 20
290+ color: w.on ? Theme.fillFg : Theme.fg
291+ }
196292 }
197− Num {
198− text: w.modelData.word.toUpperCase()
199− font.pixelSize: w.on ? 76 : 32
200− color: w.on ? Theme.fg : wm.hovered ? Theme.grey : Theme.mute
201− Behavior on font.pixelSize {
202− enabled: Theme.motion
203− NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide }
293+ Column {
294+ anchors.left: ic.right
295+ anchors.leftMargin: 14
296+ anchors.right: parent.right
297+ anchors.rightMargin: 16
298+ anchors.verticalCenter: parent.verticalCenter
299+ spacing: 3
300+ Text {
301+ width: parent.width
302+ elide: Text.ElideRight
303+ text: win.secName(w.modelData)
304+ color: w.on ? Theme.containerFg : Theme.fg
305+ font.family: Theme.ui
306+ font.pixelSize: 16
307+ font.weight: w.on ? Font.DemiBold : Font.Normal
308+ }
309+ Lb {
310+ width: parent.width
311+ elide: Text.ElideRight
312+ text: win.secSub(w.modelData)
313+ px: 10
314+ color: w.on ? Theme.fade(Theme.containerFg, 0.7) : Theme.grey
204315 }
205− Behavior on color { enabled: Theme.motion; ColorAnimation { duration: Theme.t } }
206316 }
207317 HoverHandler { id: wm; cursorShape: Qt.PointingHandCursor }
208318 TapHandler { onTapped: { win.stopSearch(); win.sectionRequested(w.modelData.id); root.forceActiveFocus() } }
209319 }
210320 }
321+
322+ Flow {
323+ id: hints
324+ anchors.bottom: parent.bottom
325+ x: 16
326+ width: parent.width - 16
327+ spacing: 22
328+ Repeater {
329+ model: win.searching ? ["↑↓ result", "↵ open", "esc back"] : ["ctrl+f search", "tab · ↑↓ section", "esc close"]
330+ Lb { required property string modelData; text: modelData; px: 10 }
331+ }
332+ }
211333 }
212334
213− // ---- the page ----
335+ // ---- the page: Doto title + caption, then the section's cards (or the search results) ----
214336 Item {
215337 id: pageBox
216− x: Math.max(words.x + 560, Math.round(win.width * 0.42))
217− y: 80
218− width: Math.min(900, win.width - x - 80)
219− height: parent.height - y - 110
338+ readonly property real room: parent.width - nav.x - nav.width - 112 // right of the nav
339+ width: Math.min(980, room)
340+ x: nav.x + nav.width + 64 + Math.max(0, Math.round((room - width) / 2))
341+ y: 40
342+ height: parent.height - 80
220343
221− // the caption + search button, or the search pill
222− Item {
344+ ColumnLayout {
223345 id: head
224346 width: parent.width
225− height: 52
226− Lb {
227− x: 18
228− anchors.verticalCenter: parent.verticalCenter
229− visible: !win.searching
230− text: win.sections[win.index].caption
231− }
232− IconBtn {
233− anchors.right: parent.right
234− anchors.verticalCenter: parent.verticalCenter
235− visible: !win.searching
236− icon: Theme.iSearch
237− label: "search"
238− onClicked: win.startSearch()
347+ spacing: 10
348+ Num {
349+ Layout.leftMargin: 14
350+ text: (win.searching ? "search" : win.sections[win.index].word).toUpperCase()
351+ font.pixelSize: 56
239352 }
240− Rectangle {
241− anchors.fill: parent
242− visible: win.searching
243− radius: height / 2
244− color: Theme.s2
245− Icon {
246− id: lens
247− x: 20
248− anchors.verticalCenter: parent.verticalCenter
249− text: Theme.iSearch
250− color: Theme.grey
251− font.pixelSize: 20
252− }
253− TextInput {
254− id: searchInput
255− anchors.left: lens.right
256− anchors.leftMargin: 14
257− anchors.right: found.left
258− anchors.rightMargin: 12
259− anchors.verticalCenter: parent.verticalCenter
260− font.family: Theme.ui
261− font.pixelSize: 18
262− color: Theme.fg
263− selectionColor: Theme.fill
264− selectedTextColor: Theme.fillFg
265− clip: true
266− cursorDelegate: Rectangle {
267− width: 2
268− color: Theme.live
269− visible: searchInput.activeFocus
270− Timer { running: searchInput.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
271− }
272− Text {
273− visible: !searchInput.text
274− text: "search settings"
275− font: searchInput.font
276− color: Theme.mute
277− }
278− onTextChanged: { win.query = text; win.pick = 0 }
279− // handled here, so the window's Tab / Ctrl+J/K / Esc don't fire
280− Keys.onPressed: e => {
281− var ctrl = e.modifiers & Qt.ControlModifier, n = win.results.length
282− if (e.key === Qt.Key_Escape) win.stopSearch()
283− else if (e.key === Qt.Key_Return || e.key === Qt.Key_Enter) { if (n) win.go(win.results[win.pick]) }
284− else if (e.key === Qt.Key_Down || e.key === Qt.Key_Tab || (ctrl && e.key === Qt.Key_J)) { if (n) win.pick = (win.pick + 1) % n }
285− else if (e.key === Qt.Key_Up || e.key === Qt.Key_Backtab || (ctrl && e.key === Qt.Key_K)) { if (n) win.pick = (win.pick + n - 1) % n }
286− else if (ctrl && e.key === Qt.Key_F) selectAll()
287− else return
288− e.accepted = true
289− }
290− }
291− Lb {
292− id: found
293− anchors.right: parent.right
294− anchors.rightMargin: 22
295− anchors.verticalCenter: parent.verticalCenter
296− text: win.results.length ? (win.results.length - 1) + " found" : ""
297− }
353+ Lb {
354+ Layout.leftMargin: 16
355+ text: !win.searching ? win.secSub(win.sections[win.index])
356+ : win.results.length ? (win.results.length - 1) + " found"
357+ : "type a setting, a word or a section"
298358 }
299359 }
300360
301361 Item {
362+ id: body
302363 anchors.top: head.bottom
303− anchors.topMargin: 16
364+ anchors.topMargin: 28
304365 anchors.left: parent.left
305366 anchors.right: parent.right
306367 anchors.bottom: parent.bottom
307368
308− // the found row's flash, behind the page (rows have no background of their own)
309− Item {
310− id: flashBox
311− anchors.fill: parent
312− clip: true
313− Rectangle {
314− id: flash
315− // re-mapped while the page scrolls or relayouts
316− readonly property rect r: {
317− var t = win.hlTarget
318− if (!t) return Qt.rect(0, 0, 0, 0)
319− void ((win.hlFlick ? win.hlFlick.contentY : 0) + t.y + t.width + t.height + flashBox.width)
320− return t.mapToItem(flashBox, 0, 0, t.width, t.height)
321− }
322− readonly property real pad: 4
323− visible: win.hlTarget !== null && !win.searching
324− opacity: 0
325− width: parent.width
326− y: r.y - pad
327− height: r.height + 2 * pad
328− radius: height / 2
329− color: Theme.fade(Theme.fill, 0.2)
330− }
331− }
332−
333369 Loader {
334370 id: loader
335371 anchors.fill: parent
@@ -352,99 +388,117 @@ FloatingWindow {
352388 }
353389 }
354390
355− // ---- search results ----
356− Lb {
357− x: 18
358− visible: win.searching && win.query.trim() === ""
359− text: "type a setting, a word or a section"
360− }
361− ListView {
362− id: list
363− anchors.fill: parent
364− visible: win.searching
365− clip: true
366− spacing: 2
367− boundsBehavior: Flickable.StopAtBounds
368− model: win.results
369− header: Item {
370− width: list.width
371− height: win.results.length === 1 ? 44 : 0
372− Lb {
373− x: 18
374− anchors.verticalCenter: parent.verticalCenter
375− visible: parent.height > 0
376− text: "no setting matches"
377− }
378− }
379− delegate: Rectangle {
380− id: r
381− required property var modelData
382− required property int index
383− readonly property bool on: win.pick === index
384− width: ListView.view.width
385− height: Math.max(56, txt.implicitHeight + 20)
386− radius: 28
387− color: on || rm.containsMouse ? Theme.s2 : "transparent"
388− Rectangle {
389− x: 20
390− anchors.verticalCenter: parent.verticalCenter
391− visible: r.on
392− width: 10
393− height: 10
394− radius: 5
395− color: Theme.live
391+ // ---- search results, on one card ----
392+ Rectangle {
393+ width: parent.width
394+ height: Math.min(parent.height, list.contentHeight + 16)
395+ visible: win.searching && win.results.length > 0
396+ radius: 36
397+ color: Theme.s1
398+ ListView {
399+ id: list
400+ anchors.fill: parent
401+ anchors.margins: 8
402+ clip: true
403+ spacing: 2
404+ boundsBehavior: Flickable.StopAtBounds
405+ model: win.results
406+ header: Item {
407+ width: list.width
408+ height: win.results.length === 1 ? 44 : 0
409+ Lb {
410+ x: 18
411+ anchors.verticalCenter: parent.verticalCenter
412+ visible: parent.height > 0
413+ text: "no setting matches"
414+ }
396415 }
397− ColumnLayout {
398− id: txt
399− anchors.left: parent.left
400− anchors.leftMargin: 44
401− anchors.right: parent.right
402− anchors.rightMargin: 18
403− anchors.verticalCenter: parent.verticalCenter
404− spacing: 3
405− Text {
406− Layout.fillWidth: true
407− text: r.modelData.label
408− color: Theme.fg
409− elide: Text.ElideRight
410− font.family: Theme.ui
411− font.pixelSize: 15
416+ delegate: Rectangle {
417+ id: r
418+ required property var modelData
419+ required property int index
420+ readonly property bool on: win.pick === index
421+ width: ListView.view.width
422+ height: Math.max(56, txt.implicitHeight + 20)
423+ radius: 28
424+ color: on || rm.containsMouse ? Theme.s2 : "transparent"
425+ Rectangle {
426+ x: 20
427+ anchors.verticalCenter: parent.verticalCenter
428+ visible: r.on
429+ width: 10
430+ height: 10
431+ radius: 5
432+ color: Theme.live
412433 }
413− RowLayout {
414− Layout.fillWidth: true
415− spacing: 12
416− Lb { text: win.crumb(r.modelData); px: 10 }
434+ ColumnLayout {
435+ id: txt
436+ anchors.left: parent.left
437+ anchors.leftMargin: 44
438+ anchors.right: parent.right
439+ anchors.rightMargin: 18
440+ anchors.verticalCenter: parent.verticalCenter
441+ spacing: 3
417442 Text {
418443 Layout.fillWidth: true
419− visible: r.modelData.hint !== ""
420− text: r.modelData.hint
421− color: Theme.fgDim
444+ text: r.modelData.label
445+ color: Theme.fg
422446 elide: Text.ElideRight
423447 font.family: Theme.ui
424− font.pixelSize: 13
448+ font.pixelSize: 15
449+ }
450+ RowLayout {
451+ Layout.fillWidth: true
452+ spacing: 12
453+ Lb { text: win.crumb(r.modelData); px: 10 }
454+ Text {
455+ Layout.fillWidth: true
456+ visible: r.modelData.hint !== ""
457+ text: r.modelData.hint
458+ color: Theme.fgDim
459+ elide: Text.ElideRight
460+ font.family: Theme.ui
461+ font.pixelSize: 13
462+ }
425463 }
426464 }
427− }
428− MouseArea {
429− id: rm
430− anchors.fill: parent
431− hoverEnabled: true
432− cursorShape: Qt.PointingHandCursor
433− onClicked: win.go(r.modelData)
465+ MouseArea {
466+ id: rm
467+ anchors.fill: parent
468+ hoverEnabled: true
469+ cursorShape: Qt.PointingHandCursor
470+ onClicked: win.go(r.modelData)
471+ }
434472 }
435473 }
436474 }
437− }
438− }
439475
440− Row {
441− x: words.x
442− anchors.bottom: parent.bottom
443− anchors.bottomMargin: 50
444− spacing: 34
445− Repeater {
446− model: win.searching ? ["↑↓ result", "↵ open", "esc back"] : ["ctrl+f search", "tab · ↑↓ section", "esc close"]
447− Lb { required property string modelData; text: modelData }
476+ // the found row's flash, over the page (the cards are opaque); as wide as the row's column
477+ Item {
478+ id: flashBox
479+ anchors.fill: parent
480+ clip: true
481+ Rectangle {
482+ id: flash
483+ // re-mapped while the page scrolls or relayouts
484+ readonly property rect r: {
485+ var t = win.hlTarget
486+ if (!t || !t.parent) return Qt.rect(0, 0, 0, 0)
487+ void ((win.hlFlick ? win.hlFlick.contentY : 0) + t.y + t.width + t.height + flashBox.width)
488+ var a = t.mapToItem(flashBox, 0, 0, t.width, t.height)
489+ var p = t.parent.mapToItem(flashBox, 0, 0, t.parent.width, t.parent.height)
490+ return Qt.rect(p.x, a.y, p.width, a.height)
491+ }
492+ visible: win.hlTarget !== null && !win.searching
493+ opacity: 0
494+ x: r.x
495+ y: r.y
496+ width: r.width
497+ height: r.height
498+ radius: Math.min(height / 2, 36)
499+ color: Theme.fade(Theme.fill, 0.2)
500+ }
501+ }
448502 }
449503 }
450504 }
@@ -453,9 +507,9 @@ FloatingWindow {
453507 Keys.onDownPressed: e => { if (win.inText()) { e.accepted = false; return } win.cycle(1) }
454508 }
455509
456− Component { id: netC; NetPage { active: true } }
457− Component { id: btC; BtPage { active: true } }
458− Component { id: soundC; AudioPage {} }
510+ Component { id: netC; NetPage { active: true; cards: true } }
511+ Component { id: btC; BtPage { active: true; cards: true } }
512+ Component { id: soundC; AudioPage { cards: true } }
459513 Component { id: appearanceC; SetAppearance { onOverlayRequested: name => win.overlayRequested(name) } }
460514 Component { id: shellC; SetShell {} }
461515 Component { id: windowsC; SetWindows {} }
M .config/quickshell/aurora/Theme.qml View
@@ -184,6 +184,7 @@ Singleton {
184184 readonly property string iCast: cp(0xF0118)
185185 readonly property string iCrop: cp(0xF019E)
186186 readonly property string iWindow: cp(0xF05B0)
187+ readonly property string iWindows: cp(0xF05B2) // window-restore (settings > windows)
187188 readonly property string iMonitor: cp(0xF0379)
188189 readonly property string iSave: cp(0xF0193)
189190 readonly property string iClipboard: cp(0xF0147)