Two halves of one feature: - Playlist custom covers (Qt-first; no Slint counterpart yet): the playlist header cover gets the cover menu (Add/Change/Remove) and the lightbox on left-click. The store is a NEW file, custom_playlist_covers.json — a `playlists` key inside the shared custom_artwork.json would be silently dropped the next time the Slint app wrote an override (its store round-trips only known keys). The override beats the playlist's own Qobuz graphic AND the member-cover mosaic on the header, the sidebar rows, the cards and the library grid. - Album custom covers now apply everywhere, not just the album header: doc builders route an album's art URL through cover_artwork_qt::prefer_album_cover (the override's path flows through the ordinary artwork pipeline — artwork_qt::classify's LocalFile arm resolves it synchronously, no QML change). Swept: album page carousels (more-from-artist / suggestions / Last.fm similar), artist page releases, search result cards, and the playlist mosaics (a member album with a custom cover contributes THAT art to the collage tiles). km
87 lines
2.7 KiB
QML
87 lines
2.7 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) {
|
|
// Builders hand over remote URLs, file:// urls, and (for custom
|
|
// covers) bare absolute paths — normalize here, once.
|
|
if (source.charAt(0) === "/") source = "file://" + 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
|
|
}
|
|
}
|
|
}
|