Files
qbz/crates/qbz-qt/qml/controls/QbzAlphaStrip.qml
T
vicrodh 0b6b111c74 fix(qt): correct playback commands and library browsing
Handle explicit MPRIS Play/Pause without toggling paused playback, remove the 10,000-favorite cutoff, and sort albums by original release dates. Isolate Library filters by tab, include purchased release dates, and support server-ranked artist popularity with safe pagination.

Validated with 766 Qt tests, QML checks, release-sort regressions, and a release build. Refs #750, #751.

cx
2026-09-08 18:27:51 -06:00

92 lines
4.0 KiB
QML

// Vertical A-Z jump strip (favorites/FavoritesView.slint:398 `AlphaStrip`;
// LocalLibraryView.slint:577 is a 1:1 lift of the same component). 18px
// gutter, 15px rows, 9px semibold muted letters that go accent on hover.
// Emits jump(ordinal, index); the host scrolls its own view — exactly like
// the Slint, which scrolls proportionally on the grouped grids (:1996) and by
// row index on the uniform track list (:1687).
//
// PROMOTED from views/local/AlphaStrip.qml, unchanged except for the import
// depth — same move QbzMultiSelectBar made and for the same reason: the Slint
// component was always shared (Local Library albums/artists/tracks AND the
// Library favorites tabs), so the "local" folder was a naming accident. The
// old views/local/AlphaStrip.qml is GONE; this file is the only copy.
// Call sites: views/local/{LocalAlbumsTab,LocalArtistsTab,LocalTracksTab}.qml,
// views/library/{LibraryAlbumsList}.qml and views/LibraryView.qml.
import QtQuick
import com.blitzfc.qbz
import "../theme"
Item {
id: root
/// [{ letter, index }] — index = the row ordinal in the flat model.
property var jumps: []
/// Paint the stable # A-Z affordance even when a query has no row for a
/// letter. Missing buckets are deliberately inert and dimmed; present
/// buckets retain their exact native/global row index.
property bool completeAlphabet: false
signal jump(int ordinal, int index)
function entryAt(position) {
if (!root.completeAlphabet)
return (root.jumps || [])[position] || ({ "letter": "", "index": -1 })
var targets = ({})
var source = root.jumps || []
for (var i = 0; i < source.length; i++) {
var letter = String(source[i].letter || "#").toUpperCase()
if (letter < "A" || letter > "Z") letter = "#"
if (targets[letter] === undefined)
targets[letter] = Number(source[i].index)
}
var letter = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(position)
return { "letter": letter,
"index": targets[letter] === undefined ? -1 : targets[letter] }
}
QbzTheme { id: theme }
width: 18
Column {
anchors.centerIn: parent
spacing: 0
Repeater {
// An integer model makes the full rail structural: it always
// creates exactly 27 delegates, regardless of when the async
// jump document lands or how a JS-array binding is invalidated.
model: root.completeAlphabet ? 27 : (root.jumps || []).length
delegate: Item {
id: cell
required property int index
readonly property var entry: root.entryAt(index)
width: root.width
// A complete rail is 27 entries (# + A-Z). Keep the reference
// 15px row when it fits, then compress uniformly on short
// windows instead of painting letters outside the viewport.
height: Math.max(1, Math.min(15,
root.height / Math.max(1, root.completeAlphabet
? 27 : (root.jumps || []).length)))
Text {
anchors.fill: parent
text: cell.entry.letter
color: cell.entry.index < 0 ? theme.alphaTier(24)
: cellArea.containsMouse ? theme.accent : theme.textMuted
font.pixelSize: Math.min(9, Math.max(7, parent.height - 1))
font.weight: theme.weightSemibold
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
MouseArea {
id: cellArea
anchors.fill: parent
enabled: cell.entry.index >= 0
hoverEnabled: true
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: root.jump(cell.index, cell.entry.index)
}
}
}
}
}