fix(qbz-qt): covers were never actually resized, plus the adversarial parity round
**The scaled-cover fix from the last round produced nothing.** The seam ran — 462 requests in one offscreen smoke — and wrote zero files, so the owner was right that nothing improved. The artwork cache stores files as `.img`, and `image::open` picks its decoder from the EXTENSION, so every call failed silently. Sniffing the content instead yields 101 derivatives on the same run. The header atmosphere had the identical bug and had therefore never produced an image either, which is why the flat fallback was all anyone ever saw. Reported as fixed last round without checking that it ran. Verified by effect this time, not by reading the diff. The rest came through an analyse / refute / implement / verify pipeline, and the adversarial passes earned their keep: - The sidebar mini rail: icons sat left of centre because their slot kept its fixed 24px width while the rail collapsed, so the hit target was rectangular and off-axis. The rail narrows, rows are square, and the mini state gains the folder flyout and tooltips it never had. The reviewer caught a comment claiming the width change was a one-token revert when it is two — corrected rather than left to mislead the next reader. - Playback context now travels from every producer, not the handful that happened to stamp it. - Collapsible per-tab search in Library, and label artwork. - Every View-all destination Home offers, plus the label page and its releases. - Collapsing the sidebar drops a Large bar back to New, live, without overwriting the user's persisted preference. cc
This commit is contained in:
34 files changed
+6234
-201
No files matched your search
@@ -0,0 +1,217 @@
|
||||
// Recently Played Albums + Most Played Albums — ONE view for the two
|
||||
// local-history "View all" pages, because discover/RecentAlbumsView.slint
|
||||
// and discover/MostPlayedAlbumsView.slint are the same page with five
|
||||
// differences (the .slint keeps them apart only because Slint has no
|
||||
// runtime-armed component). `mode` on the published document arms them:
|
||||
//
|
||||
// | recent | mostPlayed
|
||||
// title | "Recently Played Albums" | "Most Played Albums"
|
||||
// card height | 266 | 286 (the "{} plays" line)
|
||||
// search box | none | 240x34, right-anchored
|
||||
// root background | ambient-aware (:31) | surface-main (:26)
|
||||
// header backgrnd | surface-main (:43) | surface-main (:34)
|
||||
//
|
||||
// Everything else is byte-identical, INCLUDING the loading block (spinner 36
|
||||
// + @tr("Loading…")) and the empty msgid.
|
||||
//
|
||||
// NO OFFLINE GATE, deliberately: AppShell.slint mounts both routes while
|
||||
// offline (:456-470) — neither is in `qobuz-view-blocked` (:112-136) — because
|
||||
// both read a LOCAL store. Rendering QbzOfflinePlaceholder here would hide
|
||||
// data that is perfectly available.
|
||||
//
|
||||
// NAV BUTTONS: back/forward live in the shell header, so the title starts at
|
||||
// x: 48 (see DiscoverBrowseView.qml's note).
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import com.blitzfc.qbz
|
||||
import "../controls"
|
||||
import "../theme"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
QbzTheme { id: theme }
|
||||
|
||||
readonly property var doc: {
|
||||
try {
|
||||
return JSON.parse(QbzHome.playHistoryJson)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
// The document is the source of truth; `currentView` only covers the
|
||||
// frame before the first publish lands.
|
||||
readonly property string mode: doc.mode
|
||||
|| (QbzShell.currentView === "mostplayedalbums" ? "mostPlayed" : "recent")
|
||||
readonly property bool mostPlayed: mode === "mostPlayed"
|
||||
readonly property var items: doc.items || []
|
||||
|
||||
readonly property bool ambientOn: QbzShell.ambientMode > 0 && QbzPlayer.npHasTrack
|
||||
// RecentAlbumsView.slint:31 is ambient-aware; MostPlayedAlbumsView
|
||||
// .slint:26 is plain surface-main.
|
||||
color: (!mostPlayed && ambientOn) ? "transparent" : theme.surfaceMain
|
||||
radius: 12
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
// --- Fixed 56px header --------------------------------------------
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 56
|
||||
|
||||
// Unconditional surface-main on BOTH pages (:43 / :34) — these
|
||||
// two never take the ambient bar alpha the browse pages do.
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: theme.surfaceMain
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 48
|
||||
y: 25 - height / 2
|
||||
width: Math.max(0, parent.width - 48 - 32 - (root.mostPlayed ? 240 + 16 : 0))
|
||||
text: root.mostPlayed
|
||||
? QbzSession.tr("Most Played Albums", QbzSession.trRev)
|
||||
: QbzSession.tr("Recently Played Albums", QbzSession.trRev)
|
||||
color: theme.textPrimary
|
||||
font.pixelSize: theme.fontSection
|
||||
font.weight: theme.weightBold
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// Most Played only: a 240x34 title/artist filter. No clear-X in
|
||||
// the .slint (:59-104), so the shared search arm is not used here.
|
||||
Rectangle {
|
||||
visible: root.mostPlayed
|
||||
x: parent.width - width - 32
|
||||
y: 25 - height / 2
|
||||
width: 240
|
||||
height: 34
|
||||
radius: 6
|
||||
border.width: 1
|
||||
border.color: theme.borderSubtle
|
||||
color: theme.surfaceElevated
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 8
|
||||
QbzIcon {
|
||||
name: "search"
|
||||
width: 14
|
||||
height: 14
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
tintName: "muted"
|
||||
}
|
||||
Item {
|
||||
width: parent.width - 14 - 8
|
||||
height: parent.height
|
||||
clip: true
|
||||
TextInput {
|
||||
id: mpSearch
|
||||
anchors.fill: parent
|
||||
color: theme.textPrimary
|
||||
font.pixelSize: theme.fontBody
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
selectByMouse: true
|
||||
onTextEdited: QbzHome.mostPlayedFilter(text)
|
||||
// The opener resets the box in the same hop that
|
||||
// raises `loading`; re-seed while not editing.
|
||||
Binding {
|
||||
target: mpSearch
|
||||
property: "text"
|
||||
value: root.doc.search || ""
|
||||
when: !mpSearch.activeFocus
|
||||
}
|
||||
}
|
||||
Text {
|
||||
visible: mpSearch.text === ""
|
||||
anchors.fill: parent
|
||||
text: QbzSession.tr("Search…", QbzSession.trRev)
|
||||
color: theme.textMuted
|
||||
font.pixelSize: theme.fontBody
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Scrolling grid ------------------------------------------------
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height - 56
|
||||
|
||||
Flickable {
|
||||
id: flick
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
contentWidth: width
|
||||
contentHeight: page.implicitHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Column {
|
||||
id: page
|
||||
width: parent.width
|
||||
leftPadding: 32
|
||||
rightPadding: 32
|
||||
topPadding: 8
|
||||
bottomPadding: 100
|
||||
spacing: 0
|
||||
|
||||
// Spinner + "Loading…" — identical on both pages.
|
||||
Column {
|
||||
visible: QbzHome.playHistoryLoading
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 12
|
||||
QbzSpinner {
|
||||
size: 36
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: QbzSession.tr("Loading…", QbzSession.trRev)
|
||||
color: theme.textMuted
|
||||
font.pixelSize: 14
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: !QbzHome.playHistoryLoading && root.items.length === 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: QbzSession.tr("Albums you play will appear here.", QbzSession.trRev)
|
||||
color: theme.textMuted
|
||||
font.pixelSize: 14
|
||||
}
|
||||
|
||||
AlbumCollection {
|
||||
visible: !QbzHome.playHistoryLoading
|
||||
width: parent.width - 64
|
||||
albums: root.items
|
||||
viewMode: "grid"
|
||||
cardWidth: 200
|
||||
// The 20px difference IS the "{} plays" line.
|
||||
cardHeight: root.mostPlayed ? 286 : 266
|
||||
cardGap: 24
|
||||
showPlays: root.mostPlayed
|
||||
flick: flick
|
||||
contentOffset: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QbzScrollBar {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
target: flick
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user