quickshell: read live volume in OSD, drain slider at 0%

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-08-16 13:57:53 +01:00
parent 7c0098a184
commit e6f97fadd9

View file

@ -7004,7 +7004,7 @@ in
property string icon: "volume-2" property string icon: "volume-2"
property real value: 0 property real value: 0
property color fill: Theme.base0D property color fill: Theme.base0D
// Takes over the server glance's slot: fixed position, no // Takes over the server glance's slot: fixed position, no
// dependence on how tall the workspace column has grown. // dependence on how tall the workspace column has grown.
width: Theme.barWidth width: Theme.barWidth
@ -7015,11 +7015,11 @@ in
y: srvWidget.y + srvWidget.height / 2 - height / 2 y: srvWidget.y + srvWidget.height / 2 - height / 2
opacity: shown ? 1 : 0 opacity: shown ? 1 : 0
visible: opacity > 0.01 visible: opacity > 0.01
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: Theme.animContent; easing.type: Easing.OutCubic } NumberAnimation { duration: Theme.animContent; easing.type: Easing.OutCubic }
} }
function show(ic, v, f) { function show(ic, v, f) {
if (!armed || !isFocused) return; if (!armed || !isFocused) return;
icon = ic; icon = ic;
@ -7028,17 +7028,26 @@ in
shown = true; shown = true;
osdTimer.restart(); osdTimer.restart();
} }
Timer { id: osdTimer; interval: 1600; onTriggered: osdItem.shown = false } Timer { id: osdTimer; interval: 1600; onTriggered: osdItem.shown = false }
Timer { id: osdArm; interval: 1500; running: true; onTriggered: osdItem.armed = true } Timer { id: osdArm; interval: 1500; running: true; onTriggered: osdItem.armed = true }
function showVolume() { function showVolume() {
// The volume dropdown already shows the level live. // The volume dropdown already shows the level live.
if (bar.activeDropdown === volDropdown) return; if (bar.activeDropdown === volDropdown) return;
show(volWidget.volIcon, volWidget.vol / 100, // Read the audio object, not volWidget's derived
volWidget.muted ? Theme.base03 : Theme.base0D); // properties: this runs from the change signal, and
// those bindings haven't re-evaluated yet they
// still hold the previous level.
const a = volWidget.sink ? volWidget.sink.audio : null;
if (!a) return;
const v = a.volume;
show(a.muted ? "volume-x"
: v >= 0.5 ? "volume-2"
: v > 0 ? "volume-1" : "volume",
v, a.muted ? Theme.base03 : Theme.base0D);
} }
Connections { Connections {
target: volWidget.sink && volWidget.sink.audio ? volWidget.sink.audio : null target: volWidget.sink && volWidget.sink.audio ? volWidget.sink.audio : null
// `volume` notifies through volumesChanged, not // `volume` notifies through volumesChanged, not
@ -7047,14 +7056,14 @@ in
function onVolumesChanged() { osdItem.showVolume(); } function onVolumesChanged() { osdItem.showVolume(); }
function onMutedChanged() { osdItem.showVolume(); } function onMutedChanged() { osdItem.showVolume(); }
} }
Connections { Connections {
target: bar.shellRoot target: bar.shellRoot
function onOsdBrightness(icon, value) { function onOsdBrightness(icon, value) {
osdItem.show(icon, value, Theme.base0D); osdItem.show(icon, value, Theme.base0D);
} }
} }
// Card body so the OSD reads as a surface, not floating // Card body so the OSD reads as a surface, not floating
// marks on the bar. Scales in from slightly small. // marks on the bar. Scales in from slightly small.
Rectangle { Rectangle {
@ -7067,19 +7076,19 @@ in
Behavior on scale { Behavior on scale {
NumberAnimation { duration: Theme.animMorph; easing.type: Easing.OutExpo } NumberAnimation { duration: Theme.animMorph; easing.type: Easing.OutExpo }
} }
Column { Column {
id: osdCol id: osdCol
anchors.centerIn: parent anchors.centerIn: parent
spacing: 6 spacing: 6
SText { SText {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Math.round(Math.max(0, Math.min(1, osdItem.value)) * 100) text: Math.round(Math.max(0, Math.min(1, osdItem.value)) * 100)
color: Theme.base04 color: Theme.base04
font.pixelSize: 11 font.pixelSize: 11
} }
// Vertical track fill grows upward from the bottom. // Vertical track fill grows upward from the bottom.
// PillSlider is horizontal-only, so this is its own // PillSlider is horizontal-only, so this is its own
// rather than a rotated instance. // rather than a rotated instance.
@ -7089,12 +7098,16 @@ in
height: 96 height: 96
radius: 6 radius: 6
color: Theme.base02 color: Theme.base02
Rectangle { Rectangle {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
height: Math.max(parent.width, // Round cap needs width as a floor, but
parent.height * Math.max(0, Math.min(1, osdItem.value))) // 0% draws nothing so the track reads
// empty.
height: osdItem.value <= 0 ? 0
: Math.max(parent.width,
parent.height * Math.min(1, osdItem.value))
radius: parent.radius radius: parent.radius
color: osdItem.fill color: osdItem.fill
Behavior on height { Behavior on height {
@ -7102,7 +7115,7 @@ in
} }
} }
} }
SIcon { SIcon {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: osdItem.icon text: osdItem.icon