aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-29 12:55:16 +0200
committerDanilo M. <danix@danix.xyz>2026-09-29 12:55:16 +0200
commit700f4be432e2c0f54fd6708b6a085b1fb9dd6108 (patch)
treecc58fc6caaf5c779c0b61094f6369a0eae2141d0
parent401cacb17fa2a5c756340be90d9a0b2c9c2e4a63 (diff)
downloadquickshell-700f4be432e2c0f54fd6708b6a085b1fb9dd6108.tar.gz
quickshell-700f4be432e2c0f54fd6708b6a085b1fb9dd6108.zip
feat(sound): album art as the OSD background
When the current track has art, the volume OSD now draws it across the whole panel, blurred (MultiEffect blur 0.5, default blurMax) and masked to the panel's rounded corners, since clip only cuts to the bounding box. The thumbnail in the track row stays sharp on top. A dark scrim (Theme.base at 0.82) sits over the art. The palette is dark and the text light, so a light cover made the panel unreadable, and more blur did not fix that; only darkening did. The scrim is a sibling of the MultiEffect, not a child: nested inside it, it did not paint at all, which is why raising its alpha first appeared to change nothing. Without art the panel keeps its translucent, compositor-frosted look. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--desktop/modules/sound/Osd.qml50
-rw-r--r--desktop/modules/sound/README.md6
2 files changed, 56 insertions, 0 deletions
diff --git a/desktop/modules/sound/Osd.qml b/desktop/modules/sound/Osd.qml
index 4a5e4a2..2894d35 100644
--- a/desktop/modules/sound/Osd.qml
+++ b/desktop/modules/sound/Osd.qml
@@ -13,6 +13,7 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Pipewire
import QtQuick
+import QtQuick.Effects
import "../.."
// The transient on-screen display. Unchanged in behaviour from volume-osd,
@@ -82,6 +83,55 @@ Scope {
border.width: 1
border.color: Qt.alpha(Theme.text, 0.12)
+ // Album art as the whole background, lightly blurred, when the
+ // player has any. It is opaque, so it hides the compositor's
+ // frosting; without art the panel keeps the translucent look.
+ // Inset by the border width so the border stays visible.
+ Image {
+ id: bgArt
+ anchors.fill: parent
+ anchors.margins: 1
+ source: Player.active ? Player.artUrl : ""
+ cache: false
+ asynchronous: true
+ fillMode: Image.PreserveAspectCrop
+ visible: false
+ }
+
+ // clip only cuts to the bounding box, so the corners come from a
+ // mask the size of the art with the panel's radius.
+ Rectangle {
+ id: bgMask
+ anchors.fill: bgArt
+ radius: parent.radius - 1
+ layer.enabled: true
+ visible: false
+ }
+
+ MultiEffect {
+ id: bgEffect
+ anchors.fill: bgArt
+ visible: bgArt.status === Image.Ready && bgArt.source != ""
+ source: bgArt
+ autoPaddingEnabled: false
+ blurEnabled: true
+ blur: 0.5
+ maskEnabled: true
+ maskSource: bgMask
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 1.0
+ }
+
+ // Keeps text and the accent bar legible on a bright cover. A
+ // sibling, not a child: nested inside the MultiEffect it did not
+ // paint, and a light cover showed through at full brightness.
+ Rectangle {
+ anchors.fill: bgArt
+ visible: bgEffect.visible
+ radius: bgMask.radius
+ color: Qt.alpha(Theme.base, 0.82)
+ }
+
HoverHandler {
onHoveredChanged: root.hovered = hovered
}
diff --git a/desktop/modules/sound/README.md b/desktop/modules/sound/README.md
index 72db235..d92d017 100644
--- a/desktop/modules/sound/README.md
+++ b/desktop/modules/sound/README.md
@@ -40,6 +40,12 @@ art, title, artist, and prev/play/next. With no player the panel is exactly
the volume OSD, at its original size. A track change or a play/pause shows the
panel too, so the row is not something you only see by touching the volume.
+When the track has art, it also fills the whole panel as a blurred background
+under a dark scrim (`Theme.base` at 0.82), and the thumbnail stays sharp in
+the row. The palette is dark, so the scrim is what keeps text legible on a
+light cover; blur alone did not. The art is opaque, so it hides the
+compositor's frosting while it shows. Without art the panel is unchanged.
+
Hovering the panel freezes its fade so the buttons can be clicked; moving away
starts the countdown again. Without a hover it behaves exactly as it did before
there was anything clickable on it. It still takes no keyboard focus.