62 lines
1.8 KiB
QML
62 lines
1.8 KiB
QML
// Output / input device picker. Writing Pipewire.preferredDefaultAudioSink is
|
|
// what actually moves audio; the default* properties are read-only reflections
|
|
// of what the session manager settled on.
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Services.Pipewire
|
|
import qs.config
|
|
|
|
Item {
|
|
id: root
|
|
|
|
// false → input devices.
|
|
property bool output: true
|
|
property alias maxHeight: list.maxHeight
|
|
|
|
implicitHeight: list.implicitHeight
|
|
|
|
readonly property var nodes: {
|
|
return Pipewire.nodes.values.filter(n => {
|
|
if (n.isStream)
|
|
return false;
|
|
// Sources have to be filtered on the type flags: !isSink also
|
|
// matches video nodes (webcams show up here otherwise).
|
|
return root.output ? n.isSink : (n.type & PwNodeType.AudioSource) === PwNodeType.AudioSource;
|
|
});
|
|
}
|
|
|
|
readonly property var current: root.output ? Pipewire.defaultAudioSink : Pipewire.defaultAudioSource
|
|
|
|
function select(node): void {
|
|
if (root.output)
|
|
Pipewire.preferredDefaultAudioSink = node;
|
|
else
|
|
Pipewire.preferredDefaultAudioSource = node;
|
|
}
|
|
|
|
ScrollColumn {
|
|
id: list
|
|
anchors.fill: parent
|
|
maxHeight: 220
|
|
|
|
Repeater {
|
|
model: root.nodes
|
|
|
|
RowButton {
|
|
id: nodeRow
|
|
|
|
required property var modelData
|
|
|
|
width: parent.width
|
|
implicitHeight: 38
|
|
icon: root.output ? "audio-speakers-symbolic" : "audio-input-microphone-symbolic"
|
|
iconFallback: "audio-card-symbolic"
|
|
label: nodeRow.modelData.description || nodeRow.modelData.nickname || nodeRow.modelData.name
|
|
selected: nodeRow.modelData === root.current
|
|
onClicked: root.select(nodeRow.modelData)
|
|
}
|
|
}
|
|
}
|
|
}
|