Files
qbz/crates/qbz-qt/qml/views/CoverLightbox.qml
T
vicrodh d9b1c26ff3 feat(qt): album page parity — cover menu + lightbox, header actions, album info modal
Four wired surfaces on AlbumView, all previously inert stubs:

- Cover menu (right-click, AlbumPageView.slint:300-370): Add/Change/Remove
  custom cover over the SAME custom_artwork.json store the Slint app uses
  (a cover set in one app shows in both), Open in browser, Save as…. Plus
  the NEW lightbox (menu entry + left-click): the art at its best available
  quality fitted to the window, custom override preferred.
- Header action row: radio (the existing foryou_qt album-radio seam),
  booklet (visible when a PDF goody exists; downloads via save-as — no
  in-app reader, like the reference), and add-to-mixtape/collection — the
  MyQBZ picker's Accepts engine already lets albums target Collections
  (the Tauri rule), so the button just feeds it.
- Album Info modal (the extensive one): fresh /album/get → credits grouped
  through the shared qbz_qobuz::performers parser, per-track copyright,
  review tab when the album has editorial text, label link, musician
  navigation, per-track play in album context. Tauri layout (left fixed
  meta column, right scrollable tabs) per the project ruling for this
  feature; the header's label link is wired along the way (LabelView has
  existed for a while — the POC-NOTE was stale).
- … menu: Add to playlist (the loaded view's track ids into the picker),
  Add to mixtape, Share Qobuz link, Share Album.link (UPC -> Deezer ->
  album.link, #514 path — the resolver half of share.rs is now ported for
  the album arm). The offline rows stay out: no Qt album-cache seam.

i18n: "View cover" + "Could not load the image" catalogued in all seven
locales. Gate: both QML audits + 305 tests + debug/release builds green.

km
2026-08-10 20:24:25 -06:00

84 lines
2.5 KiB
QML

// Cover lightbox — NEW in the Qt port (no Slint counterpart): the album art
// at its best available quality, fitted to the window. Opened by left-click
// on the AlbumView cover or by the cover menu's "View cover" entry.
//
// Shape follows TrackInfoModal: a full-window Popup parented to
// Overlay.overlay, a dim scrim whose click closes, Escape closes. The image
// asks for a decode capped at the window size (sourceSize), so a mega
// variant does not cost a full-res decode to display at ~90% of the window.
import QtQuick
import QtQuick.Controls
import com.blitzfc.qbz
import "../theme"
Popup {
id: root
parent: Overlay.overlay
x: 0
y: 0
width: parent ? parent.width : 0
height: parent ? parent.height : 0
padding: 0
closePolicy: Popup.CloseOnEscape
// file:// (custom cover) or the remote best() URL. Set through openWith.
property string artSource: ""
function openWith(source) {
root.artSource = source
root.open()
}
QbzTheme { id: theme }
background: Rectangle {
color: "#bf000000"
}
contentItem: Item {
// Scrim click closes; clicks on the image itself are swallowed so
// the lightbox stays up while the user inspects it.
MouseArea {
anchors.fill: parent
onClicked: root.close()
}
// Fit the LONGER window axis at 90%, so the cover never touches the
// rim. Covers are square; PreserveAspectFit keeps any odd one honest.
readonly property real side: Math.min(root.width, root.height) * 0.9
Image {
id: art
anchors.centerIn: parent
width: parent.side
height: parent.side
source: root.artSource
asynchronous: true
fillMode: Image.PreserveAspectFit
sourceSize: Qt.size(parent.side, parent.side)
smooth: true
visible: status === Image.Ready
MouseArea {
anchors.fill: parent
onClicked: {} // swallow — the scrim's close must not fire here
}
}
QbzSpinner {
anchors.centerIn: parent
visible: root.artSource !== "" && art.status === Image.Loading
}
Text {
anchors.centerIn: parent
visible: root.artSource !== "" && art.status === Image.Error
text: QbzSession.tr("Could not load the image", QbzSession.trRev)
color: theme.textSecondary
font.pixelSize: theme.fontBody
}
}
}