Three views instantiated components the phase-23 directory move had left unimportable: PlaylistView and SearchView use TrackRow (now in rows/) and SectionRail uses AlbumCard (now in cards/). QML resolves lazily, so these survived cargo check, the test suite and the offscreen smoke — they only failed when the user opened a playlist. Adds a repo-wide audit of every instantiated component against the importing file's visible directories; the three above were all of them. The ambient field and the new dock band froze on lost focus. That is wrong on any tiling or mosaic desktop, where windows stay fully visible while unfocused — the field would stop while the user is looking at it. Both now gate on the window not being minimized or hidden, which is the real "nobody can see this". Freezing on not-visible stays. Dock band render cost, in descending order of impact: the drain publishes only the stream the active mode consumes (Bars mode was marshalling a 512-float waveform QList 30 times a second for nothing); each mode marshals its QList into JS once per tick instead of once per delegate; one shared Gradient replaces 28 per-delegate instances; and the per-bar Behaviors are gone since qbz-audio already smooths and clamps the amplitudes upstream. 18/18 tests; offscreen smoke clean; component-resolution audit clean. cc
87 lines
2.9 KiB
QML
87 lines
2.9 KiB
QML
// Horizontal album rail (Carousel.slint) — extracted for reuse by the
|
|
// detail views (AlbumView / ArtistView): section header + page chevrons +
|
|
// clipped ListView of the shared AlbumCard, with Cider-style edge fades.
|
|
|
|
import QtQuick
|
|
import com.blitzfc.qbz
|
|
import "../controls"
|
|
import "../cards"
|
|
import "../theme"
|
|
|
|
Column {
|
|
id: root
|
|
property string title: ""
|
|
property var items: []
|
|
// The host's url-keyed cover map ({url: file://path}).
|
|
property var coverMap: ({})
|
|
|
|
QbzTheme { id: theme }
|
|
width: parent ? parent.width : 0
|
|
spacing: 12
|
|
|
|
readonly property int perPage: Math.max(1, Math.floor((rail.width + 32) / 232))
|
|
readonly property int step: perPage * 232
|
|
readonly property real maxScroll: Math.max(0, rail.contentWidth - rail.width)
|
|
|
|
|
|
|
|
QbzSectionHeader {
|
|
title: root.title
|
|
leftEnabled: rail.contentX > 1
|
|
rightEnabled: rail.contentX < root.maxScroll - 1
|
|
onPageLeft: rail.contentX = Math.max(0, rail.contentX - root.step)
|
|
onPageRight: rail.contentX = Math.min(root.maxScroll, rail.contentX + root.step)
|
|
}
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: 246
|
|
ListView {
|
|
id: rail
|
|
anchors.fill: parent
|
|
orientation: ListView.Horizontal
|
|
spacing: 32
|
|
clip: true
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
model: root.items
|
|
delegate: AlbumCard {
|
|
albumId: modelData.id
|
|
title: modelData.title
|
|
artist: modelData.artist
|
|
artistId: modelData.artistId
|
|
genre: modelData.genre
|
|
year: modelData.year
|
|
qualityTier: modelData.qualityTier
|
|
artSource: root.coverMap[modelData.artUrl] || ""
|
|
isFavorite: false
|
|
}
|
|
}
|
|
Rectangle {
|
|
anchors.left: parent.left
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
width: 56
|
|
opacity: rail.contentX > 1 ? 1.0 : 0.0
|
|
Behavior on opacity { NumberAnimation { duration: 150 } }
|
|
gradient: Gradient {
|
|
orientation: Gradient.Horizontal
|
|
GradientStop { position: 0.0; color: theme.surfaceMain }
|
|
GradientStop { position: 1.0; color: "transparent" }
|
|
}
|
|
}
|
|
Rectangle {
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
width: 56
|
|
opacity: rail.contentX < root.maxScroll - 1 ? 1.0 : 0.0
|
|
Behavior on opacity { NumberAnimation { duration: 150 } }
|
|
gradient: Gradient {
|
|
orientation: Gradient.Horizontal
|
|
GradientStop { position: 0.0; color: "transparent" }
|
|
GradientStop { position: 1.0; color: theme.surfaceMain }
|
|
}
|
|
}
|
|
}
|
|
}
|