repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
pi/agent/skills/optional/quickshell/examples/05-osd-volume/shell.qml
Raw// 05-osd-volume/shell.qml
// OSD volume: appears for 1s when the default sink's volume changes.
// Pattern: Scope (shared state) + Connections (signal) + Timer (debounce) + LazyLoader (memory-aware) + PanelWindow (default layer).
// Run with: qs -p examples/05-osd-volume/shell.qml
// Try: `wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.5` in another terminal to trigger.
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
import Quickshell.Widgets
Scope {
id: root
readonly property var sinkAudio: Pipewire.defaultAudioSink ? Pipewire.defaultAudioSink.audio : null
// Keep the default sink tracked. The Connections below needs it to be live.
PwObjectTracker { objects: [ Pipewire.defaultAudioSink ] }
// Show OSD for 1s after every volume change.
Connections {
target: root.sinkAudio
function onVolumeChanged() {
root.shouldShowOsd = true
hideTimer.restart()
}
function onMutedChanged() {
root.shouldShowOsd = true
hideTimer.restart()
}
}
property bool shouldShowOsd: false
Timer {
id: hideTimer
interval: 1000
onTriggered: root.shouldShowOsd = false
}
// LazyLoader: only exists when shouldShowOsd. Frees memory between events.
// PanelWindow, not FloatingWindow, provides layer-shell placement.
LazyLoader {
active: root.shouldShowOsd
PanelWindow {
// The compositor picks the current focused monitor when no screen is set.
anchors.bottom: true
margins.bottom: screen.height / 5
exclusiveZone: 0
implicitWidth: 400
implicitHeight: 60
color: "transparent"
// Empty region = click-through. Clicks pass to whatever's behind.
mask: Region {}
Rectangle {
anchors.fill: parent
radius: height / 2
color: "#aa000000"
RowLayout {
anchors {
fill: parent
leftMargin: 14
rightMargin: 18
}
spacing: 12
IconImage {
implicitSize: 28
source: Quickshell.iconPath(
root.sinkAudio && root.sinkAudio.muted
? "audio-volume-muted-symbolic"
: "audio-volume-high-symbolic"
)
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 8
radius: 4
color: "#33ffffff"
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
implicitWidth: parent.width * Math.min(1, root.sinkAudio ? root.sinkAudio.volume : 0)
radius: 4
color: "white"
}
}
Text {
text: `${Math.floor((root.sinkAudio ? root.sinkAudio.volume : 0) * 100)}%`
color: "white"
font.pointSize: 13
Layout.preferredWidth: 50
}
}
}
}
}
}