// The devices of one direction, as a list you choose from. // // The list tells the whole story, which means it also has to account for the // device that is not here: a session whose default is a pair of AirPods in // their case used to show no trace of them, so the output that audio will // return to the moment they connect looked like something Panama had // forgotten. SoundDefaults knows the configured name, and a configured name // with no node behind it renders last, dimmed and inert -- a ghost row. import QtQuick import Quickshell.Services.Pipewire import qs.config import qs.services Column { id: root property bool output: true readonly property var nodes: AudioDevices.nodes(root.output) readonly property var current: AudioDevices.current(root.output) // { name, label } for the configured default that is not present, or null // when everything the session chose is here. The service owns both the // comparison and the naming: an absent device has no description to borrow, // and its stored name is all there is to read it from. readonly property var absent: SoundDefaults.absent(root.output) width: parent ? parent.width : 620 spacing: 0 Repeater { model: root.nodes SoundDeviceRow { required property var modelData width: root.width node: modelData output: root.output selected: modelData === root.current } } SoundDeviceRow { width: root.width visible: !!root.absent output: root.output ghost: true ghostLabel: root.absent ? String(root.absent.label) : "" } Text { width: parent.width visible: root.nodes.length === 0 && !root.absent text: Pipewire.ready ? "No audio devices found" : "Discovering audio devices…" color: Theme.fgDim font.family: Theme.fontFamily font.pixelSize: Theme.fontSize horizontalAlignment: Text.AlignHCenter topPadding: 18 bottomPadding: 18 } }