// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. import Quickshell import Quickshell.Services.Pipewire import QtQuick import "../.." Column { id: page required property var service signal back spacing: 16 // Output and input, each with its own slider. Repeater { model: [ { label: "Output", node: page.service.sink }, { label: "Input", node: page.service.source }, ] Column { required property var modelData readonly property var audio: modelData.node?.audio ?? null width: page.width spacing: 6 Item { width: parent.width implicitHeight: name.implicitHeight Text { id: name anchors.left: parent.left text: modelData.label font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true } color: Theme.text } Text { anchors.right: parent.right text: !audio ? "—" : audio.muted ? "muted" : Math.round(audio.volume * 100) + "%" font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1 } color: audio?.muted ? Theme.red : Theme.subtext } } Text { width: parent.width elide: Text.ElideRight text: modelData.node?.description ?? modelData.node?.name ?? "" font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } color: Theme.overlay } // Click or drag anywhere on the bar to set the level. Rectangle { width: parent.width height: 8 radius: 4 color: Theme.surface Rectangle { height: parent.height radius: parent.radius width: parent.width * Math.min(audio?.volume ?? 0, 1) color: audio?.muted ? Theme.red : Theme.accent opacity: audio?.muted ? 0.5 : 1 } MouseArea { anchors.fill: parent enabled: audio !== null onPositionChanged: mouse => set(mouse.x) onPressed: mouse => set(mouse.x) function set(x) { if (audio) audio.volume = Math.max(0, Math.min(1, x / width)); } } } } } Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.12) } // What is playing, with transport. Same Player singleton the OSD uses, // so playerctld's duplicate is already filtered out by dbusName. Column { width: parent.width spacing: 8 visible: Player.active Text { width: parent.width elide: Text.ElideRight text: Player.title || "Nothing playing" font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true } color: Theme.text } Text { width: parent.width elide: Text.ElideRight visible: Player.artist !== "" text: Player.artist font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } color: Theme.subtext } Row { spacing: 4 TransportButton { glyph: "" enabled: Player.current?.canGoPrevious ?? false onClicked: Player.current?.previous() } TransportButton { glyph: Player.playing ? "" : "" enabled: Player.current?.canTogglePlaying ?? false onClicked: Player.current?.togglePlaying() } TransportButton { glyph: "" enabled: Player.current?.canGoNext ?? false onClicked: Player.current?.next() } } } }