Krolyxon
Commit eff182a

Branches

Refs →

aurora.configquickshellauroraBar.qml

216 lines · 8.8 KB · QML

1import QtQuick
2import Quickshell
3import Quickshell.Wayland
4import Quickshell.Bluetooth
5import Quickshell.Hyprland
6import Quickshell.Services.Pipewire
7import Quickshell.Services.UPower
8
9PanelWindow {
10 id: bar
11 required property var modelData
12 screen: modelData
13 signal togglePanel(string name, var scr)
14 signal toggleOverlay(string name, var scr)
15
16 anchors { top: true; left: true; right: true }
17 implicitHeight: Theme.barHeight
18 color: Theme.barBg
19 WlrLayershell.namespace: "aurora-bar"
20
21 SystemClock { id: clock; precision: SystemClock.Minutes }
22
23 // caffeine: one inhibitor is enough, keep it on the first screen's bar
24 IdleInhibitor {
25 window: bar
26 enabled: Toggles.caffeine && bar.modelData === Quickshell.screens[0]
27 }
28
29 // ---- state ----
30 readonly property var audio: Pipewire.defaultAudioSink ? Pipewire.defaultAudioSink.audio : null
31 readonly property var mic: Pipewire.defaultAudioSource ? Pipewire.defaultAudioSource.audio : null
32 readonly property var bat: UPower.displayDevice
33 readonly property bool hasBat: bat !== null && bat.isPresent
34 readonly property real batPct: hasBat ? (bat.percentage > 1 ? bat.percentage / 100 : bat.percentage) : 0
35 readonly property bool charging: hasBat && bat.state === UPowerDeviceState.Charging
36 readonly property bool plugged: hasBat && bat.state === UPowerDeviceState.FullyCharged
37 readonly property bool batLow: hasBat && batPct <= 0.15 && !charging
38 readonly property var adapter: Bluetooth.defaultAdapter
39
40 // which Hyprland monitor this bar belongs to, and whether it has focus
41 readonly property var hmon: Hyprland.monitorFor(modelData)
42 readonly property bool monFocused: hmon !== null && hmon.focused
43
44 function findHeadset() {
45 var l = Bluetooth.devices.values
46 for (var i = 0; i < l.length; i++) {
47 var d = l[i]
48 if (!d.connected || !d.batteryAvailable) continue
49 var ic = String(d.icon)
50 if (ic.indexOf("head") >= 0 || ic.indexOf("audio") >= 0) return d
51 }
52 return null
53 }
54 function btConnected() {
55 var l = Bluetooth.devices.values
56 for (var i = 0; i < l.length; i++) if (l[i].connected) return true
57 return false
58 }
59 readonly property var headset: findHeadset()
60 readonly property bool btOn: adapter !== null && adapter.enabled
61 readonly property bool btLink: btOn && btConnected()
62
63 Item {
64 anchors.fill: parent
65
66 // ---------------- left ----------------
67 Row {
68 anchors.left: parent.left
69 height: parent.height
70 spacing: 10
71 Workspaces { height: parent.height; monitor: bar.hmon }
72 CatLoop { height: parent.height }
73 }
74
75 // ---------------- center ----------------
76 Text {
77 anchors.centerIn: parent
78 width: Math.min(implicitWidth, parent.width * 0.32)
79 elide: Text.ElideRight
80 horizontalAlignment: Text.AlignHCenter
81 readonly property string title: ToplevelManager.activeToplevel ? ToplevelManager.activeToplevel.title : ""
82 visible: bar.monFocused && title !== "" // active window is global: show it on the focused monitor only
83 text: Theme.iArch + " " + title
84 color: Theme.fg
85 font.family: Theme.font
86 font.pixelSize: Theme.fontSize
87 font.bold: true
88 }
89
90 // ---------------- right ----------------
91 Row {
92 anchors.right: parent.right
93 height: parent.height
94 spacing: 10
95
96 Tray { show: bar.monFocused }
97
98 BarPill { // click to stop recording / cancel the area selection
99 visible: Rec.busy
100 icon: Theme.iStop
101 text: Rec.running ? "rec " + Theme.fmtClock(Rec.elapsed) : "select area"
102 tint: Theme.err
103 underline: Theme.err
104 onActivated: Rec.stop()
105 }
106
107 // privacy: global, so only on the focused monitor's bar. Click the mic one to mute the mic.
108 BarPill {
109 visible: bar.monFocused && Privacy.mic
110 icon: bar.mic && bar.mic.muted ? Theme.iMicOff : Theme.iMic
111 text: Privacy.micApps.join(", ")
112 tint: bar.mic && bar.mic.muted ? Theme.dim : Theme.err
113 underline: Theme.err
114 onActivated: if (bar.mic) bar.mic.muted = !bar.mic.muted
115 }
116 BarPill {
117 visible: bar.monFocused && Privacy.cam
118 icon: Theme.iWebcam
119 text: Privacy.camApps.join(", ")
120 tint: Theme.err
121 underline: Theme.err
122 }
123 BarPill {
124 visible: bar.monFocused && Privacy.screen
125 icon: Theme.iCast
126 text: Privacy.shareApps.join(", ")
127 tint: Theme.err
128 underline: Theme.err
129 }
130
131 BarPill { // left click: list in the sys tab, right click: upgrade in foot
132 visible: bar.monFocused && Settings.updatesPill && Updates.count > 0
133 icon: Theme.iUpdate
134 text: String(Updates.count)
135 tint: Updates.upgrading ? Theme.dim : Theme.fg
136 onActivated: button => {
137 if (button === Qt.RightButton) Updates.upgrade()
138 else bar.togglePanel("sys", bar.modelData)
139 }
140 }
141
142 BarPill {
143 visible: Toggles.caffeine || Toggles.airplane
144 icon: (Toggles.caffeine ? Theme.iCoffee : "") + (Toggles.caffeine && Toggles.airplane ? " " : "")
145 + (Toggles.airplane ? Theme.iPlane : "")
146 onActivated: bar.togglePanel("home", bar.modelData)
147 }
148
149 BarPill {
150 visible: bar.headset !== null
151 icon: Theme.iHeadphones
152 text: bar.headset ? Math.round(bar.headset.battery * 100) + "%" : ""
153 onActivated: bar.togglePanel("bt", bar.modelData)
154 }
155
156 BarPill {
157 visible: bar.audio !== null
158 icon: Theme.volIcon(bar.audio ? bar.audio.volume : 0, bar.audio ? bar.audio.muted : true)
159 text: (bar.audio ? Math.round(bar.audio.volume * 100) : 0) + "%"
160 + (bar.mic && bar.mic.muted ? " " + Theme.iMicOff : "")
161 onActivated: button => {
162 if (button === Qt.RightButton && bar.audio) bar.audio.muted = !bar.audio.muted
163 else if (button === Qt.MiddleButton && bar.mic) bar.mic.muted = !bar.mic.muted
164 else bar.togglePanel("audio", bar.modelData)
165 }
166 onScrolled: dir => {
167 if (bar.audio) bar.audio.volume = Math.max(0, Math.min(1, bar.audio.volume + dir * 0.02))
168 }
169 }
170
171 BarPill {
172 icon: Net.wired && !(Net.wifiEnabled && Net.connected !== "") ? Net.wiredIcon
173 : (Net.wifiEnabled ? Theme.wifiIcon(Net.connected !== "" ? Net.activeSignal : 0) : Theme.iWifiOff)
174 tint: Net.portal ? Theme.err : ((Net.wired || (Net.wifiEnabled && Net.connected !== "")) ? Theme.fg : Theme.dim)
175 onActivated: bar.togglePanel("net", bar.modelData)
176 }
177
178 BarPill {
179 visible: bar.adapter !== null
180 icon: !bar.btOn ? Theme.iBtOff : (bar.btLink ? Theme.iBtConn : Theme.iBt)
181 tint: bar.btLink ? Theme.fg : Theme.dim
182 onActivated: bar.togglePanel("bt", bar.modelData)
183 }
184
185 BarPill {
186 visible: bar.hasBat
187 icon: Theme.batIcon(bar.batPct, bar.charging, bar.plugged)
188 text: Math.round(bar.batPct * 100) + "%"
189 tint: bar.batLow ? Theme.err : Theme.fg
190 underline: bar.batLow ? Theme.err : Theme.accent
191 onActivated: bar.togglePanel("sys", bar.modelData)
192 }
193
194 BarPill {
195 icon: Notifs.dnd ? Theme.iBellOff : Theme.iBell
196 text: Notifs.count > 0 ? String(Notifs.count) : ""
197 tint: Notifs.dnd ? Theme.dim : Theme.fg
198 onActivated: button => {
199 if (button === Qt.RightButton) Notifs.dnd = !Notifs.dnd
200 else bar.togglePanel("notif", bar.modelData)
201 }
202 }
203
204 BarPill {
205 icon: Theme.iCal
206 text: Qt.formatDateTime(clock.date, "dd MMM yyyy")
207 onActivated: bar.toggleOverlay("cal", bar.modelData)
208 }
209 BarPill {
210 icon: Theme.iClock
211 text: Qt.formatDateTime(clock.date, "HH:mm")
212 onActivated: bar.toggleOverlay("cal", bar.modelData)
213 }
214 }
215 }
216}