diff --git a/volume-osd/README.md b/volume-osd/README.md new file mode 100644 index 0000000..cc7b8e3 --- /dev/null +++ b/volume-osd/README.md @@ -0,0 +1,7 @@ +# Volume OSD + +This is a simple overlay that shows the system volume when it changes using pipewire. + +You can run the overlay with `qs -p shell.qml`. + +![](./image.png) diff --git a/volume-osd/image.png b/volume-osd/image.png new file mode 100644 index 0000000..94b3e4e Binary files /dev/null and b/volume-osd/image.png differ diff --git a/volume-osd/shell.qml b/volume-osd/shell.qml new file mode 100644 index 0000000..f7c9215 --- /dev/null +++ b/volume-osd/shell.qml @@ -0,0 +1,92 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.Pipewire +import Quickshell.Widgets + +Scope { + id: root + + // Bind the pipewire node so its volume will be tracked + PwObjectTracker { + objects: [ Pipewire.defaultAudioSink ] + } + + Connections { + target: Pipewire.defaultAudioSink?.audio + + function onVolumeChanged() { + root.shouldShowOsd = true; + hideTimer.restart(); + } + } + + property bool shouldShowOsd: false + + Timer { + id: hideTimer + interval: 1000 + onTriggered: root.shouldShowOsd = false + } + + // The OSD window will be created and destroyed based on shouldShowOsd. + // PanelWindow.visible could be set instead of using a loader, but using + // a loader will reduce the memory overhead when the window isn't open. + LazyLoader { + active: root.shouldShowOsd + + PanelWindow { + // Since the panel's screen is unset, it will be picked by the compositor + // when the window is created. Most compositors pick the current active monitor. + + anchors.bottom: true + margins.bottom: screen.height / 5 + + implicitWidth: 400 + implicitHeight: 50 + color: "transparent" + + // An empty click mask prevents the window from blocking mouse events. + mask: Region {} + + Rectangle { + anchors.fill: parent + radius: height / 2 + color: "#80000000" + + RowLayout { + anchors { + fill: parent + leftMargin: 10 + rightMargin: 15 + } + + IconImage { + implicitSize: 30 + source: Quickshell.iconPath("audio-volume-high-symbolic") + } + + Rectangle { + // Stretches to fill all left-over space + Layout.fillWidth: true + + implicitHeight: 10 + radius: 20 + color: "#50ffffff" + + Rectangle { + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom + } + + implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0) + radius: parent.radius + } + } + } + } + } + } +}