122 lines
3.6 KiB
QML
122 lines
3.6 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import Quickshell.Services.Pipewire
|
|
|
|
Scope {
|
|
id: root
|
|
|
|
// Pick a Nerd Font glyph matching the current level / mute state, matching
|
|
// the glyph-based icon style used elsewhere in this config (e.g. Battery).
|
|
function volumeGlyph(): string {
|
|
const sink = Pipewire.defaultAudioSink;
|
|
if (!sink || !sink.audio || sink.audio.muted)
|
|
return ""; // muted
|
|
|
|
const v = sink.audio.volume;
|
|
if (v <= 0.0)
|
|
return ""; // muted / zero
|
|
if (v < 0.34)
|
|
return ""; // low
|
|
if (v < 0.67)
|
|
return ""; // medium
|
|
return ""; // high
|
|
}
|
|
|
|
// Bind the pipewire node so its volume will be tracked
|
|
PwObjectTracker {
|
|
objects: [Pipewire.defaultAudioSink]
|
|
}
|
|
|
|
Connections {
|
|
target: Pipewire.defaultAudioSink?.audio
|
|
|
|
function onVolumesChanged() {
|
|
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
|
|
}
|
|
|
|
// 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
|
|
exclusiveZone: 0
|
|
|
|
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
|
|
}
|
|
|
|
Text {
|
|
Layout.alignment: Qt.AlignVCenter
|
|
font.family: "JetBrainsMono Nerd Font"
|
|
font.pixelSize: 24
|
|
color: "white"
|
|
text: root.volumeGlyph()
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Dim the fill while muted as an extra visual cue.
|
|
color: (Pipewire.defaultAudioSink?.audio.muted ?? false) ? "#80ffffff" : "white"
|
|
implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
|
|
radius: parent.radius
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|