Files
qbz/crates/qbz-qt/qml/kiosk/KioskShelf.qml
T
vicrodh b1dba198c9 feat(qt): kiosk profile release hardening
Integrate the Kiosk profile hardening round: 12 dedicated Kiosk views plus
22 shared opt-in hosts, shared navigation history with Back/Forward, Albums
as the default library tab, native paged Local Library (artists/tracks),
lightweight MyQBZ grid and detail, Ephemeral lifecycle, bounded lists and
artwork sized to the physical frame, static skeletons, 64px transport bar,
Settings touch reflow and a persistent Immersive exit.

Desktop keeps its prior behavior: every shared component defaults to the
desktop path and only opts into Kiosk behavior through explicit properties
(origin-relative ScrollMemory, viewport-bounded artwork requests).

Regression coverage: kiosk navigation and artwork Node tests added to the
runtime gate, scroll-origin fixtures for fixed and grouped rows, i18n
strings for all eight locales.

Verified by 750 release tests, six QML audits, Kiosk smokes at 800x480 and
1280x800 and a Desktop A/B with seven pixel-identical captures.

cc
2026-09-07 08:23:11 -06:00

65 lines
2.3 KiB
QML

// One horizontal viewport. Recycles cards; offscreen shelves never mount it.
import QtQuick
import com.blitzfc.qbz
ListView {
id: root
property var rows: []
property string kind: "album"
property real artSize: 128
property bool navFocused: false
property int cursor: 0
property real restoreX: 0
signal scrollSettled(real position)
function restorePosition() { if (!moving && restoreX > 0) contentX = restoreX }
Component.onCompleted: Qt.callLater(restorePosition)
onRestoreXChanged: Qt.callLater(restorePosition)
onMovementEnded: scrollSettled(contentX)
signal open(string id)
orientation: ListView.Horizontal
model: rows
spacing: 14
height: artSize + 58
clip: true
boundsBehavior: Flickable.StopAtBounds
cacheBuffer: 0
reuseItems: true
onCursorChanged: if (navFocused) positionViewAtIndex(cursor, ListView.Contain)
onNavFocusedChanged: if (navFocused) positionViewAtIndex(cursor, ListView.Contain)
delegate: Loader {
id: slot
required property var modelData
required property int index
width: root.artSize
height: root.height
active: true
sourceComponent: root.kind === "artist" ? artistCard : albumCard
KioskCoverSource {
id: cover
remote: slot.modelData.artUrl || ""
local: slot.modelData.artPath || slot.modelData.artwork || ""
edge: root.artSize
}
Component {
id: albumCard
KioskCard {
artSize: root.artSize
album: ({ id: slot.modelData.id || "", title: slot.modelData.title || "",
artist: slot.modelData.artist || "", artwork: cover.source,
qualityTier: slot.modelData.qualityTier || "" })
navFocused: root.navFocused && slot.index === root.cursor
onClicked: function(id) { root.open(id) }
}
}
Component {
id: artistCard
KioskArtistCard {
artSize: root.artSize
artist: ({ id: slot.modelData.id || "", title: slot.modelData.title || slot.modelData.name || "", artwork: cover.source })
navFocused: root.navFocused && slot.index === root.cursor
onClicked: function(id) { root.open(id) }
}
}
}
}