summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml')
-rw-r--r--accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml428
1 files changed, 279 insertions, 149 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml
index 007ce100..9c6b65a4 100644
--- a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml
+++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml
@@ -114,232 +114,362 @@ Item {
114 114
115 implicitWidth: tooltipContent.width 115 implicitWidth: tooltipContent.width
116 implicitHeight: tooltipContent.height 116 implicitHeight: tooltipContent.height
117 color: "black" 117 color: "transparent"
118 118
119 WrapperMouseArea { 119 Rectangle {
120 id: tooltipMouseArea 120 width: tooltip.width
121 height: tooltipLayout.childrenRect.height + 16
122 color: "black"
123 }
121 124
122 hoverEnabled: true 125 WrapperItem {
123 enabled: true 126 id: tooltipContent
124 127
125 anchors.fill: parent 128 bottomMargin: Math.max(0, 200 - tooltipLayout.implicitHeight)
129
130 WrapperMouseArea {
131 id: tooltipMouseArea
126 132
127 WrapperItem { 133 hoverEnabled: true
128 id: tooltipContent 134 enabled: true
129 135
130 margin: 8 136 WrapperItem {
131 bottomMargin: 8 + Math.max(0, 200 - tooltipLayout.implicitHeight) 137 margin: 8
138 bottomMargin: 8
132 139
133 GridLayout { 140 GridLayout {
134 id: tooltipLayout 141 id: tooltipLayout
135 142
136 columns: 4 143 columns: 4
137 144
138 Repeater { 145 Repeater {
139 model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") 146 model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device")
140 147
141 Item { 148 Item {
142 id: descItem 149 id: descItem
143 150
144 required property var modelData 151 required property var modelData
145 required property int index 152 required property int index
146 153
147 Layout.column: 0 154 Layout.column: 0
148 Layout.row: index 155 Layout.row: index
149 156
150 implicitWidth: descText.contentWidth 157 implicitWidth: descText.contentWidth
151 implicitHeight: descText.contentHeight 158 implicitHeight: descText.contentHeight
152 159
153 Text { 160 Text {
154 id: descText 161 id: descText
155 162
156 color: "white" 163 color: "white"
157 font.pointSize: 10 164 font.pointSize: 10
158 font.family: "Fira Sans" 165 font.family: "Fira Sans"
159 166
160 text: descItem.modelData.description 167 text: descItem.modelData.description
168 }
161 } 169 }
162 } 170 }
163 }
164 171
165 Repeater { 172 Repeater {
166 id: defaultSinkRepeater 173 id: defaultSinkRepeater
167 174
168 model: { 175 model: {
169 Array.from(Pipewire.devices.values) 176 Array.from(Pipewire.devices.values)
170 .filter(dev => dev.type == "Audio/Device") 177 .filter(dev => dev.type == "Audio/Device")
171 .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSink && node.device?.id == device.id )); 178 .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSink && node.device?.id == device.id ));
172 } 179 }
173 180
174 Item { 181 Item {
175 id: defaultSinkItem 182 id: defaultSinkItem
176 183
177 required property var modelData 184 required property var modelData
178 required property int index 185 required property int index
179 186
180 PwObjectTracker { 187 visible: Boolean(modelData)
181 objects: [defaultSinkItem.modelData]
182 }
183 188
184 Layout.column: 1 189 PwObjectTracker {
185 Layout.row: index 190 objects: [defaultSinkItem.modelData]
191 }
186 192
187 Layout.fillHeight: true 193 Layout.column: 1
194 Layout.row: index
188 195
189 implicitWidth: 16 + 8 196 Layout.fillHeight: true
190 197
191 WrapperMouseArea { 198 implicitWidth: 16 + 8
192 id: defaultSinkMouseArea
193 199
194 anchors.fill: parent 200 WrapperMouseArea {
195 hoverEnabled: true 201 id: defaultSinkMouseArea
196 cursorShape: Qt.PointingHandCursor
197 202
198 onClicked: { 203 anchors.fill: parent
199 Pipewire.preferredDefaultAudioSink = defaultSinkItem.modelData 204 hoverEnabled: true
200 } 205 cursorShape: Qt.PointingHandCursor
201 206
202 Rectangle { 207 onClicked: {
203 id: defaultSinkWidget 208 Pipewire.preferredDefaultAudioSink = defaultSinkItem.modelData
209 }
204 210
205 anchors.fill: parent 211 onWheel: event => scrollVolume(event);
206 color: { 212 property real sensitivity: (1 / 40) / 120
207 if (defaultSinkMouseArea.containsMouse) 213 function scrollVolume(event) {
208 return "#33808080"; 214 defaultSinkItem.modelData.audio.volume += event.angleDelta.y * sensitivity;
209 return "transparent";
210 } 215 }
211 216
212 MaterialDesignIcon { 217 Rectangle {
213 width: 16 218 id: defaultSinkWidget
214 height: 16 219
215 anchors.centerIn: parent 220 anchors.fill: parent
221 color: {
222 if (defaultSinkMouseArea.containsMouse)
223 return "#33808080";
224 return "transparent";
225 }
216 226
217 icon: { 227 MaterialDesignIcon {
218 if (defaultSinkItem.modelData?.id == Pipewire.defaultAudioSink?.id) 228 width: 16
219 return "speaker"; 229 height: 16
220 return "speaker-off"; 230 anchors.centerIn: parent
231
232 icon: {
233 if (defaultSinkItem.modelData?.id == Pipewire.defaultAudioSink?.id)
234 return "speaker";
235 return "speaker-off";
236 }
237 color: icon == "speaker" ? "white" : "#555"
238 }
239 }
240 }
241
242 PopupWindow {
243 id: volumeTooltip
244
245 property bool nextVisible: defaultSinkMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse
246
247 anchor {
248 item: defaultSinkMouseArea
249 edges: Edges.Bottom | Edges.Left
250 }
251 visible: false
252
253 onNextVisibleChanged: volumeHangTimer.restart()
254
255 onVisibleChanged: tooltip.openPopup = volumeTooltip.visible
256
257 Timer {
258 id: volumeHangTimer
259 interval: 100
260 onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible
261 }
262
263 implicitWidth: volumeTooltipText.contentWidth + 16
264 implicitHeight: volumeTooltipText.contentHeight + 16
265 color: "black"
266
267 WrapperMouseArea {
268 id: volumeTooltipMouseArea
269
270 hoverEnabled: true
271 enabled: true
272
273 onWheel: event => defaultSinkMouseArea.scrollVolume(event);
274
275 anchors.fill: parent
276
277 Item {
278 anchors.fill: parent
279
280 Text {
281 id: volumeTooltipText
282
283 anchors.centerIn: parent
284
285 font.pointSize: 10
286 font.family: "Fira Sans"
287 color: "white"
288
289 text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%`
290 }
221 } 291 }
222 color: icon == "speaker" ? "white" : "#555"
223 } 292 }
224 } 293 }
225 } 294 }
226 } 295 }
227 }
228 296
229 Repeater { 297 Repeater {
230 id: defaultSourceRepeater 298 id: defaultSourceRepeater
231 299
232 model: { 300 model: {
233 Array.from(Pipewire.devices.values) 301 Array.from(Pipewire.devices.values)
234 .filter(dev => dev.type == "Audio/Device") 302 .filter(dev => dev.type == "Audio/Device")
235 .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSource && node.device?.id == device.id )); 303 .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSource && node.device?.id == device.id ));
236 } 304 }
237 305
238 Item { 306 Item {
239 id: defaultSourceItem 307 id: defaultSourceItem
240 308
241 required property var modelData 309 required property var modelData
242 required property int index 310 required property int index
243 311
244 PwObjectTracker { 312 visible: Boolean(modelData)
245 objects: [defaultSourceItem.modelData] 313
246 } 314 PwObjectTracker {
315 objects: [defaultSourceItem.modelData]
316 }
317
318 Layout.column: 2
319 Layout.row: index
247 320
248 Layout.column: 2 321 Layout.fillHeight: true
249 Layout.row: index
250 322
251 Layout.fillHeight: true 323 implicitWidth: 16 + 8
252 324
253 implicitWidth: 16 + 8 325 WrapperMouseArea {
326 id: defaultSourceMouseArea
254 327
255 WrapperMouseArea { 328 anchors.fill: parent
256 id: defaultSourceMouseArea 329 hoverEnabled: true
330 cursorShape: Qt.PointingHandCursor
331
332 onClicked: {
333 Pipewire.preferredDefaultAudioSource = defaultSourceItem.modelData
334 }
335
336 onWheel: event => scrollVolume(event);
337 property real sensitivity: (1 / 40) / 120
338 function scrollVolume(event) {
339 defaultSourceItem.modelData.audio.volume += event.angleDelta.y * sensitivity;
340 }
257 341
258 anchors.fill: parent 342 Rectangle {
259 hoverEnabled: true 343 id: defaultSourceWidget
260 cursorShape: Qt.PointingHandCursor
261 344
262 onClicked: { 345 anchors.fill: parent
263 Pipewire.preferredDefaultAudioSource = defaultSourceItem.modelData 346 color: {
347 if (defaultSourceMouseArea.containsMouse)
348 return "#33808080";
349 return "transparent";
350 }
351
352 MaterialDesignIcon {
353 width: 16
354 height: 16
355 anchors.centerIn: parent
356
357 icon: {
358 if (defaultSourceItem.modelData?.id == Pipewire.defaultAudioSource?.id)
359 return "microphone";
360 return "microphone-off";
361 }
362 color: icon == "microphone" ? "white" : "#555"
363 }
364 }
264 } 365 }
265 366
266 Rectangle { 367 PopupWindow {
267 id: defaultSourceWidget 368 id: volumeTooltip
268 369
269 anchors.fill: parent 370 property bool nextVisible: defaultSourceMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse
270 color: { 371
271 if (defaultSourceMouseArea.containsMouse) 372 anchor {
272 return "#33808080"; 373 item: defaultSourceMouseArea
273 return "transparent"; 374 edges: Edges.Bottom | Edges.Left
274 } 375 }
376 visible: false
377
378 onNextVisibleChanged: volumeHangTimer.restart()
275 379
276 MaterialDesignIcon { 380 onVisibleChanged: tooltip.openPopup = volumeTooltip.visible
277 width: 16 381
278 height: 16 382 Timer {
279 anchors.centerIn: parent 383 id: volumeHangTimer
384 interval: 100
385 onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible
386 }
280 387
281 icon: { 388 implicitWidth: volumeTooltipText.contentWidth + 16
282 if (defaultSourceItem.modelData?.id == Pipewire.defaultAudioSource?.id) 389 implicitHeight: volumeTooltipText.contentHeight + 16
283 return "microphone"; 390 color: "black"
284 return "microphone-off"; 391
392 WrapperMouseArea {
393 id: volumeTooltipMouseArea
394
395 hoverEnabled: true
396 enabled: true
397
398 onWheel: event => defaultSourceMouseArea.scrollVolume(event);
399
400 anchors.fill: parent
401
402 Item {
403 anchors.fill: parent
404
405 Text {
406 id: volumeTooltipText
407
408 anchors.centerIn: parent
409
410 font.pointSize: 10
411 font.family: "Fira Sans"
412 color: "white"
413
414 text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%`
415 }
285 } 416 }
286 color: icon == "microphone" ? "white" : "#555"
287 } 417 }
288 } 418 }
289 } 419 }
290 } 420 }
291 }
292 421
293 Repeater { 422 Repeater {
294 id: profileRepeater 423 id: profileRepeater
295 424
296 model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") 425 model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device")
297 426
298 Item { 427 Item {
299 id: profileItem 428 id: profileItem
300 429
301 required property var modelData 430 required property var modelData
302 required property int index 431 required property int index
303 432
304 PwObjectTracker { 433 PwObjectTracker {
305 objects: [profileItem.modelData] 434 objects: [profileItem.modelData]
306 } 435 }
307 436
308 Layout.column: 3 437 Layout.column: 3
309 Layout.row: index 438 Layout.row: index
310 439
311 Layout.fillWidth: true 440 Layout.fillWidth: true
312 441
313 implicitWidth: Math.max(profileBox.implicitWidth, 300) 442 implicitWidth: Math.max(profileBox.implicitWidth, 300)
314 implicitHeight: profileBox.height 443 implicitHeight: profileBox.height
315 444
316 ComboBox { 445 ComboBox {
317 id: profileBox 446 id: profileBox
318 447
319 model: profileItem.modelData.profiles 448 model: profileItem.modelData.profiles
320 449
321 textRole: "description" 450 textRole: "description"
322 valueRole: "index" 451 valueRole: "index"
323 onActivated: profileItem.modelData.setProfile(currentValue) 452 onActivated: profileItem.modelData.setProfile(currentValue)
324 453
325 anchors.fill: parent 454 anchors.fill: parent
326 455
327 implicitContentWidthPolicy: ComboBox.WidestText 456 implicitContentWidthPolicy: ComboBox.WidestText
328 457
329 Connections { 458 Connections {
330 target: profileItem.modelData 459 target: profileItem.modelData
331 function onCurrentProfileChanged() { 460 function onCurrentProfileChanged() {
461 profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile);
462 }
463 }
464 Component.onCompleted: {
332 profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); 465 profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile);
333 } 466 }
334 }
335 Component.onCompleted: {
336 profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile);
337 }
338 467
339 Connections { 468 Connections {
340 target: profileBox.popup 469 target: profileBox.popup
341 function onVisibleChanged() { 470 function onVisibleChanged() {
342 tooltip.openPopup = profileBox.popup.visible 471 tooltip.openPopup = profileBox.popup.visible
472 }
343 } 473 }
344 } 474 }
345 } 475 }