feat(slint): A-Z alpha index for the Artists sidepanel left list

The sidepanel left artist list is now always A-Z grouped (derive_artists
groups when group-enabled OR view-mode==sidepanel; the view toggle re-derives)
with letter headers, and an AlphaStrip on its right edge jumps the list
Flickable proportionally (same approach as the album grouping — not exact,
by design). Replaces the flat ListView.

cc
This commit is contained in:
vicrodh committed 2026-05-27 16:19:12 -06:00
1 parent 0b12d102ce
commit 81e7d21868
3 files changed
+44 -10

No files matched your search

+4 -1
View File
@@ -630,7 +630,10 @@ pub fn derive_artists(window: &AppWindow) {
let state = window.global::<FavoritesState>();
let query_owned = state.get_artists_search().to_lowercase();
let query = query_owned.trim();
let group = state.get_artists_group_enabled();
// The sidepanel left list is ALWAYS A-Z grouped (independent of the grid
// group toggle), so it shows letter headers + the alpha jump strip.
let group = state.get_artists_group_enabled()
|| state.get_artists_view_mode().as_str() == "sidepanel";
let all = state.get_artists();
// Flat (search-filtered) model. Share `all` when no query so artwork
+3
View File
@@ -4403,6 +4403,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if v == "grid" {
st.set_selected_artist_id("".into());
}
// Rebuild grouped/alpha for the new mode (the sidepanel
// left list is always grouped).
favorites::derive_artists(&w);
favorites_prefs::save(&w);
}
});
@@ -1368,20 +1368,48 @@ export component FavoritesView inherits Rectangle {
padding-top: 16px;
spacing: 0px;
// LEFT: the favorites artist list (self-scrolling).
// LEFT: the favorites artist list, A-Z grouped, with a
// proportional alpha jump strip (like the album grouping).
Rectangle {
width: 300px;
ListView {
width: 100%;
height: 100%;
for artist[i] in FavoritesState.artists-visible: SidepanelArtistRow {
item: artist;
selected: FavoritesState.selected-artist-id == artist.id;
clicked(id, name) => {
FavoritesActions.select-artist(id, name);
clip: true;
left-flick := Flickable {
viewport-width: self.width;
viewport-height: left-col.preferred-height;
left-col := VerticalLayout {
width: parent.width;
padding-top: 4px;
padding-bottom: 40px;
padding-right: 22px;
spacing: 4px;
alignment: start;
for section[s] in FavoritesState.artists-grouped: VerticalLayout {
spacing: 2px;
Text {
text: section.title;
color: Theme.text-secondary;
font-size: Typography.legal;
font-weight: Typography.semibold;
}
for artist[i] in section.artists: SidepanelArtistRow {
item: artist;
selected: FavoritesState.selected-artist-id == artist.id;
clicked(id, name) => {
FavoritesActions.select-artist(id, name);
}
}
}
}
}
if FavoritesState.artists-alpha.length > 0: AlphaStrip {
x: parent.width - self.width - 2px;
height: 100%;
jumps: FavoritesState.artists-alpha;
jump(ord, idx) => {
left-flick.viewport-y = -(ord / Math.max(1, FavoritesState.artists-alpha.length - 1))
* Math.max(0, (left-flick.viewport-height - left-flick.height) / 1px) * 1px;
}
}
}
Rectangle {
width: 1px;