feat(slint): A-Z jump strip for favorites tracks + albums grouping

Add an AlphaStrip overlaid on the right edge when grouping
alphabetically: tracks "Group: Name" (jumps the virtualized ListView to
the first row of a letter — accurate, uniform 50px rows) and albums
"Group: A-Z" (jumps the grid Flickable proportionally by section ordinal
— approximate, always lands the section in view, per the agreed
ArtistView-style behavior). derive_tracks/derive_albums build the
per-letter jump model (tracks-alpha / albums-alpha).

cc
This commit is contained in:
vicrodh committed 2026-05-27 10:38:34 -06:00
1 parent 4465a36716
commit dbd6d2e4b5
3 files changed
+97 -1

No files matched your search

@@ -19,6 +19,7 @@ import {
DragState,
GenreFilterState,
GenreFilterActions,
AlphaJump,
} from "../state.slint";
import { AlbumCollectionView } from "../discover/AlbumCollectionView.slint";
import { GenreFilterPopup } from "../discover/GenreFilterPopup.slint";
@@ -366,6 +367,37 @@ component ViewToggle inherits Rectangle {
}
}
// Vertical A-Z jump strip overlaid on the right edge of a grouped list /
// grid. Emits jump(ordinal, index); the host scrolls its list/flickable.
component AlphaStrip inherits Rectangle {
in property <[AlphaJump]> jumps;
callback jump(int /* ordinal */, int /* index */);
width: 18px;
VerticalLayout {
alignment: center;
spacing: 0px;
for j[k] in root.jumps: Rectangle {
height: 15px;
width: 18px;
Text {
text: j.letter;
color: la.has-hover ? Theme.accent : Theme.text-muted;
font-size: 9px;
font-weight: Typography.semibold;
horizontal-alignment: center;
vertical-alignment: center;
}
la := TouchArea {
mouse-cursor: pointer;
clicked => {
root.jump(k, j.index);
}
}
}
}
}
export component FavoritesView inherits Rectangle {
callback open-album(string);
callback open-artist(string);
@@ -740,6 +772,18 @@ export component FavoritesView inherits Rectangle {
visible-height: track-list.visible-height;
viewport-y <=> track-list.viewport-y;
}
// A-Z jump strip (name grouping) — accurate (uniform rows).
if FavoritesState.tracks-group-mode == "name"
&& FavoritesState.tracks-alpha.length > 0: AlphaStrip {
x: parent.width - self.width - 20px;
height: 100%;
jumps: FavoritesState.tracks-alpha;
jump(ord, idx) => {
track-list.viewport-y = -Math.min(
idx * 50px / 1px,
Math.max(0, (track-list.viewport-height - track-list.visible-height) / 1px)) * 1px;
}
}
}
}
@@ -866,6 +910,18 @@ export component FavoritesView inherits Rectangle {
visible-height: other-flick.height;
viewport-y <=> other-flick.viewport-y;
}
// A-Z jump strip (albums alpha grouping) — proportional jump.
if FavoritesState.active-tab == "albums"
&& FavoritesState.albums-group-mode == "alpha"
&& FavoritesState.albums-alpha.length > 0: AlphaStrip {
x: parent.width - self.width - 20px;
height: 100%;
jumps: FavoritesState.albums-alpha;
jump(ord, idx) => {
other-flick.viewport-y = -(ord / Math.max(1, FavoritesState.albums-alpha.length - 1))
* Math.max(0, (other-flick.viewport-height - other-flick.height) / 1px) * 1px;
}
}
}
}
}