Krolyxon
Commit 55505e6

Branches

Refs →

aurora55505e6

aurora: settings app window (network, bluetooth, sound, appearance, shell)

Replaces the panel's settings tab with a standalone window (SUPER+I,
`settings [section]` IPC, cog in the panel header).
Author
krolyxon
Date
2026-10-20 00:39 +0530
Parent
ced5016
Commit
55505e6dd0da46ebb1088cd20bd825b01f13b65b

+532 −142

16 files changed

  1. M .config/hypr/source/keybinds.lua +1 −0
  2. M .config/hypr/source/window_rules.lua +14 −0
  3. M .config/quickshell/aurora/BtPage.qml +3 −0
  4. A .config/quickshell/aurora/Choice.qml +36 −0
  5. M .config/quickshell/aurora/ControlPanel.qml +3 −3
  6. A .config/quickshell/aurora/Knob.qml +40 −0
  7. M .config/quickshell/aurora/NetPage.qml +3 −0
  8. M .config/quickshell/aurora/Notifs.qml +2 −2
  9. A .config/quickshell/aurora/SetAppearance.qml +111 −0
  10. A .config/quickshell/aurora/SetShell.qml +73 −0
  11. M .config/quickshell/aurora/Settings.qml +6 −1
  12. A .config/quickshell/aurora/SettingsApp.qml +152 −0
  13. D .config/quickshell/aurora/SettingsPage.qml +0 −134
  14. A .config/quickshell/aurora/Switch.qml +65 −0
  15. M .config/quickshell/aurora/Theme.qml +2 −1
  16. M .config/quickshell/aurora/shell.qml +21 −1
M .config/hypr/source/keybinds.lua View
@@ -39,6 +39,7 @@ hl.bind(mainMod .. " + R", hl.dsp.exec_cmd(menu))
3939 hl.bind(mainMod .. " + Tab", hl.dsp.window.cycle_next({ next = false }), { repeating = true })
4040 hl.bind(mainMod .. " + grave", hl.dsp.exec_cmd("aurora-chars"))
4141 hl.bind(mainMod .. " + escape", hl.dsp.exec_cmd("qs -c aurora ipc call shell power"))
42+hl.bind(mainMod .. " + I", hl.dsp.exec_cmd("qs -c aurora ipc call shell settings ''"), { description = "aurora settings" })
4243
4344 -- Applications
4445 hl.bind(mainMod .. " + W", hl.dsp.exec_cmd(browser))
M .config/hypr/source/window_rules.lua View
@@ -36,3 +36,17 @@ hl.window_rule({
3636 move = "20 monitor_h-120",
3737 float = true,
3838 })
39+
40+-- aurora's settings app: a normal window, floating and centred by default
41+hl.window_rule({
42+ name = "aurora-settings",
43+
44+ match = {
45+ class = "^org.quickshell$",
46+ title = "^aurora settings$",
47+ },
48+
49+ float = true,
50+ size = "960 680",
51+ center = true,
52+})
M .config/quickshell/aurora/BtPage.qml View
@@ -12,6 +12,9 @@ ColumnLayout {
1212 onActiveChanged: {
1313 if (adapter) adapter.discovering = active && adapter.enabled
1414 }
15+ // the settings app creates this page already active and destroys it when leaving the section
16+ Component.onCompleted: if (active && adapter && adapter.enabled) adapter.discovering = true
17+ Component.onDestruction: if (active && adapter) adapter.discovering = false
1518
1619 function paired(d) { return d.bonded === true || d.paired === true }
1720
A .config/quickshell/aurora/Choice.qml View
@@ -0,0 +1,36 @@
1+import QtQuick
2+import QtQuick.Layouts
3+
4+// label + segmented buttons. options: [{value, label}] (settings app)
5+RowLayout {
6+ id: c
7+ property string label: ""
8+ property var options: []
9+ property var value
10+ signal picked(var v)
11+ Layout.fillWidth: true
12+ spacing: 8
13+ Text {
14+ Layout.preferredWidth: 150
15+ text: c.label
16+ color: Theme.fg
17+ elide: Text.ElideRight
18+ font.family: Theme.font
19+ font.pixelSize: Theme.fontSize
20+ }
21+ RowLayout {
22+ Layout.fillWidth: true
23+ spacing: 2
24+ Repeater {
25+ model: c.options
26+ delegate: IconBtn {
27+ required property var modelData
28+ Layout.fillWidth: true
29+ implicitHeight: 26
30+ label: modelData.label
31+ active: c.value === modelData.value
32+ onClicked: c.picked(modelData.value)
33+ }
34+ }
35+ }
36+}
M .config/quickshell/aurora/ControlPanel.qml View
@@ -11,6 +11,7 @@ PanelWindow {
1111 signal tabRequested(int index)
1212 signal closeRequested()
1313 signal overlayRequested(string name)
14+ signal settingsRequested()
1415
1516 visible: opened
1617 anchors { top: true; right: true }
@@ -36,8 +37,7 @@ PanelWindow {
3637 { icon: Theme.iWifi, name: "net" },
3738 { icon: Theme.iBt, name: "bt" },
3839 { icon: Theme.iChip, name: "system" },
39− { icon: Theme.iBell, name: "notifs" },
40− { icon: Theme.iCog, name: "settings" }
40+ { icon: Theme.iBell, name: "notifs" }
4141 ]
4242
4343 Rectangle {
@@ -98,6 +98,7 @@ PanelWindow {
9898 }
9999 }
100100 Item { Layout.fillWidth: true }
101+ IconBtn { icon: Theme.iCog; implicitHeight: 28; onClicked: win.settingsRequested() }
101102 IconBtn { icon: Theme.iClose; implicitHeight: 28; onClicked: win.closeRequested() }
102103 }
103104
@@ -117,7 +118,6 @@ PanelWindow {
117118 BtPage { active: win.opened && win.tab === 3 }
118119 SysPage {}
119120 NotifPage {}
120− SettingsPage {}
121121 }
122122 }
123123 }
A .config/quickshell/aurora/Knob.qml View
@@ -0,0 +1,40 @@
1+import QtQuick
2+import QtQuick.Layouts
3+
4+// label + slider + value, for an integer or real setting (settings app)
5+RowLayout {
6+ id: k
7+ property string label: ""
8+ property real value: 0
9+ property real from: 0
10+ property real to: 1
11+ property int decimals: 0
12+ property string unit: ""
13+ signal moved(real v)
14+ Layout.fillWidth: true
15+ spacing: 8
16+ Text {
17+ Layout.preferredWidth: 150
18+ text: k.label
19+ color: Theme.fg
20+ elide: Text.ElideRight
21+ font.family: Theme.font
22+ font.pixelSize: Theme.fontSize
23+ }
24+ BarSlider {
25+ Layout.fillWidth: true
26+ from: k.from
27+ to: k.to
28+ step: 1 / Math.max(1, (k.to - k.from) * (k.decimals ? 100 : 1))
29+ value: k.value
30+ onMoved: v => k.moved(k.decimals ? Math.round(v * 100) / 100 : Math.round(v))
31+ }
32+ Text {
33+ Layout.preferredWidth: 56
34+ horizontalAlignment: Text.AlignRight
35+ text: (k.decimals ? k.value.toFixed(k.decimals) : String(Math.round(k.value))) + k.unit
36+ color: Theme.fg
37+ font.family: Theme.font
38+ font.pixelSize: Theme.fontSize
39+ }
40+}
M .config/quickshell/aurora/NetPage.qml View
@@ -5,6 +5,9 @@ ColumnLayout {
55 id: page
66 property bool active: false
77 onActiveChanged: Net.active = active
8+ // the settings app creates this page already active and destroys it when leaving the section
9+ Component.onCompleted: if (active) Net.active = true
10+ Component.onDestruction: if (active) Net.active = false
811 spacing: 8
912
1013 // wired / usb tethering (read-only status, plus a disconnect button)
M .config/quickshell/aurora/Notifs.qml View
@@ -10,9 +10,9 @@ Singleton {
1010 property bool dnd: false
1111
1212 // sound played on every toast (silenced by do-not-disturb, except critical ones)
13− property bool soundEnabled: true
13+ property bool soundEnabled: Settings.notifSound
1414 property string soundFile: Quickshell.env("HOME") + "/.config/quickshell/aurora/sounds/Blow.aiff"
15− property real soundVolume: 0.7
15+ property real soundVolume: Settings.notifVolume
1616 property var popups: [] // [{n, until}]
1717 readonly property var model: server.trackedNotifications
1818 readonly property int count: server.trackedNotifications.values.length
A .config/quickshell/aurora/SetAppearance.qml View
@@ -0,0 +1,111 @@
1+import QtQuick
2+import QtQuick.Layouts
3+
4+// Settings app > Appearance: wallpaper, accent, sizes and transparency (all in Settings, applied live).
5+Flickable {
6+ id: flick
7+ signal overlayRequested(string name)
8+ clip: true
9+ contentWidth: width
10+ contentHeight: col.implicitHeight
11+ boundsBehavior: Flickable.StopAtBounds
12+
13+ Component.onCompleted: Wall.refresh()
14+
15+ ColumnLayout {
16+ id: col
17+ width: flick.width
18+ spacing: 10
19+
20+ SectionLabel { text: "wallpaper" }
21+ Text {
22+ Layout.fillWidth: true
23+ text: Wall.current !== "" ? Wall.current.replace(Wall.home, "~") : "-"
24+ elide: Text.ElideLeft
25+ color: Theme.dim
26+ font.family: Theme.font
27+ font.pixelSize: Theme.fontSize - 1
28+ }
29+ RowLayout {
30+ Layout.fillWidth: true
31+ spacing: 6
32+ IconBtn {
33+ Layout.fillWidth: true
34+ icon: Theme.iImage
35+ label: "open picker"
36+ onClicked: flick.overlayRequested("wall")
37+ }
38+ IconBtn {
39+ Layout.fillWidth: true
40+ icon: Theme.iShuffle
41+ label: "random from folder"
42+ onClicked: Wall.random()
43+ }
44+ }
45+ Text {
46+ text: "random picks from"
47+ color: Theme.fg
48+ font.family: Theme.font
49+ font.pixelSize: Theme.fontSize
50+ }
51+ Flow {
52+ Layout.fillWidth: true
53+ spacing: 4
54+ Repeater {
55+ model: [""].concat(Wall.folders)
56+ delegate: IconBtn {
57+ required property string modelData
58+ implicitHeight: 26
59+ label: modelData === "" ? "all" : modelData
60+ active: Settings.wallFolder === modelData
61+ onClicked: Settings.wallFolder = modelData
62+ }
63+ }
64+ }
65+
66+ SectionLabel { text: "accent" }
67+ GridLayout {
68+ Layout.fillWidth: true
69+ columns: 17
70+ columnSpacing: 4
71+ rowSpacing: 4
72+ Repeater {
73+ model: 17 // "auto" + color0..color15
74+ delegate: Rectangle {
75+ id: sw
76+ required property int index
77+ readonly property int ci: index - 1 // -1 = auto
78+ readonly property bool current: Settings.accentIndex === ci
79+ Layout.fillWidth: true
80+ Layout.preferredHeight: 28
81+ color: ci < 0 ? Theme.surface : Theme.pal[ci]
82+ border.width: current ? 2 : 1
83+ border.color: current ? Theme.fg : Theme.line
84+ Text {
85+ anchors.centerIn: parent
86+ visible: sw.ci < 0 || sw.current
87+ text: sw.ci < 0 ? "auto" : Theme.iCheck
88+ color: sw.ci < 0 ? Theme.fg : Theme.bg
89+ font.family: Theme.font
90+ font.pixelSize: Theme.fontSize - 2
91+ font.bold: true
92+ }
93+ MouseArea {
94+ anchors.fill: parent
95+ cursorShape: Qt.PointingHandCursor
96+ onClicked: Settings.accentIndex = sw.ci
97+ }
98+ }
99+ }
100+ }
101+
102+ SectionLabel { text: "sizes" }
103+ Knob { label: "font size"; from: 9; to: 18; value: Settings.fontSize; onMoved: v => Settings.fontSize = v }
104+ Knob { label: "workspace icons"; from: 8; to: 18; value: Settings.wsFontSize; onMoved: v => Settings.wsFontSize = v }
105+ Knob { label: "bar height"; from: 16; to: 36; value: Settings.barHeight; onMoved: v => Settings.barHeight = v }
106+
107+ SectionLabel { text: "transparency" }
108+ Knob { label: "bar opacity"; from: 0.3; to: 1; decimals: 2; value: Settings.barAlpha; onMoved: v => Settings.barAlpha = v }
109+ Knob { label: "panel opacity"; from: 0.3; to: 1; decimals: 2; value: Settings.panelAlpha; onMoved: v => Settings.panelAlpha = v }
110+ }
111+}
A .config/quickshell/aurora/SetShell.qml View
@@ -0,0 +1,73 @@
1+import QtQuick
2+import QtQuick.Layouts
3+
4+// Settings app > Shell: desktop widgets, dock, OSD, notifications.
5+Flickable {
6+ id: flick
7+ clip: true
8+ contentWidth: width
9+ contentHeight: col.implicitHeight
10+ boundsBehavior: Flickable.StopAtBounds
11+
12+ ColumnLayout {
13+ id: col
14+ width: flick.width
15+ spacing: 8
16+
17+ SectionLabel { text: "desktop" }
18+ Switch { label: "clock"; hint: "big clock + stats on the wallpaper"; checked: Settings.desktopClock; onToggled: on => Settings.desktopClock = on }
19+ Switch { label: "visualizer"; hint: "cava bars while music plays"; checked: Settings.desktopViz; onToggled: on => Settings.desktopViz = on }
20+ Knob { label: "visualizer bars"; from: 16; to: 128; value: Settings.vizBars; onMoved: v => Settings.vizBars = v }
21+
22+ SectionLabel { text: "dock" }
23+ Switch { label: "dock"; 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.dim
27+ font.family: Theme.font
28+ font.pixelSize: Theme.fontSize - 1
29+ }
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)
42+ }
43+ }
44+ }
45+
46+ SectionLabel { text: "on-screen display" }
47+ Knob { label: "osd timeout"; unit: "ms"; from: 500; to: 4000; value: Settings.osdTimeout; onMoved: v => Settings.osdTimeout = Math.round(v / 100) * 100 }
48+
49+ SectionLabel { text: "notifications" }
50+ Switch { label: "do not disturb"; hint: "hide popups and mute the sound (critical ones still show)"; checked: Notifs.dnd; onToggled: on => Notifs.dnd = on }
51+ Switch { label: "sound"; checked: Settings.notifSound; onToggled: on => Settings.notifSound = on }
52+ RowLayout {
53+ Layout.fillWidth: true
54+ spacing: 8
55+ Knob { label: "sound volume"; from: 0; to: 1; decimals: 2; value: Settings.notifVolume; onMoved: v => Settings.notifVolume = v }
56+ IconBtn { icon: Theme.iPlay; implicitHeight: 26; onClicked: Notifs.playSound() }
57+ }
58+
59+ Item { Layout.preferredHeight: 6 }
60+ RowLayout {
61+ Layout.fillWidth: true
62+ Text {
63+ Layout.fillWidth: true
64+ text: "~/.config/aurora/settings.json"
65+ elide: Text.ElideLeft
66+ color: Theme.dim
67+ font.family: Theme.font
68+ font.pixelSize: Theme.fontSize - 2
69+ }
70+ IconBtn { icon: Theme.iRefresh; label: "reset shell settings"; onClicked: Settings.reset() }
71+ }
72+ }
73+}
M .config/quickshell/aurora/Settings.qml View
@@ -13,7 +13,8 @@ Singleton {
1313 readonly property var defaults: ({
1414 fontSize: 12, wsFontSize: 11, barHeight: 22, barAlpha: 1.0, panelAlpha: 0.88, accentIndex: 14,
1515 osdTimeout: 1500, desktopClock: true, desktopViz: true, vizBars: 64,
16− dock: true, dockPinned: ["foot", "librewolf", "pcmanfm", "obsidian"], wallFolder: "dusky-dark"
16+ dock: true, dockPinned: ["foot", "librewolf", "pcmanfm", "obsidian"], wallFolder: "dusky-dark",
17+ notifSound: true, notifVolume: 0.7
1718 })
1819
1920 property alias fontSize: a.fontSize
@@ -29,6 +30,8 @@ Singleton {
2930 property alias dock: a.dock
3031 property alias dockPinned: a.dockPinned
3132 property alias wallFolder: a.wallFolder
33+ property alias notifSound: a.notifSound
34+ property alias notifVolume: a.notifVolume
3235
3336 function reset() {
3437 for (var k in defaults) a[k] = defaults[k]
@@ -66,6 +69,8 @@ Singleton {
6669 property bool dock: true
6770 property list<string> dockPinned: ["foot", "librewolf", "pcmanfm", "obsidian"]
6871 property string wallFolder: "dusky-dark"
72+ property bool notifSound: true
73+ property real notifVolume: 0.7
6974 }
7075 }
7176 // first run: create the file once the directory exists
A .config/quickshell/aurora/SettingsApp.qml View
@@ -0,0 +1,152 @@
1+import QtQuick
2+import QtQuick.Layouts
3+import Quickshell
4+
5+// The settings app: a normal Hyprland window (class org.quickshell, title "aurora settings"; floated + sized by a
6+// window rule). Sidebar of sections, one Loader so only the shown section runs (net scans, bt discovery).
7+// Esc closes, Ctrl+Tab / Ctrl+Shift+Tab (or Ctrl+J/K) cycle sections. Opened with the `settings [section]` IPC.
8+FloatingWindow {
9+ id: win
10+ property bool opened: false
11+ property string section: "network"
12+ signal closeRequested()
13+ signal sectionRequested(string name)
14+ signal overlayRequested(string name)
15+
16+ visible: opened
17+ title: "aurora settings"
18+ implicitWidth: 960
19+ implicitHeight: 680
20+ color: Theme.bg
21+ // closed by the compositor (killactive): keep shell state in sync
22+ onVisibleChanged: if (!visible && opened) closeRequested()
23+
24+ readonly property var sections: [
25+ { id: "network", icon: Theme.iWifi, name: "network", group: "connections" },
26+ { id: "bluetooth", icon: Theme.iBt, name: "bluetooth", group: "connections" },
27+ { id: "sound", icon: Theme.iVolHigh, name: "sound", group: "connections" },
28+ { id: "appearance", icon: Theme.iPalette, name: "appearance", group: "look" },
29+ { id: "shell", icon: Theme.iDock, name: "shell", group: "look" }
30+ ]
31+ readonly property int index: {
32+ for (var i = 0; i < sections.length; i++) if (sections[i].id === section) return i
33+ return 0
34+ }
35+ function cycle(d) { sectionRequested(sections[(index + d + sections.length) % sections.length].id) }
36+
37+ Item {
38+ anchors.fill: parent
39+ focus: true
40+ Keys.onPressed: e => {
41+ var ctrl = e.modifiers & Qt.ControlModifier
42+ if (e.key === Qt.Key_Escape) { win.closeRequested(); e.accepted = true }
43+ else if (ctrl && (e.key === Qt.Key_Tab || e.key === Qt.Key_J)) { win.cycle(1); e.accepted = true }
44+ else if (ctrl && (e.key === Qt.Key_Backtab || e.key === Qt.Key_K)) { win.cycle(-1); e.accepted = true }
45+ }
46+
47+ RowLayout {
48+ anchors.fill: parent
49+ spacing: 0
50+
51+ // sidebar
52+ Rectangle {
53+ Layout.fillHeight: true
54+ Layout.preferredWidth: 200
55+ color: Theme.surface
56+ ColumnLayout {
57+ anchors.fill: parent
58+ anchors.margins: 10
59+ spacing: 2
60+ Text {
61+ Layout.bottomMargin: 8
62+ text: Theme.iCog + " aurora settings"
63+ color: Theme.accent
64+ font.family: Theme.font
65+ font.pixelSize: Theme.fontSize + 1
66+ font.bold: true
67+ }
68+ Repeater {
69+ model: win.sections
70+ delegate: ColumnLayout {
71+ id: entry
72+ required property var modelData
73+ required property int index
74+ readonly property bool current: win.section === modelData.id
75+ readonly property bool firstOfGroup: index === 0 || win.sections[index - 1].group !== modelData.group
76+ Layout.fillWidth: true
77+ spacing: 2
78+ Text {
79+ visible: entry.firstOfGroup
80+ Layout.topMargin: entry.index === 0 ? 0 : 8
81+ text: entry.modelData.group.toUpperCase()
82+ color: Theme.dim
83+ font.family: Theme.font
84+ font.pixelSize: Theme.fontSize - 3
85+ font.letterSpacing: 1.5
86+ }
87+ Rectangle {
88+ Layout.fillWidth: true
89+ implicitHeight: 30
90+ color: entry.current ? Theme.accent : (em.containsMouse ? Theme.surfaceHi : "transparent")
91+ Text {
92+ anchors.verticalCenter: parent.verticalCenter
93+ x: 10
94+ text: entry.modelData.icon + " " + entry.modelData.name
95+ color: entry.current ? Theme.bg : Theme.fg
96+ font.family: Theme.font
97+ font.pixelSize: Theme.fontSize
98+ font.bold: entry.current
99+ }
100+ MouseArea {
101+ id: em
102+ anchors.fill: parent
103+ hoverEnabled: true
104+ cursorShape: Qt.PointingHandCursor
105+ onClicked: win.sectionRequested(entry.modelData.id)
106+ }
107+ }
108+ }
109+ }
110+ Item { Layout.fillHeight: true }
111+ }
112+ }
113+ Rectangle { Layout.fillHeight: true; Layout.preferredWidth: 1; color: Theme.line }
114+
115+ // content
116+ ColumnLayout {
117+ Layout.fillWidth: true
118+ Layout.fillHeight: true
119+ Layout.margins: 16
120+ spacing: 12
121+ Text {
122+ text: win.sections[win.index].name.toUpperCase()
123+ color: Theme.fg
124+ font.family: Theme.font
125+ font.pixelSize: Theme.fontSize + 4
126+ font.bold: true
127+ font.letterSpacing: 2
128+ }
129+ Loader {
130+ Layout.fillWidth: true
131+ Layout.fillHeight: true
132+ active: win.opened
133+ sourceComponent: {
134+ switch (win.section) {
135+ case "bluetooth": return btC
136+ case "sound": return soundC
137+ case "appearance": return appearanceC
138+ case "shell": return shellC
139+ default: return netC
140+ }
141+ }
142+ }
143+ }
144+ }
145+ }
146+
147+ Component { id: netC; NetPage { active: true } }
148+ Component { id: btC; BtPage { active: true } }
149+ Component { id: soundC; AudioPage {} }
150+ Component { id: appearanceC; SetAppearance { onOverlayRequested: name => win.overlayRequested(name) } }
151+ Component { id: shellC; SetShell {} }
152+}
D .config/quickshell/aurora/SettingsPage.qml View
@@ -1,134 +0,0 @@
1−import QtQuick
2−import QtQuick.Layouts
3−
4−// Theme / shell knobs. Everything writes straight to Settings (persisted, applied live).
5−ColumnLayout {
6− id: page
7− spacing: 10
8−
9− // label + slider + value, for an integer or real setting
10− component Knob: RowLayout {
11− id: k
12− property string label: ""
13− property real value: 0
14− property real from: 0
15− property real to: 1
16− property int decimals: 0
17− signal moved(real v)
18− Layout.fillWidth: true
19− spacing: 8
20− Text {
21− Layout.preferredWidth: 96
22− text: k.label
23− color: Theme.fg
24− font.family: Theme.font
25− font.pixelSize: Theme.fontSize
26− }
27− BarSlider {
28− Layout.fillWidth: true
29− from: k.from
30− to: k.to
31− step: 1 / Math.max(1, (k.to - k.from) * (k.decimals ? 100 : 1))
32− value: k.value
33− onMoved: v => k.moved(k.decimals ? Math.round(v * 100) / 100 : Math.round(v))
34− }
35− Text {
36− Layout.preferredWidth: 40
37− horizontalAlignment: Text.AlignRight
38− text: k.decimals ? k.value.toFixed(2) : String(k.value)
39− color: Theme.fg
40− font.family: Theme.font
41− font.pixelSize: Theme.fontSize
42− }
43− }
44−
45− SectionLabel { text: "appearance" }
46−
47− Knob { label: "font size"; from: 9; to: 18; value: Settings.fontSize; onMoved: v => Settings.fontSize = v }
48− Knob { label: "ws icons"; from: 8; to: 18; value: Settings.wsFontSize; onMoved: v => Settings.wsFontSize = v }
49− Knob { label: "bar height"; from: 18; to: 36; value: Settings.barHeight; onMoved: v => Settings.barHeight = v }
50− Knob { label: "bar alpha"; from: 0.3; to: 1; decimals: 2; value: Settings.barAlpha; onMoved: v => Settings.barAlpha = v }
51− Knob { label: "panel alpha"; from: 0.3; to: 1; decimals: 2; value: Settings.panelAlpha; onMoved: v => Settings.panelAlpha = v }
52−
53− SectionLabel { text: "accent" }
54−
55− GridLayout {
56− Layout.fillWidth: true
57− columns: 9
58− columnSpacing: 4
59− rowSpacing: 4
60− Repeater {
61− model: 17 // "auto" + color0..color15
62− delegate: Rectangle {
63− id: sw
64− required property int index
65− readonly property int ci: index - 1 // -1 = auto
66− readonly property bool current: Settings.accentIndex === ci
67− Layout.fillWidth: true
68− Layout.preferredHeight: 26
69− color: ci < 0 ? Theme.surface : Theme.pal[ci]
70− border.width: current ? 2 : 1
71− border.color: current ? Theme.fg : Theme.line
72− Text {
73− anchors.centerIn: parent
74− visible: sw.ci < 0 || sw.current
75− text: sw.ci < 0 ? "auto" : Theme.iCheck
76− color: sw.ci < 0 ? Theme.fg : Theme.bg
77− font.family: Theme.font
78− font.pixelSize: Theme.fontSize - 2
79− font.bold: true
80− }
81− MouseArea {
82− anchors.fill: parent
83− cursorShape: Qt.PointingHandCursor
84− onClicked: Settings.accentIndex = sw.ci
85− }
86− }
87− }
88− }
89−
90− SectionLabel { text: "desktop" }
91−
92− RowLayout {
93− Layout.fillWidth: true
94− spacing: 6
95− IconBtn {
96− Layout.fillWidth: true
97− icon: Theme.iClock
98− label: "clock"
99− active: Settings.desktopClock
100− onClicked: Settings.desktopClock = !Settings.desktopClock
101− }
102− IconBtn {
103− Layout.fillWidth: true
104− icon: Theme.iMusic
105− label: "visualizer"
106− active: Settings.desktopViz
107− onClicked: Settings.desktopViz = !Settings.desktopViz
108− }
109− IconBtn {
110− Layout.fillWidth: true
111− icon: Theme.iDock
112− label: "dock"
113− active: Settings.dock
114− onClicked: Settings.dock = !Settings.dock
115− }
116− }
117− Knob { label: "viz bars"; from: 16; to: 128; value: Settings.vizBars; onMoved: v => Settings.vizBars = v }
118− Knob { label: "osd ms"; from: 500; to: 4000; value: Settings.osdTimeout; onMoved: v => Settings.osdTimeout = Math.round(v / 100) * 100 }
119−
120− Item { Layout.fillHeight: true }
121−
122− RowLayout {
123− Layout.fillWidth: true
124− Text {
125− Layout.fillWidth: true
126− text: "~/.config/aurora/settings.json"
127− elide: Text.ElideLeft
128− color: Theme.dim
129− font.family: Theme.font
130− font.pixelSize: Theme.fontSize - 2
131− }
132− IconBtn { icon: Theme.iRefresh; label: "reset"; onClicked: Settings.reset() }
133− }
134−}
A .config/quickshell/aurora/Switch.qml View
@@ -0,0 +1,65 @@
1+import QtQuick
2+import QtQuick.Layouts
3+
4+// label (+ optional hint) and an on/off box; the whole row is clickable (settings app)
5+Rectangle {
6+ id: s
7+ property string label: ""
8+ property string hint: ""
9+ property bool checked: false
10+ signal toggled(bool on)
11+
12+ Layout.fillWidth: true
13+ implicitHeight: Math.max(30, txt.implicitHeight + 10)
14+ color: m.containsMouse ? Theme.surface : "transparent"
15+
16+ ColumnLayout {
17+ id: txt
18+ anchors.left: parent.left
19+ anchors.right: box.left
20+ anchors.rightMargin: 10
21+ anchors.verticalCenter: parent.verticalCenter
22+ spacing: 0
23+ Text {
24+ Layout.fillWidth: true
25+ text: s.label
26+ color: Theme.fg
27+ elide: Text.ElideRight
28+ font.family: Theme.font
29+ font.pixelSize: Theme.fontSize
30+ }
31+ Text {
32+ Layout.fillWidth: true
33+ visible: s.hint !== ""
34+ text: s.hint
35+ color: Theme.dim
36+ wrapMode: Text.WordWrap
37+ font.family: Theme.font
38+ font.pixelSize: Theme.fontSize - 2
39+ }
40+ }
41+ Rectangle { // the switch: square track, knob left (off) / right (on)
42+ id: box
43+ anchors.right: parent.right
44+ anchors.verticalCenter: parent.verticalCenter
45+ width: 36
46+ height: 18
47+ color: s.checked ? Theme.accent : Theme.surfaceHi
48+ border.width: 1
49+ border.color: s.checked ? Theme.accent : Theme.line
50+ Rectangle {
51+ width: 12
52+ height: 12
53+ anchors.verticalCenter: parent.verticalCenter
54+ x: s.checked ? parent.width - width - 3 : 3
55+ color: s.checked ? Theme.bg : Theme.dim
56+ }
57+ }
58+ MouseArea {
59+ id: m
60+ anchors.fill: parent
61+ hoverEnabled: true
62+ cursorShape: Qt.PointingHandCursor
63+ onClicked: s.toggled(!s.checked)
64+ }
65+}
M .config/quickshell/aurora/Theme.qml View
@@ -7,7 +7,7 @@ import Quickshell.Io
77 Singleton {
88 id: root
99
10− // ---- knobs (persisted in ~/.config/aurora/settings.json, edited in the Settings tab) ----
10+ // ---- knobs (persisted in ~/.config/aurora/settings.json, edited in the settings app) ----
1111 property string font: "JetBrainsMono Nerd Font" // any Nerd Font mono
1212 readonly property int fontSize: Settings.fontSize
1313 readonly property int wsFontSize: Settings.wsFontSize // workspace icons only; everything else uses fontSize
@@ -164,6 +164,7 @@ Singleton {
164164 readonly property string iConsole: cp(0xF018D)
165165 readonly property string iShell: cp(0xF07B7)
166166 readonly property string iCalc: cp(0xF00EC)
167+ readonly property string iPalette: cp(0xF03D8)
167168
168169 // workspace icons: the exact Font Awesome glyphs from your waybar config.
169170 // 1 browser, 2 code, 3 book, 4 cog, 5 chat, 6 file, 7 folder, 8 terminal, 9 flask, 10 image
M .config/quickshell/aurora/shell.qml View
@@ -19,7 +19,10 @@ ShellRoot {
1919 property string overlay: ""
2020 property bool barShown: true // `bar` IPC (SUPER+B); not persisted, a restart shows it again
2121 property var overlayScreen: focusedScreen()
22− readonly property var tabNames: ["home", "audio", "net", "bt", "sys", "notif", "settings"]
22+ readonly property var tabNames: ["home", "audio", "net", "bt", "sys", "notif"]
23+ // settings app (a normal window): open state + shown section
24+ property bool settingsOpen: false
25+ property string settingsSection: "network"
2326
2427 // screen of the monitor that currently has focus (falls back to the first screen)
2528 function focusedScreen() {
@@ -51,7 +54,14 @@ ShellRoot {
5154 overlayScreen = scr ? scr : focusedScreen()
5255 overlay = name
5356 }
57+ // section "" = toggle the window on its last section; a name opens (or switches) to it
58+ function settings(section) {
59+ if (section === "" || section === undefined) { settingsOpen = !settingsOpen; return }
60+ settingsSection = section
61+ settingsOpen = true
62+ }
5463 function openTab(name) {
64+ if (name === "settings") { settings("appearance"); return } // old panel tab lives in the app now
5565 var i = tabNames.indexOf(name)
5666 if (i >= 0) tab = i
5767 panelScreen = focusedScreen()
@@ -99,6 +109,7 @@ ShellRoot {
99109 onTabRequested: i => root.tab = i
100110 onCloseRequested: root.close()
101111 onOverlayRequested: name => { root.close(); root.toggleOverlay(name) }
112+ onSettingsRequested: { root.close(); root.settingsOpen = true }
102113 }
103114
104115 Overview {
@@ -145,6 +156,14 @@ ShellRoot {
145156 }
146157 }
147158
159+ SettingsApp {
160+ opened: root.settingsOpen
161+ section: root.settingsSection
162+ onCloseRequested: root.settingsOpen = false
163+ onSectionRequested: name => root.settingsSection = name
164+ onOverlayRequested: name => root.toggleOverlay(name)
165+ }
166+
148167 NotifPopups { screen: root.focusedScr; suppressed: root.opened }
149168
150169 Osd { id: osd; screen: root.focusedScr; suppressed: root.opened }
@@ -180,6 +199,7 @@ ShellRoot {
180199 function wallrandom(): void { Wall.random() }
181200 function wallset(path: string): void { Wall.apply(path) } // absolute path (setwall resolves it)
182201 function rec(mode: string): void { root.close(); Rec.toggle(mode === "region" ? "region" : "screen") } // screen (default) | region
202+ function settings(section: string): void { root.settings(section) } // "" = toggle
183203 function caffeine(): void { Toggles.caffeine = !Toggles.caffeine }
184204 function bright(dir: string): void { Bright.step(dir === "down" ? -1 : 1) } // up | down
185205 }