Krolyxon
Commit 85941a2

Branches

Refs →

aurora.configquickshellwal-controlAudioPage.qml

108 lines · 4.0 KB · QML

1import QtQuick
2import QtQuick.Layouts
3import Quickshell.Services.Pipewire
4
5Flickable {
6 id: flick
7 clip: true
8 contentWidth: width
9 contentHeight: col.implicitHeight
10 boundsBehavior: Flickable.StopAtBounds
11
12 function mediaClass(n) {
13 return (n.properties && n.properties["media.class"]) ? String(n.properties["media.class"]) : ""
14 }
15 function label(n) { return n.description || n.nickname || n.name }
16
17 // kind: "sink" | "source" | "stream"
18 function nodes(kind) {
19 var l = Pipewire.nodes.values, out = []
20 for (var i = 0; i < l.length; i++) {
21 var n = l[i]
22 if (!n.audio) continue
23 if (kind === "sink" && !n.isStream && n.isSink) out.push(n)
24 else if (kind === "source" && !n.isStream && !n.isSink) out.push(n)
25 else if (kind === "stream" && n.isStream && mediaClass(n).indexOf("Output") >= 0) out.push(n)
26 }
27 return out
28 }
29 function sinkIcon(n) {
30 var s = (n.name + " " + n.description).toLowerCase()
31 return (s.indexOf("bluez") >= 0 || s.indexOf("head") >= 0) ? Theme.iHeadphones : Theme.iSpeaker
32 }
33
34 readonly property var sinks: nodes("sink")
35 readonly property var sources: nodes("source")
36 readonly property var streams: nodes("stream")
37
38 ColumnLayout {
39 id: col
40 width: flick.width
41 spacing: 4
42
43 SectionLabel { text: "output device" }
44 Repeater {
45 model: flick.sinks
46 delegate: ListRow {
47 required property var modelData
48 icon: flick.sinkIcon(modelData)
49 title: flick.label(modelData)
50 subtitle: current ? "default output" : ""
51 current: Pipewire.defaultAudioSink === modelData
52 trailing: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
53 onClicked: Pipewire.preferredDefaultAudioSink = modelData
54 }
55 }
56
57 SectionLabel { text: "input device"; Layout.topMargin: 10 }
58 Repeater {
59 model: flick.sources
60 delegate: ListRow {
61 required property var modelData
62 icon: Theme.iMic
63 title: flick.label(modelData)
64 subtitle: current ? "default input" : ""
65 current: Pipewire.defaultAudioSource === modelData
66 trailing: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
67 onClicked: Pipewire.preferredDefaultAudioSource = modelData
68 }
69 }
70
71 SectionLabel { text: "applications"; Layout.topMargin: 10 }
72 Text {
73 visible: flick.streams.length === 0
74 text: "no apps playing audio"
75 color: Theme.dim
76 font.family: Theme.font
77 font.pixelSize: Theme.fontSize - 1
78 Layout.leftMargin: 10
79 }
80 Repeater {
81 model: flick.streams
82 delegate: ColumnLayout {
83 required property var modelData
84 Layout.fillWidth: true
85 spacing: 0
86 Text {
87 Layout.leftMargin: 4
88 Layout.fillWidth: true
89 elide: Text.ElideRight
90 text: (modelData.properties && modelData.properties["application.name"])
91 ? modelData.properties["application.name"] : flick.label(modelData)
92 color: Theme.fg
93 font.family: Theme.font
94 font.pixelSize: Theme.fontSize - 1
95 }
96 SliderRow {
97 Layout.fillWidth: true
98 icon: Theme.volIcon(modelData.audio.volume, modelData.audio.muted)
99 value: modelData.audio.volume
100 muted: modelData.audio.muted
101 onMoved: v => modelData.audio.volume = v
102 onIconClicked: modelData.audio.muted = !modelData.audio.muted
103 }
104 }
105 }
106 Item { Layout.preferredHeight: 8 }
107 }
108}