feat(slint): favorites albums list view + grid/list toggle
Replace the bare favorites albums grid with the shared AlbumCollectionView plus a grid/list segmented toggle and a count line — adding the list view that was missing (the AlbumListRow extras now fill via the shared album_map). Album card actions (play / queue / favorite / go-to) route through a new album-action callback to the album media-action arms; the artist link still opens the artist. cc
This commit is contained in:
3 files changed
+72
-19
No files matched your search
@@ -4264,6 +4264,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
// Favorite album card actions (play / queue / favorite / go-to)
|
||||
// route through the album media-action arms.
|
||||
let weak = window.as_weak();
|
||||
window
|
||||
.global::<FavoritesActions>()
|
||||
.on_album_action(move |id, action| {
|
||||
if let Some(w) = weak.upgrade() {
|
||||
w.invoke_media_action("album".into(), id, action);
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
// Local search over the loaded favorite tracks (title / artist /
|
||||
// album), re-deriving the rendered list.
|
||||
|
||||
@@ -18,11 +18,12 @@ import {
|
||||
FavoriteLabelItem,
|
||||
DragState,
|
||||
} from "../state.slint";
|
||||
import { AlbumCard } from "../discover/AlbumCard.slint";
|
||||
import { AlbumCollectionView } from "../discover/AlbumCollectionView.slint";
|
||||
import { TrackRow } from "../primitives/TrackRow.slint";
|
||||
import { ExpandableSearch } from "../primitives/ExpandableSearch.slint";
|
||||
import { CircleAction } from "../primitives/CircleAction.slint";
|
||||
import { MultiSelectBar } from "../primitives/MultiSelectBar.slint";
|
||||
import { ToggleButton } from "../primitives/ToggleButton.slint";
|
||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||
import { ListScrollbar } from "../primitives/ListScrollbar.slint";
|
||||
@@ -458,30 +459,68 @@ export component FavoritesView inherits Rectangle {
|
||||
|
||||
// --- Albums tab --------------------------------------------
|
||||
if !FavoritesState.loading
|
||||
&& FavoritesState.active-tab == "albums": albums-grid := Rectangle {
|
||||
property <int> columns: Math.max(
|
||||
1,
|
||||
Math.floor((self.width + root.gap) / (root.album-w + root.gap)));
|
||||
property <int> rows: Math.ceil(FavoritesState.albums.length / self.columns);
|
||||
height: self.rows > 0
|
||||
? self.rows * root.album-h + Math.max(0, self.rows - 1) * root.gap
|
||||
: 0px;
|
||||
&& FavoritesState.active-tab == "albums": VerticalLayout {
|
||||
spacing: 0px;
|
||||
// Toolbar — count (left) + grid/list segmented toggle (right).
|
||||
if FavoritesState.albums.length > 0: HorizontalLayout {
|
||||
spacing: 8px;
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
horizontal-stretch: 1;
|
||||
Text {
|
||||
text: FavoritesState.albums.length + " " + @tr("albums");
|
||||
color: Theme.text-muted;
|
||||
font-size: Typography.legal;
|
||||
}
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
Rectangle {
|
||||
width: 68px;
|
||||
height: 34px;
|
||||
border-radius: 6px;
|
||||
background: Theme.surface-elevated;
|
||||
clip: true;
|
||||
HorizontalLayout {
|
||||
ToggleButton {
|
||||
icon: @image-url("../assets/icons/layout-grid.svg");
|
||||
active: FavoritesState.albums-view-mode == "grid";
|
||||
clicked => {
|
||||
FavoritesState.albums-view-mode = "grid";
|
||||
}
|
||||
}
|
||||
ToggleButton {
|
||||
icon: @image-url("../assets/icons/list.svg");
|
||||
active: FavoritesState.albums-view-mode == "list";
|
||||
clicked => {
|
||||
FavoritesState.albums-view-mode = "list";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if FavoritesState.albums.length > 0: Rectangle { height: 12px; }
|
||||
|
||||
if FavoritesState.albums.length == 0: Text {
|
||||
text: @tr("No favorite albums yet.");
|
||||
color: Theme.text-muted;
|
||||
font-size: Typography.body;
|
||||
}
|
||||
for album[i] in FavoritesState.albums: AlbumCard {
|
||||
x: Math.mod(i, albums-grid.columns) * (root.album-w + root.gap);
|
||||
y: Math.floor(i / albums-grid.columns) * (root.album-h + root.gap);
|
||||
width: root.album-w;
|
||||
height: root.album-h;
|
||||
album: album;
|
||||
clicked(id) => { root.open-album(id); }
|
||||
|
||||
AlbumCollectionView {
|
||||
albums: FavoritesState.albums;
|
||||
view-mode: FavoritesState.albums-view-mode;
|
||||
is-grouped: false;
|
||||
show-source: false;
|
||||
open-album(id) => {
|
||||
root.open-album(id);
|
||||
}
|
||||
open-artist(id) => {
|
||||
root.open-artist(id);
|
||||
}
|
||||
media-action(kind, id, action) => {
|
||||
if (kind == "artist") {
|
||||
root.open-artist(id);
|
||||
}
|
||||
FavoritesActions.album-action(id, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1457,6 +1457,7 @@ export global FavoritesState {
|
||||
in-out property <bool> tracks-multi-select: false;
|
||||
in property <int> tracks-selected-count: 0;
|
||||
in property <[AlbumCardItem]> albums: [];
|
||||
in-out property <string> albums-view-mode: "grid"; // "grid" | "list"
|
||||
in property <[FavoriteArtistItem]> artists: [];
|
||||
in property <[FavoritePlaylistItem]> playlists: [];
|
||||
in property <[FavoriteLabelItem]> labels: [];
|
||||
@@ -1483,6 +1484,7 @@ export global FavoritesActions {
|
||||
callback shuffle-tracks();
|
||||
callback toggle-multi-select();
|
||||
callback bulk-action(string /* select-all | remove-selected | clear */);
|
||||
callback album-action(string /* album id */, string /* action */);
|
||||
}
|
||||
|
||||
// A Qobuz mix detail page (DailyQ / WeeklyQ / FavQ / TopQ), opened
|
||||
|
||||
Reference in new issue
Block a user