Files
qbz/crates/qbz-qt/qml/shell/ArtPreviewOverlay.qml
T
vicrodh 8b26cda6f6 feat(qt): floating album-art preview on now-playing cover hover
Port of ArtPreviewOverlay.slint: hovering the small cover floats a 220px
preview above it. Missing in every bar mode except Large.

Non-interactive and mounted last in AppShell, both for the reason the
.slint records: the original used a PopupWindow and it stole the hover
from the trigger, so the preview flickered open and shut. An overlay that
cannot receive events cannot do that, and clicks pass through.

The anchor rides QbzShell like the drag ghost does -- trigger and overlay
are in different files with no shared id.

cc
2026-07-29 00:56:12 -06:00

72 lines
2.8 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
// The .slint prefers `artwork-large` and falls back to `artwork`;
// this port publishes only one cover path (player_bridge
// .np_artwork_path), which is the cached original — 600px for a
// Qobuz cover, so the 220px preview is a downscale either way.
source: QbzPlayer.npArtworkPath
asynchronous: true
smooth: true
mipmap: true
}
}
}