feat(slint): favorites tracks search + results count
Add an expandable search over the loaded favorite tracks (title / artist / album) plus a results-count line. tracks-visible (what the list renders) derives from the full tracks set: an empty query shares the model so artwork stays live, a query forks a filtered clone carrying the already-decoded artwork (the LabelState albums/visible pattern). Adds a no-match empty state. cc
This commit is contained in:
4 files changed
+91
-4
No files matched your search
@@ -20,6 +20,7 @@ import {
|
||||
} from "../state.slint";
|
||||
import { AlbumCard } from "../discover/AlbumCard.slint";
|
||||
import { TrackRow } from "../primitives/TrackRow.slint";
|
||||
import { ExpandableSearch } from "../primitives/ExpandableSearch.slint";
|
||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||
import { ListScrollbar } from "../primitives/ListScrollbar.slint";
|
||||
@@ -333,12 +334,48 @@ export component FavoritesView inherits Rectangle {
|
||||
if !FavoritesState.loading
|
||||
&& FavoritesState.active-tab == "tracks": VerticalLayout {
|
||||
spacing: 0px;
|
||||
// Toolbar — results count (left) + expandable search (right).
|
||||
if FavoritesState.tracks.length > 0: HorizontalLayout {
|
||||
spacing: 8px;
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
horizontal-stretch: 1;
|
||||
Text {
|
||||
text: FavoritesState.tracks-visible.length
|
||||
+ (FavoritesState.tracks-search != ""
|
||||
? " / " + FavoritesState.tracks.length
|
||||
: "")
|
||||
+ " " + @tr("tracks");
|
||||
color: Theme.text-muted;
|
||||
font-size: Typography.legal;
|
||||
}
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
ExpandableSearch {
|
||||
placeholder: @tr("Search");
|
||||
query <=> FavoritesState.tracks-search;
|
||||
edited(q) => {
|
||||
FavoritesActions.search-tracks(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if FavoritesState.tracks.length > 0: Rectangle { height: 12px; }
|
||||
|
||||
if FavoritesState.tracks.length == 0: Text {
|
||||
text: @tr("No favorite tracks yet.");
|
||||
color: Theme.text-muted;
|
||||
font-size: Typography.body;
|
||||
}
|
||||
for track[i] in FavoritesState.tracks: TrackRow {
|
||||
// Search filtered everything out.
|
||||
if FavoritesState.tracks.length > 0
|
||||
&& FavoritesState.tracks-visible.length == 0: Text {
|
||||
text: @tr("No tracks match your search.");
|
||||
color: Theme.text-muted;
|
||||
font-size: Typography.body;
|
||||
}
|
||||
for track[i] in FavoritesState.tracks-visible: TrackRow {
|
||||
track: track;
|
||||
index: i;
|
||||
show-artwork: true;
|
||||
|
||||
@@ -1447,8 +1447,13 @@ export global FavoritesState {
|
||||
// "tracks" | "albums" | "artists" | "playlists" | "labels"
|
||||
in-out property <string> active-tab: "tracks";
|
||||
// Tracks reuse TrackItem so the shared SearchTrackRow can
|
||||
// render them (artwork-on, per-row cover thumbnail).
|
||||
// render them (artwork-on, per-row cover thumbnail). `tracks` is the
|
||||
// full fetched set (the artwork pipeline targets it); `tracks-visible`
|
||||
// is what the list renders after the search filter (mirrors
|
||||
// LabelState.albums vs .visible).
|
||||
in property <[TrackItem]> tracks: [];
|
||||
in property <[TrackItem]> tracks-visible: [];
|
||||
in-out property <string> tracks-search: "";
|
||||
in property <[AlbumCardItem]> albums: [];
|
||||
in property <[FavoriteArtistItem]> artists: [];
|
||||
in property <[FavoritePlaylistItem]> playlists: [];
|
||||
@@ -1471,6 +1476,7 @@ export global FavoritesActions {
|
||||
callback open-playlist(string /* playlist id */);
|
||||
callback play-track(string /* track id */);
|
||||
callback track-action(string /* track id */, string /* action */);
|
||||
callback search-tracks(string /* query */);
|
||||
}
|
||||
|
||||
// A Qobuz mix detail page (DailyQ / WeeklyQ / FavQ / TopQ), opened
|
||||
|
||||
Reference in new issue
Block a user