diff options
Diffstat (limited to 'volume-osd/VolumeOsd.qml')
| -rw-r--r-- | volume-osd/VolumeOsd.qml | 135 |
1 files changed, 131 insertions, 4 deletions
diff --git a/volume-osd/VolumeOsd.qml b/volume-osd/VolumeOsd.qml index 9ee21ff..ab7eb62 100644 --- a/volume-osd/VolumeOsd.qml +++ b/volume-osd/VolumeOsd.qml @@ -47,14 +47,36 @@ Scope { function onMutedChanged() { root.show(root.sink, false); } } + // A track change shows the OSD as well, so the row is not something you + // only see when you happen to touch the volume. + Connections { + target: Player.current ?? null + function onTrackTitleChanged() { if (Player.title) root.showTrack(); } + function onPlaybackStateChanged() { root.showTrack(); } + } + Connections { target: root.source?.audio ?? null function onVolumeChanged() { root.show(root.source, true); } function onMutedChanged() { root.show(root.source, true); } } + function showTrack() { + if (!Player.active) return; + root.active = root.sink; + root.isInput = false; + hideTimer.restart(); + } + + // Hovering freezes the countdown so the transport buttons can be clicked; + // leaving starts it again. Without a hover the OSD behaves exactly as it + // did before there was anything clickable on it. + property bool hovered: false + + // Only counts down while the OSD is up and the pointer is elsewhere. Timer { id: hideTimer + running: root.active !== null && !root.hovered interval: root.timeout onTriggered: root.active = null } @@ -72,15 +94,16 @@ Scope { anchors.bottom: true margins.bottom: 120 + // Grows to fit the track row; the volume-only size is unchanged. implicitWidth: 360 - implicitHeight: 72 + implicitHeight: Player.active ? 150 : 72 color: "transparent" exclusionMode: ExclusionMode.Ignore WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.namespace: "quickshell-volume-osd" - // No keyboard focus: the OSD must never steal input from the window - // the user is typing in. + // Still no keyboard focus: the transport buttons are pointer targets, + // and the OSD must never take keys from the window being typed in. WlrLayershell.keyboardFocus: WlrKeyboardFocus.None Rectangle { @@ -93,9 +116,31 @@ Scope { border.width: 1 border.color: Qt.alpha(Theme.text, 0.12) - Row { + // Tracks the pointer over the whole panel so the fade can pause. + HoverHandler { + onHoveredChanged: root.hovered = hovered + } + + Column { anchors.fill: parent anchors.margins: 16 + spacing: 12 + + Loader { + active: Player.active + width: parent.width + sourceComponent: trackRow + } + + Rectangle { + visible: Player.active + width: parent.width + height: 1 + color: Qt.alpha(Theme.text, 0.1) + } + + Row { + width: parent.width spacing: 14 Text { @@ -157,6 +202,88 @@ Scope { } } } + } + } + } + + Component { + id: trackRow + + Row { + id: trackLine + // A Row sizes to its children, so the panel width has to be + // pushed in: the text column below subtracts from it. + width: parent ? parent.width : 0 + spacing: 12 + + // Players that extract embedded art reuse one temp path, so the + // source carries a per-track suffix and caching is off. + Rectangle { + width: 46; height: 46; radius: 6 + color: Qt.alpha(Theme.surface, 0.8) + clip: true + + Image { + anchors.fill: parent + source: Player.artUrl + cache: false + asynchronous: true + fillMode: Image.PreserveAspectCrop + visible: status === Image.Ready + } + Text { + anchors.centerIn: parent + visible: Player.artUrl === "" || parent.children[0].status !== Image.Ready + text: "" + font { family: Theme.fontFamily; pixelSize: 20 } + color: Theme.overlay + } + } + + Column { + anchors.verticalCenter: parent.verticalCenter + // Whatever the art and transport buttons leave: a fixed width + // here overflowed the panel and pushed `next` past its edge. + width: trackLine.width - 46 - transport.width - 2 * trackLine.spacing + spacing: 3 + + 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 { + id: transport + anchors.verticalCenter: parent.verticalCenter + spacing: 2 + 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() + } + } } } } |
