Krolyxon
Commit a9ecc13

Branches

Refs →

aurora.configquickshellaurorashell.qml

217 lines · 8.4 KB · QML

1import QtQuick
2import Quickshell
3import Quickshell.Io
4import Quickshell.Hyprland
5import Quickshell.Services.Pipewire
6
7// aurora: bar + control panel + notifications, themed from pywal16.
8ShellRoot {
9 id: root
10
11 property bool opened: false
12 property int tab: 0
13 property double closedAt: 0
14 property string lastTab: ""
15 property string lastScreen: ""
16 property var panelScreen: focusedScreen()
17 readonly property var focusedScr: focusedScreen()
18 // centered overlay windows (wallpaper picker, clipboard, ...): one at a time, on the focused monitor
19 property string overlay: ""
20 property bool barShown: true // `bar` IPC (SUPER+B); not persisted, a restart shows it again
21 property var overlayScreen: focusedScreen()
22 // touch singletons that must start with the shell (Idle creates ~/.config/aurora/hypridle.conf)
23 readonly property string idleFile: Idle.file
24 readonly property bool batCare: Battery.ready // charge limit + low battery warnings
25 readonly property var tabNames: ["home", "audio", "net", "bt", "sys", "notif"]
26 // settings app (a normal window): open state + shown section
27 property bool settingsOpen: false
28 property string settingsSection: "network"
29
30 // screen of the monitor that currently has focus (falls back to the first screen)
31 function focusedScreen() {
32 var m = Hyprland.focusedMonitor
33 var l = Quickshell.screens
34 if (m) for (var i = 0; i < l.length; i++) if (l[i].name === m.name) return l[i]
35 return l.length ? l[0] : null
36 }
37 function close() {
38 if (!opened) return
39 opened = false
40 closedAt = Date.now()
41 lastTab = tabNames[tab]
42 lastScreen = panelScreen ? panelScreen.name : ""
43 }
44 property double overlayClosedAt: 0
45 property string lastOverlay: ""
46 function closeOverlay() {
47 if (overlay === "") return
48 lastOverlay = overlay
49 overlayClosedAt = Date.now()
50 overlay = ""
51 }
52 // scr: where to open (defaults to the focused monitor). Same instant re-open guard as togglePanel.
53 function toggleOverlay(name, scr) {
54 if (overlay === name) { closeOverlay(); return }
55 if (lastOverlay === name && Date.now() - overlayClosedAt < 250) return
56 close()
57 overlayScreen = scr ? scr : focusedScreen()
58 overlay = name
59 }
60 // section "" = toggle the window on its last section; a name opens (or switches) to it
61 function settings(section) {
62 if (section === "" || section === undefined) { settingsOpen = !settingsOpen; return }
63 settingsSection = section
64 settingsOpen = true
65 }
66 function openTab(name) {
67 if (name === "settings") { settings("appearance"); return } // old panel tab lives in the app now
68 var i = tabNames.indexOf(name)
69 if (i >= 0) tab = i
70 panelScreen = focusedScreen()
71 opened = true
72 }
73 // bar buttons: click once to open that tab, again to close.
74 // (the focus grab already closed the panel on the press, so guard against instant re-open)
75 // scr = the screen whose bar was clicked, so the panel opens on that monitor
76 function togglePanel(name, scr) {
77 var i = tabNames.indexOf(name)
78 var sn = scr ? scr.name : ""
79 if (opened && tab === i && panelScreen && panelScreen.name === sn) { close(); return }
80 if (!opened && lastTab === name && lastScreen === sn && Date.now() - closedAt < 250) return
81 tab = i
82 panelScreen = scr
83 opened = true
84 }
85
86 // keep every pipewire node bound so volume / mute / properties are readable
87 PwObjectTracker { objects: Pipewire.nodes.values }
88
89 Variants {
90 model: Quickshell.screens
91 Desktop {}
92 }
93
94 Variants {
95 model: Quickshell.screens
96 Dock {}
97 }
98
99 Variants {
100 model: Quickshell.screens
101 Bar {
102 visible: root.barShown
103 onTogglePanel: (name, scr) => root.togglePanel(name, scr)
104 onToggleOverlay: (name, scr) => root.toggleOverlay(name, scr)
105 }
106 }
107
108 ControlPanel {
109 screen: root.panelScreen
110 opened: root.opened
111 tab: root.tab
112 onTabRequested: i => root.tab = i
113 onCloseRequested: root.close()
114 onOverlayRequested: name => { root.close(); root.toggleOverlay(name) }
115 onSettingsRequested: { root.close(); root.settingsOpen = true }
116 }
117
118 Overview {
119 screen: root.overlayScreen
120 opened: root.overlay === "overview"
121 onCloseRequested: root.closeOverlay()
122 }
123
124 CalPopup {
125 screen: root.overlayScreen
126 opened: root.overlay === "cal"
127 onCloseRequested: root.closeOverlay()
128 }
129
130 ClipWindow {
131 screen: root.overlayScreen
132 opened: root.overlay === "clip"
133 onCloseRequested: root.closeOverlay()
134 }
135
136 CharPicker {
137 screen: root.overlayScreen
138 opened: root.overlay === "chars"
139 onCloseRequested: root.closeOverlay()
140 }
141
142 WallPicker {
143 screen: root.overlayScreen
144 opened: root.overlay === "wall"
145 onCloseRequested: root.closeOverlay()
146 }
147
148 PowerMenu {
149 screen: root.overlayScreen
150 opened: root.overlay === "power"
151 onCloseRequested: root.closeOverlay()
152 }
153
154 Runner {
155 id: runner
156 screen: root.overlayScreen
157 opened: root.overlay === "runner"
158 shell: root
159 onCloseRequested: root.closeOverlay()
160 onOpenRequested: {
161 root.close()
162 if (root.overlay === "runner") return // already open (run mode): it just switched to the list
163 root.overlayScreen = root.focusedScreen()
164 root.overlay = "runner"
165 }
166 }
167
168 SettingsApp {
169 opened: root.settingsOpen
170 section: root.settingsSection
171 onCloseRequested: root.settingsOpen = false
172 onSectionRequested: name => root.settingsSection = name
173 onOverlayRequested: name => root.toggleOverlay(name)
174 }
175
176 NotifPopups { screen: root.focusedScr; suppressed: root.opened }
177
178 Osd { id: osd; screen: root.focusedScr; suppressed: root.opened }
179 Connections {
180 target: Bright
181 // brightnessctl only knows the laptop panel; external monitors go through ddcutil (no readback)
182 function onStepped() {
183 var m = Hyprland.focusedMonitor
184 if (Bright.available && (!m || m.name.indexOf("eDP") === 0))
185 osd.show(Theme.iBright, Bright.percent / 100, false)
186 }
187 }
188
189 IpcHandler {
190 target: "shell"
191 function toggle(): void { if (root.opened) root.close(); else root.openTab(root.tabNames[root.tab]) }
192 function open(): void { root.openTab(root.tabNames[root.tab]) }
193 function close(): void { root.close() }
194 function show(name: string): void { root.openTab(name) }
195 // `qs ipc call shell show x` is swallowed by the qs CLI (it has its own `show`), so use this
196 function tab(name: string): void { root.openTab(name) }
197 function dnd(): void { Notifs.dnd = !Notifs.dnd }
198 function shot(mode: string): void { root.close(); Shot.take(mode) } // region | window | output | ocr
199 function shotsave(): void { Shot.save = !Shot.save }
200 function overview(): void { root.toggleOverlay("overview") }
201 function cal(): void { root.toggleOverlay("cal") }
202 function clip(): void { root.toggleOverlay("clip") }
203 function chars(): void { root.toggleOverlay("chars") } // glyph picker (SUPER+grave)
204 function wall(): void { root.toggleOverlay("wall") }
205 function power(): void { root.toggleOverlay("power") }
206 function run(): void { root.toggleOverlay("runner") }
207 function dmenu(dir: string): void { runner.startDmenu(dir) } // used by ~/.local/bin/aurora-run -d
208 function bar(): void { root.barShown = !root.barShown }
209 function wallrandom(): void { Wall.random() }
210 function wallset(path: string): void { Wall.apply(path) } // absolute path (setwall resolves it)
211 function rec(mode: string): void { root.close(); Rec.toggle(mode === "region" ? "region" : "screen") } // screen (default) | region
212 function settings(section: string): void { root.settings(section) } // "" = toggle
213 function caffeine(): void { Toggles.caffeine = !Toggles.caffeine }
214 function portal(): void { Net.signIn() } // open the captive portal login page (or say we're online)
215 function bright(dir: string): void { Bright.step(dir === "down" ? -1 : 1) } // up | down
216 }
217}