Krolyxon
Commit ff7bd95

Branches

Refs →

aurora.configquickshellaurorashell.qml

224 lines · 8.8 KB · QML

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