me like nix
1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Io
5import "../../config" as Config
6import "../../components"
7import "../../services" as Services
8
9PanelWindow {
10 id: rightTrigger
11
12 required property var modelData
13 screen: modelData
14
15 anchors {
16 right: true
17 top: true
18 bottom: true
19 }
20 margins.right: 0
21 margins.top: 8
22 margins.bottom: 8
23 implicitWidth: 2
24 exclusiveZone: 0
25 color: "transparent"
26
27 property bool panelOpen: false
28
29 MouseArea {
30 anchors.fill: parent
31 hoverEnabled: true
32 onEntered: { closeTimer.stop(); rightTrigger.panelOpen = true }
33 onExited: closeTimer.restart()
34 }
35
36 onPanelOpenChanged: {
37 if (panelOpen) {
38 contentRect._opening = true
39 fadeInTimer.start()
40 } else {
41 fadeInTimer.stop()
42 contentRect._opening = false
43 contentRect.opacity = 0
44 }
45 }
46
47 Timer {
48 id: closeTimer
49 interval: 400
50 onTriggered: rightTrigger.panelOpen = false
51 }
52
53 PopupWindow {
54 id: rightPanel
55 anchor.window: rightTrigger
56 anchor.onAnchoring: {
57 anchor.rect.x = -(implicitWidth + 8)
58 anchor.rect.y = (rightTrigger.height - implicitHeight) / 2
59 }
60
61 visible: rightTrigger.panelOpen || contentRect.opacity > 0
62 implicitWidth: 280
63 implicitHeight: panelContent.implicitHeight + 32
64 color: "transparent"
65
66 Timer {
67 id: fadeInTimer
68 interval: 16
69 onTriggered: contentRect.opacity = 1
70 }
71
72 Rectangle {
73 id: contentRect
74 anchors.fill: parent
75 color: Config.Colours.base
76 radius: Config.Appearance.rounding.large
77 border.width: 1
78 border.color: Config.Colours.surface0
79 opacity: 0
80
81 property bool _opening: false
82 Behavior on opacity {
83 Anim {
84 duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small
85 easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel
86 }
87 }
88
89 HoverHandler {
90 onHoveredChanged: {
91 if (hovered) closeTimer.stop()
92 else closeTimer.restart()
93 }
94 }
95
96 ColumnLayout {
97 id: panelContent
98 anchors.fill: parent
99 anchors.margins: 16
100 spacing: 12
101
102 // Header
103 Text {
104 color: Config.Colours.text
105 font.family: Config.Appearance.font.family
106 font.pixelSize: Config.Appearance.font.size.large
107 font.bold: true
108 text: "Quick Settings"
109 }
110
111 Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 }
112
113 // Volume
114 ColumnLayout {
115 Layout.fillWidth: true
116 spacing: 6
117
118 RowLayout {
119 Layout.fillWidth: true
120 spacing: 8
121 Text { color: Config.Colours.maroon; font.family: Config.Appearance.font.family; font.pixelSize: 16; text: Services.Audio.muted ? "\uf026" : "\uf028" }
122 Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; text: "Volume" }
123 Item { Layout.fillWidth: true }
124 Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: Services.Audio.muted ? "Muted" : Services.Audio.volume + "%" }
125 }
126
127 Item {
128 Layout.fillWidth: true
129 height: 6
130 property real animatedPct: Services.Audio.volume
131 Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } }
132 Rectangle { anchors.fill: parent; radius: 3; color: Config.Colours.surface1 }
133 Rectangle {
134 width: parent.width * parent.animatedPct / 100
135 height: parent.height; radius: 3; color: Config.Colours.maroon
136 }
137 }
138
139 Rectangle {
140 Layout.fillWidth: true
141 height: 28
142 radius: Config.Appearance.rounding.normal
143 color: muteMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0
144 Behavior on color { CAnim {} }
145 Text { anchors.centerIn: parent; color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: Services.Audio.muted ? "Unmute" : "Mute" }
146 MouseArea { id: muteMouse; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: muteProc.running = true }
147 }
148 }
149
150 Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 }
151
152 // Brightness
153 ColumnLayout {
154 Layout.fillWidth: true
155 spacing: 6
156
157 RowLayout {
158 Layout.fillWidth: true
159 spacing: 8
160 Text { color: Config.Colours.yellow; font.family: Config.Appearance.font.family; font.pixelSize: 16; text: Services.Brightness.brightness > 50 ? "\uf0eb" : "\uf0ec" }
161 Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; text: "Brightness" }
162 Item { Layout.fillWidth: true }
163 Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: Services.Brightness.brightness + "%" }
164 }
165
166 Item {
167 Layout.fillWidth: true
168 height: 6
169 property real animatedPct: Services.Brightness.brightness
170 Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } }
171 Rectangle { anchors.fill: parent; radius: 3; color: Config.Colours.surface1 }
172 Rectangle {
173 width: parent.width * parent.animatedPct / 100
174 height: parent.height; radius: 3; color: Config.Colours.yellow
175 }
176 }
177 }
178
179 Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 }
180
181 // Network
182 RowLayout {
183 Layout.fillWidth: true
184 spacing: 8
185
186 Text {
187 color: Config.Colours.green
188 font.family: Config.Appearance.font.family
189 font.pixelSize: 16
190 text: {
191 if (!Services.Network.connected) return "\uf071"
192 if (Services.Network.isEthernet) return "\udb80\ude00"
193 return "\uf1eb"
194 }
195 }
196
197 ColumnLayout {
198 spacing: 2
199 Text {
200 color: Config.Colours.text
201 font.family: Config.Appearance.font.family
202 font.pixelSize: Config.Appearance.font.size.normal
203 text: Services.Network.connected ? Services.Network.name : "Disconnected"
204 }
205 Text {
206 color: Config.Colours.subtext0
207 font.family: Config.Appearance.font.family
208 font.pixelSize: Config.Appearance.font.size.small
209 text: {
210 if (!Services.Network.connected) return "No connection"
211 if (Services.Network.isEthernet) return "Wired connection"
212 return "Signal: " + Services.Network.strength + "%"
213 }
214 }
215 }
216 }
217
218 Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1; visible: Services.Battery.hasBattery }
219
220 // Battery + Power Profile
221 RowLayout {
222 Layout.fillWidth: true
223 spacing: 8
224 visible: Services.Battery.hasBattery
225
226 Text {
227 color: {
228 if (Services.Battery.percent <= 20) return Config.Colours.red
229 if (Services.Battery.status === "Charging") return Config.Colours.green
230 return Config.Colours.green
231 }
232 font.family: Config.Appearance.font.family
233 font.pixelSize: 16
234 text: {
235 if (Services.Battery.status === "Charging") return "\uf0e7"
236 if (Services.Battery.percent > 75) return "\uf240"
237 if (Services.Battery.percent > 50) return "\uf241"
238 if (Services.Battery.percent > 25) return "\uf242"
239 if (Services.Battery.percent > 10) return "\uf243"
240 return "\uf244"
241 }
242 }
243
244 ColumnLayout {
245 spacing: 2
246 Text {
247 color: Config.Colours.text
248 font.family: Config.Appearance.font.family
249 font.pixelSize: Config.Appearance.font.size.normal
250 text: Services.Battery.percent > 0 ? Services.Battery.percent + "%" : "No battery"
251 }
252 Text {
253 color: Config.Colours.subtext0
254 font.family: Config.Appearance.font.family
255 font.pixelSize: Config.Appearance.font.size.small
256 text: Services.Battery.status
257 }
258 }
259
260 Item { Layout.fillWidth: true }
261
262 // Power profile cycle
263 Rectangle {
264 width: 32; height: 32
265 radius: Config.Appearance.rounding.full
266 color: profileMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0
267 Behavior on color { CAnim {} }
268
269 Text {
270 anchors.centerIn: parent
271 color: {
272 if (Services.PowerProfile.profile === "performance") return Config.Colours.peach
273 if (Services.PowerProfile.profile === "power-saver") return Config.Colours.green
274 return Config.Colours.blue
275 }
276 font.family: Config.Appearance.font.family
277 font.pixelSize: 14
278 text: {
279 if (Services.PowerProfile.profile === "performance") return "\uf0e7"
280 if (Services.PowerProfile.profile === "power-saver") return "\uf06c"
281 return "\uf24e"
282 }
283 }
284
285 MouseArea {
286 id: profileMouse
287 anchors.fill: parent
288 hoverEnabled: true
289 cursorShape: Qt.PointingHandCursor
290 onClicked: Services.PowerProfile.cycleProfile()
291 }
292 }
293 }
294
295 // Power profile label
296 Text {
297 Layout.alignment: Qt.AlignRight
298 color: Config.Colours.overlay0
299 font.family: Config.Appearance.font.family
300 font.pixelSize: 10
301 text: {
302 if (Services.PowerProfile.profile === "performance") return "Performance"
303 if (Services.PowerProfile.profile === "power-saver") return "Power Saver"
304 return "Balanced"
305 }
306 }
307 }
308
309 Keys.onEscapePressed: rightTrigger.panelOpen = false
310 }
311
312 // Click outside to close
313 MouseArea {
314 anchors.fill: parent
315 z: -1
316 onClicked: rightTrigger.panelOpen = false
317 }
318 }
319
320 // Volume/brightness control processes
321 Process { id: muteProc; command: ["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"] }
322}