PLAYLISTS header: smaller title, recursive search (hides title), create modal at 1:1 with Tauri (name/description/folder/public), sort menu, and a collapse chevron. Folders: accent-tinted icon, open/close chevron as the primary affordance (no more purposeless "..."), count shown on hover only. Playlists render a 2x2 micro-collage of track covers (opt-out) instead of the glyph, sourced from the existing playlist images (no extra fetch). cc
70 lines
2.3 KiB
Plaintext
70 lines
2.3 KiB
Plaintext
// Sidebar playlist micro-collage. A tiny (20px) thumbnail of a
|
|
// playlist's track covers shown in place of the generic list-music
|
|
// glyph, mirroring Tauri's PlaylistCoverCollage:
|
|
// - 0 covers -> parent renders the fallback glyph (nothing here)
|
|
// - 1-3 covers -> a single cover (the first tile), full bleed
|
|
// - 4+ covers -> a 2x2 grid, 1px gap, 3px radius
|
|
//
|
|
// The covers + decoded images live on the SidebarEntry (cover-count,
|
|
// cover1..cover4). This component only lays them out.
|
|
|
|
import { Theme } from "../foundation/semantic-colors.slint";
|
|
import { SidebarEntry } from "../state.slint";
|
|
|
|
export component SidebarPlaylistCollage inherits Rectangle {
|
|
in property <SidebarEntry> entry;
|
|
in property <length> size: 20px;
|
|
|
|
width: root.size;
|
|
height: root.size;
|
|
border-radius: 3px;
|
|
clip: true;
|
|
background: Theme.surface-elevated;
|
|
|
|
property <int> n: root.entry.cover-count;
|
|
property <length> gap: 1px;
|
|
|
|
// 1-3 covers -> single full-bleed cover (Tauri: tiles.length < 4).
|
|
if root.n >= 1 && root.n < 4: Image {
|
|
width: 100%;
|
|
height: 100%;
|
|
source: root.entry.cover1;
|
|
image-fit: cover;
|
|
}
|
|
|
|
// 4+ covers -> 2x2 grid (Tauri: tiles.slice(0, 4)).
|
|
if root.n >= 4: VerticalLayout {
|
|
spacing: root.gap;
|
|
HorizontalLayout {
|
|
spacing: root.gap;
|
|
Image {
|
|
source: root.entry.cover1;
|
|
image-fit: cover;
|
|
width: (root.size - root.gap) / 2;
|
|
height: (root.size - root.gap) / 2;
|
|
}
|
|
Image {
|
|
source: root.entry.cover2;
|
|
image-fit: cover;
|
|
width: (root.size - root.gap) / 2;
|
|
height: (root.size - root.gap) / 2;
|
|
}
|
|
}
|
|
HorizontalLayout {
|
|
spacing: root.gap;
|
|
Image {
|
|
source: root.entry.cover3;
|
|
image-fit: cover;
|
|
width: (root.size - root.gap) / 2;
|
|
height: (root.size - root.gap) / 2;
|
|
}
|
|
Image {
|
|
source: root.entry.cover4;
|
|
image-fit: cover;
|
|
width: (root.size - root.gap) / 2;
|
|
height: (root.size - root.gap) / 2;
|
|
}
|
|
}
|
|
}
|
|
}
|