fix(slint): restore Popular Tracks buttons, wire Play All, checkbox left

Three follow-ups to the body-row restructure.

Popular Tracks header:
  - The header HorizontalLayout had `alignment: space-between` but
    no horizontal-stretch on the title cell, so the action cluster
    (Play / Select / More) collapsed to zero width inside the new
    main-col container. Now uses an explicit horizontal-stretch on
    the title cell with the action cluster pinned to its preferred
    width, so the three buttons reappear on the right.

Play All wired:
  - ArtistState gains an `id` property, set in navigate_artist
    before the page data loads. The Play CircleAction now passes
    `ArtistState.id` through the media-action callback as the
    artist id payload.
  - main.rs routes `("artist", "play-top")` to a new
    playback::play_artist_top_tracks, which re-fetches the artist
    page (so the queue carries the same audio metadata the page
    row uses), builds QueueTracks from page.top_tracks, set_queues
    them, and starts audio on the first one. Other artist-row
    actions (Select / More) stay as their current placeholders for
    now.

Multi-select layout:
  - Per-track checkboxes move from the right side of the row to
    the leading column, replacing the play-cell when multi-select
    is on (so the checkbox stays vertically aligned with the same
    column the play number normally sits in). This is the project
    standard the user called out — checkboxes belong on the
    leading edge.
  - When multi-select is on, a "Select all" check-all row appears
    immediately above the track list, also leading-aligned. The
    visual click emits media-action("artist", "",
    "select-all-top-tracks") — handler stub will land with the
    rest of the selection wiring.

cc
This commit is contained in:
vicrodh committed 2026-05-20 13:13:56 -06:00
1 parent ed8bf0054d
commit 63295583b7
4 files changed
+205 -43

No files matched your search

+96 -42
View File
@@ -97,16 +97,50 @@ component TrackRow inherits Rectangle {
padding-right: 12px;
spacing: 16px;
// Leading cell — track number / play overlay normally,
// selection checkbox when multi-select is on. The cell width
// stays the same so the rest of the row does not shift.
VerticalLayout {
alignment: center;
play-cell := TrackPlayCell {
track-id: root.track.id;
number: root.track.number;
show-artwork: false;
number-width: 28px;
row-hovered: root.row-hovered;
play => {
root.media-action("track", root.track.id, "play");
Rectangle {
width: 28px;
height: 28px;
play-cell := TrackPlayCell {
track-id: root.track.id;
number: root.track.number;
show-artwork: false;
number-width: 28px;
row-hovered: root.row-hovered && !root.multi-select-mode;
visible: !root.multi-select-mode;
play => {
root.media-action("track", root.track.id, "play");
}
}
if root.multi-select-mode: Rectangle {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
width: 22px;
height: 22px;
border-radius: 11px;
background: root.track.selected ? Theme.accent : transparent;
border-width: root.track.selected ? 0px : 1.5px;
border-color: select-ta.has-hover ? Theme.text-primary : Theme.text-muted;
animate background { duration: 120ms; }
QbzIcon {
source: @image-url("../assets/icons/check.svg");
width: 14px;
height: 14px;
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
tint: #ffffff;
visible: root.track.selected;
}
select-ta := TouchArea {
mouse-cursor: pointer;
clicked => {
root.media-action("track", root.track.id, "toggle-select");
}
}
}
}
}
@@ -140,37 +174,6 @@ component TrackRow inherits Rectangle {
vertical-alignment: center;
}
// Select checkbox — only visible while the section is in
// multi-select mode (toggled by the section header's Select
// button). Mirrors Tauri's #if multiSelectMode gate.
if root.multi-select-mode: VerticalLayout {
alignment: center;
Rectangle {
width: 22px;
height: 22px;
border-radius: 11px;
background: root.track.selected ? Theme.accent : transparent;
border-width: root.track.selected ? 0px : 1.5px;
border-color: select-ta.has-hover ? Theme.text-primary : Theme.text-muted;
animate background { duration: 120ms; }
QbzIcon {
source: @image-url("../assets/icons/check.svg");
width: 14px;
height: 14px;
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
tint: #ffffff;
visible: root.track.selected;
}
select-ta := TouchArea {
mouse-cursor: pointer;
clicked => {
root.media-action("track", root.track.id, "toggle-select");
}
}
}
}
// More / context-menu button — always visible on Popular Tracks.
VerticalLayout {
alignment: center;
@@ -581,23 +584,24 @@ export component ArtistPageView inherits Rectangle {
// (Play all / Select-mode toggle / More menu). Mirrors Tauri's
// popular-tracks section-header.
if ArtistState.top-tracks.length > 0: HorizontalLayout {
alignment: space-between;
spacing: 12px;
VerticalLayout {
alignment: center;
horizontal-stretch: 1;
SectionTitle {
text: @tr("Popular Tracks");
}
}
HorizontalLayout {
alignment: end;
horizontal-stretch: 0;
spacing: 12px;
CircleAction {
icon: @image-url("../assets/icons/play-fill.svg");
primary: true;
diameter: 40px;
clicked => {
root.media-action("artist", "", "play-top");
root.media-action(
"artist", ArtistState.id, "play-top");
}
}
CircleAction {
@@ -615,6 +619,56 @@ export component ArtistPageView inherits Rectangle {
}
}
if ArtistState.top-tracks.length > 0: Rectangle { height: 10px; }
// Check-all row — shown above the list whenever the
// section is in multi-select mode. Matches the QBZ
// pattern: leading checkbox on the left of the row, just
// like the per-row selectors below.
if root.multi-select-mode
&& ArtistState.top-tracks.length > 0:
HorizontalLayout {
height: 36px;
padding-left: 12px;
padding-right: 12px;
spacing: 16px;
VerticalLayout {
alignment: center;
Rectangle {
width: 28px;
height: 28px;
check-all-box := Rectangle {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
width: 22px;
height: 22px;
border-radius: 11px;
background: transparent;
border-width: 1.5px;
border-color: check-all-ta.has-hover
? Theme.text-primary
: Theme.text-muted;
check-all-ta := TouchArea {
mouse-cursor: pointer;
clicked => {
root.media-action(
"artist", "", "select-all-top-tracks");
}
}
}
}
}
VerticalLayout {
alignment: center;
horizontal-stretch: 1;
Text {
text: @tr("Select all");
color: Theme.text-secondary;
font-size: Typography.body;
font-weight: Typography.medium;
}
}
}
for track[index] in ArtistState.top-tracks: TrackRow {
visible: root.top-tracks-expanded || index < root.top-tracks-preview;
height: self.visible ? 52px : 0px;
+1
View File
@@ -208,6 +208,7 @@ export struct SimilarEntry {
}
export global ArtistState {
in property <string> id;
in property <string> name;
in property <string> bio;
// Truncated bio (word-boundary cut + ellipsis) for the at-rest view;