feat(slint): go-to album/artist on local track menus; playlist header buttons homologated
Local-surface track menus gain Go to Album / Go to Artist (owner improvement - Tauri omits them on local rows), routed by source: local rows navigate via their album_group_key and artist name to the local album view and LocalLibrary Artists; plex rows resolve the content-hash plex album key (local_queue_track parity); qobuz_download rows recover their real Qobuz album/artist ids through get_track and open the Qobuz pages, falling back to the local destinations when the resolve fails so the click always lands. Entries hide when keys are absent (ephemeral, file:/broken:/unresolved rows). Playlist-detail sidecar rows opt in with the same routing, and the global go-to arms divert snapshot local/plex rows before the catalog parse (a local row id would have been mis-typed as a Qobuz catalog id). Playlist header drops its local CircleAction copy (transparent + 1px border ring) for the shared user-verified primitive (solid primary disc, translucent fills, dim-on-disabled) - all seven actions restyled, callbacks untouched. cc
This commit is contained in:
4 files changed
+219
-49
No files matched your search
@@ -8,6 +8,7 @@ import { Typography } from "../foundation/typography.slint";
|
||||
import { Radius } from "../foundation/radius.slint";
|
||||
import { PlaylistState, PlaylistActions, DragState, NavState, OfflineState, BulkAction } from "../state.slint";
|
||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { CircleAction } from "../primitives/CircleAction.slint";
|
||||
import { TrackRow } from "../primitives/TrackRow.slint";
|
||||
import { ExpandableSearch } from "../primitives/ExpandableSearch.slint";
|
||||
import { ListScrollbar } from "../primitives/ListScrollbar.slint";
|
||||
@@ -16,50 +17,6 @@ import { MultiSelectBar } from "../primitives/MultiSelectBar.slint";
|
||||
import { NavButtons } from "../shell/NavButtons.slint";
|
||||
import { ListView } from "std-widgets.slint";
|
||||
|
||||
// Header action button — homologated 1:1 with AlbumPageView's
|
||||
// CircleAction: a ghost circle (translucent fill + faint ring, never a
|
||||
// solid accent fill) so the playlist transport row matches the album
|
||||
// one. Adds `active` (accent-tinted glyph for toggled state, e.g.
|
||||
// favorite / select mode) and `enabled` (dimmed + non-interactive),
|
||||
// which the album header doesn't need but the playlist one does.
|
||||
component CircleAction inherits Rectangle {
|
||||
in property <image> icon;
|
||||
in property <bool> primary: false;
|
||||
in property <bool> active: false;
|
||||
in property <length> diameter: 40px;
|
||||
in property <bool> enabled: true;
|
||||
callback clicked();
|
||||
|
||||
width: root.diameter;
|
||||
height: root.diameter;
|
||||
border-radius: root.diameter / 2;
|
||||
opacity: root.enabled ? 1.0 : 0.4;
|
||||
// Ghost circle — translucent fill with a faint ring (matches the
|
||||
// Tauri transport controls + AlbumPageView).
|
||||
background: ta.has-hover ? Theme.surface-hover : transparent;
|
||||
border-width: 1px;
|
||||
border-color: ta.has-hover ? Theme.text-disabled : Theme.border-subtle;
|
||||
|
||||
QbzIcon {
|
||||
source: root.icon;
|
||||
width: root.primary ? 19px : 17px;
|
||||
height: root.primary ? 19px : 17px;
|
||||
x: Math.round((parent.width - self.width) / 2 / 1px) * 1px;
|
||||
y: Math.round((parent.height - self.height) / 2 / 1px) * 1px;
|
||||
tint: root.active
|
||||
? Theme.accent
|
||||
: (ta.has-hover ? Theme.text-primary : Theme.text-secondary);
|
||||
}
|
||||
ta := TouchArea {
|
||||
mouse-cursor: root.enabled ? pointer : default;
|
||||
clicked => {
|
||||
if (root.enabled) {
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One row in the Sort menu. The active field shows its direction arrow
|
||||
// (asc/desc); re-selecting it toggles direction.
|
||||
component SortOption inherits Rectangle {
|
||||
@@ -718,6 +675,16 @@ export component PlaylistView inherits Rectangle {
|
||||
// of rendering a no-op entry. Other surfaces keep
|
||||
// the default until their own audit.
|
||||
track-info-action: false;
|
||||
// Go to Album / Go to Artist on the local/plex
|
||||
// sidecar rows too (Qobuz rows already carry the
|
||||
// pair via the Qobuz block) — owner improvement
|
||||
// over Tauri. Only RESOLVED rows (numeric snapshot
|
||||
// ids) qualify: the "file:"/"broken:"/unresolved-
|
||||
// "plex:" fallbacks have no navigable album/artist
|
||||
// keys, so their entries stay hidden (no dead
|
||||
// clicks).
|
||||
local-goto-actions: (track.source == "local" || track.source == "plex")
|
||||
&& track.id.is-float();
|
||||
multi-select-mode: PlaylistState.multi-select-mode;
|
||||
media-action(kind, id, action) => {
|
||||
if (action == "play") {
|
||||
|
||||
@@ -23,6 +23,12 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
// `track-info-action` lets a surface HIDE the Track Info entry while
|
||||
// its target feature is missing there (spec §3.3) — defaults on so
|
||||
// other surfaces are untouched.
|
||||
// `go-album-action` / `go-artist-action` — the Go to album / Go to
|
||||
// artist pair, gated independently of the block so library surfaces
|
||||
// can show them on local/plex/qobuz_download rows too (owner
|
||||
// improvement over Tauri, which omits both on local rows; the Rust
|
||||
// arms route by row source). Default to the block so Qobuz surfaces
|
||||
// are unchanged.
|
||||
// The transient actions (play / next / queue) always show.
|
||||
in property <bool> qobuz-actions: true;
|
||||
in property <bool> favorite-action: root.qobuz-actions;
|
||||
@@ -30,6 +36,8 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
in property <bool> playlist-action: true;
|
||||
in property <bool> remove-from-playlist-action: false;
|
||||
in property <bool> track-info-action: true;
|
||||
in property <bool> go-album-action: root.qobuz-actions;
|
||||
in property <bool> go-artist-action: root.qobuz-actions;
|
||||
// The row's offline-cache status (TrackItem.cache-status): when cached
|
||||
// (3) the single "Make available offline" entry swaps for the
|
||||
// Refresh / Remove pair — Tauri's ready-state submenu, rendered flat
|
||||
@@ -38,15 +46,18 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
callback media-action(string, string, string);
|
||||
|
||||
width: 224px;
|
||||
// 3 transient + optional favorite/mixtape/add-to-playlist/remove + the
|
||||
// Qobuz block (5 fixed + 1-or-2 offline entries + optional track info).
|
||||
// 3 transient + optional favorite/mixtape/add-to-playlist/remove +
|
||||
// optional go-to pair + the Qobuz block (3 fixed + 1-or-2 offline
|
||||
// entries + optional track info).
|
||||
height: (3
|
||||
+ (root.favorite-action ? 1 : 0)
|
||||
+ (root.mixtape-action ? 1 : 0)
|
||||
+ (root.playlist-action ? 1 : 0)
|
||||
+ (root.remove-from-playlist-action ? 1 : 0)
|
||||
+ (root.go-album-action ? 1 : 0)
|
||||
+ (root.go-artist-action ? 1 : 0)
|
||||
+ (root.qobuz-actions
|
||||
? 5 + (root.cache-status == 3 ? 2 : 1) + (root.track-info-action ? 1 : 0)
|
||||
? 3 + (root.cache-status == 3 ? 2 : 1) + (root.track-info-action ? 1 : 0)
|
||||
: 0)) * 30px;
|
||||
|
||||
ContextMenu {
|
||||
@@ -149,14 +160,14 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
root.media-action("track", root.track-id, "uncache");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions: ContextMenuItem {
|
||||
if root.go-album-action: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/disc-3.svg");
|
||||
label: @tr("Go to album");
|
||||
clicked => {
|
||||
root.media-action("track", root.track-id, "go-to-album");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions: ContextMenuItem {
|
||||
if root.go-artist-action: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/user.svg");
|
||||
label: @tr("Go to artist");
|
||||
clicked => {
|
||||
|
||||
@@ -63,6 +63,16 @@ export component TrackRow inherits Rectangle {
|
||||
// Track Info menu entry visibility — a surface whose Track Info is
|
||||
// dead hides it (spec §3.3); default on keeps other surfaces unchanged.
|
||||
in property <bool> track-info-action: true;
|
||||
// Go to Album / Go to Artist on LOCAL rows (owner improvement over
|
||||
// Tauri, which omits both there): defaults ON for every persistent
|
||||
// row of a force-local-menu surface (LocalLibrary Tracks/folder
|
||||
// detail, local album detail — their Rust arms route by row source);
|
||||
// mixed surfaces (playlist detail) opt rows in explicitly, because
|
||||
// only RESOLVED local/plex sidecar rows have navigable keys (the
|
||||
// "file:"/"broken:"/unresolved fallbacks must keep the entries
|
||||
// hidden — no dead clicks).
|
||||
in property <bool> local-goto-actions: root.force-local-menu
|
||||
&& root.track.source != "ephemeral";
|
||||
// Custom-order reorder controls (up/down chevrons) at the leading
|
||||
// edge — used by the playlist custom sort.
|
||||
in property <bool> show-reorder: false;
|
||||
@@ -631,6 +641,12 @@ export component TrackRow inherits Rectangle {
|
||||
playlist-action: root.track.source != "ephemeral";
|
||||
remove-from-playlist-action: root.remove-from-playlist-action;
|
||||
track-info-action: root.track-info-action;
|
||||
// Go to album / artist: the Qobuz block's pair, ALSO enabled on
|
||||
// local-surface rows (local-goto-actions doc above) — the Rust
|
||||
// arms route by source (local album view / LocalLibrary artist
|
||||
// for local+plex, the real Qobuz pages for qobuz_download).
|
||||
go-album-action: self.qobuz-actions || root.local-goto-actions;
|
||||
go-artist-action: self.qobuz-actions || root.local-goto-actions;
|
||||
// Cached-state-aware offline entry (Refresh/Remove when cached) —
|
||||
// straight from the row model.
|
||||
cache-status: root.track.cache-status;
|
||||
|
||||
Reference in new issue
Block a user