Files
qbz/crates/qbz-qt/qml/shell/ArtPreviewOverlay.qml
T
vicrodh f701bcd4db fix(qt): size-aware now-playing artwork, up to the mega tier
Restored queues and recently_played carried whatever variant the building
surface picked — 50px JPEGs measured in the wild — into the now-playing
bar, MPRIS, Discord and the immersive big slots. The np feed rewrites
sized Qobuz covers up to 600px; big slots (immersive panels, split,
preview overlay) resolve per size bucket through npArtworkPathLarge with
one re-resolve per tier crossed and never on shrink.

Registry-miss arm (restored sessions carry no ImageSet): rewrite to the
FULL requested bucket with a session negative cache, so a pre-mega album
pays one 404 per session instead of one per track change. And the mega
tier's wire suffix is the literal _max.jpg, not _3000 (404 measured
against the live CDN) — qobuz_cover_at_px now accepts and emits _max.

Local covers gain an on-demand 1600px tier (never upscaled); Plex big
slots tokenize at 1024.

km
2026-08-16 08:23:15 -06:00

78 lines
3.1 KiB
QML

// Global floating album-art preview — a 220x220 cover that floats ABOVE the
// now-playing bar's small cover while the pointer hovers it. 1:1 port of
// ArtPreviewOverlay.slint.
//
// Two properties of the original are load-bearing and kept exactly:
//
// 1. It is mounted LAST in AppShell and fills the window, so it paints over
// every surface without needing a z fight.
// 2. It is NON-INTERACTIVE — no MouseArea anywhere. The Slint tried a
// PopupWindow first and it STOLE THE HOVER from the trigger (the cover's
// has-hover flipped the moment the popup appeared, which closed it, which
// restored the hover…), so the preview flickered. An overlay that cannot
// receive events cannot do that; clicks also pass straight through to
// whatever is underneath.
//
// The state (anchor + visibility) rides QbzShell, like the drag ghost in the
// same file — the trigger is inside the bar and the overlay is a sibling of
// the whole shell, so they cannot see each other's ids.
import QtQuick
import com.blitzfc.qbz
import "../theme"
Item {
id: root
anchors.fill: parent
visible: QbzShell.artPreviewShow && QbzPlayer.npHasTrack
QbzTheme { id: theme }
// 232 = the 220 image plus a 6px frame on each side (ArtPreviewOverlay
// .slint:16 `card`).
readonly property int card: 232
Rectangle {
width: root.card
height: root.card
// Centred on the cover, clamped 8px inside the window, floating with
// its bottom edge 8px above the cover's top.
x: Math.max(8, Math.min(QbzShell.artPreviewX - width / 2,
Math.max(8, root.width - width - 8)))
y: QbzShell.artPreviewY - height - 8
radius: 8
color: theme.surfaceCard
border.width: 1
border.color: theme.borderSubtle
clip: true
// The .slint carries a 16px drop shadow; QtQuick's DropShadow is a
// shader effect and shader effects render NOTHING on this port's
// software path (the phase-2 finding that also killed ColorOverlay in
// QbzIcon), so the depth cue is a border instead of a fake blurred
// rectangle that would be wrong on both paths.
Image {
x: 6
y: 6
width: 220
height: 220
fillMode: Image.PreserveAspectFit
// D2 (contract 04 §4): the small feed is now the thumbnail
// variant (150px), too small for a 220px preview — prefer the
// size-resolved large feed, fall back to the small one. The
// request below asks with 2x DPR headroom (440 -> the 600
// bucket).
source: QbzPlayer.npArtworkPathLarge !== ""
? QbzPlayer.npArtworkPathLarge : QbzPlayer.npArtworkPath
asynchronous: true
smooth: true
mipmap: true
}
}
// Ask for the large feed while the preview is up (bucketed in Rust —
// repeated shows of the same track at the same size are no-ops).
onVisibleChanged: if (visible) QbzPlayer.requestNpArtworkSize(440)
}