Per the user's visual review. Replaces the reinvented MyQBZ controls with the app's existing primitives and resolves the per-item data left unresolved. Cross-cutting (all MyQBZ views): - Removed the per-view "Back" header (the app uses the global < > nav). - 32px L/R content padding so the ListScrollbar never rides over rows/cards. - Homologated page padding + Typography tokens to Favorites/LocalLibrary. - No more blue accent-filled pills / non-app colors. Component homologation (deleted the reinvented locals + dead code): - Grid/list + detail view-mode toggle -> the app ViewToggle (ToggleButton, surface-hover active, never accent). Builder ORDER-BY segmented de-blued too. - Sort/filter -> QbzSelect (sm); search -> ExpandableSearch (sm); "+ New" -> NewActionBtn (surface-elevated circle, single centered glyph). Header "+ New" hidden when empty so the empty-state CTA is the only one (no duplicate). - Deleted PrimaryCta / MyQbzSearch / ControlBtn / ViewSeg / ViewModeGroup / SortItem / ColMenuItem + the hand-rolled sort/filter popups + reset. Data (resolveItems, was deferred): - Quality column populated from the resolved tracks (QualityBadgeFull). - New ui/primitives/SourceGlyph.slint: full-color Qobuz/Plex glyphs + a text-muted local hard-drive -- distinguishes Local vs Plex, no accent blob. Reused in the detail + builder rows. - Type column shows the real release type (ALBUM/EP/SINGLE), not always ALBUM. - Local/Plex album art now loads in the detail rows (split artwork dispatch). - Collection grid card renders custom_artwork_path. - Builder rows: 44px + a hairline so quality badges read separated. Backend crates untouched. cargo check -p qbz-slint green. cc
164 lines
5.8 KiB
Plaintext
164 lines
5.8 KiB
Plaintext
// Shared MyQBZ index widgets — the grid card + list row + the two small
|
||
// app-homologated toolbar wrappers (the grid/list ViewToggle and the "+ New"
|
||
// action button), reused 1:1 by both MixtapesView and CollectionsView.
|
||
//
|
||
// HOMOLOGATION: the toolbar controls are the app's own primitives. The
|
||
// reinvented PrimaryCta (blue accent-filled pill), MyQbzSearch (fixed-width
|
||
// always-open box), ControlBtn (bordered button) and ViewSeg/ViewModeGroup
|
||
// (accent-filled segment) were DELETED — the index views now use
|
||
// ExpandableSearch + QbzSelect directly and the two small wrappers below
|
||
// (copied verbatim from Favorites/LocalLibrary). No accent-filled action pill.
|
||
|
||
import { Theme } from "../foundation/semantic-colors.slint";
|
||
import { Typography } from "../foundation/typography.slint";
|
||
import { Radius } from "../foundation/radius.slint";
|
||
import { MixtapeCardItem } from "../state.slint";
|
||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||
import { ToggleButton } from "../primitives/ToggleButton.slint";
|
||
import { CollectionMosaic } from "CollectionMosaic.slint";
|
||
|
||
// --- "+ New" action button ----------------------------------------------
|
||
// The app's secondary header action idiom (LocalLibrary's gear CircleIconBtn):
|
||
// a 28×28 surface-elevated circle with a plus glyph — NEVER an accent fill.
|
||
// Replaces the deleted blue PrimaryCta.
|
||
export component NewActionBtn inherits Rectangle {
|
||
callback clicked();
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 14px;
|
||
background: ta.has-hover ? Theme.surface-hover : Theme.surface-elevated;
|
||
QbzIcon {
|
||
source: @image-url("../assets/icons/plus.svg");
|
||
width: 15px;
|
||
height: 15px;
|
||
x: Math.round((parent.width - self.width) / 2 / 1px) * 1px;
|
||
y: Math.round((parent.height - self.height) / 2 / 1px) * 1px;
|
||
tint: Theme.text-primary;
|
||
}
|
||
ta := TouchArea {
|
||
mouse-cursor: pointer;
|
||
clicked => { root.clicked(); }
|
||
}
|
||
}
|
||
|
||
// --- Grid/list view toggle ----------------------------------------------
|
||
// Verbatim from FavoritesView/LocalLibraryView's `ViewToggle`: a 60×30
|
||
// surface-elevated rounded box holding two `ToggleButton { sm: true }`.
|
||
// ADR-008: small-radius rectangle, NOT a pill; active = surface-hover, never
|
||
// accent.
|
||
export component ViewToggle inherits Rectangle {
|
||
in property <string> view; // "grid" | "list"
|
||
callback set-view(string);
|
||
width: 60px;
|
||
height: 30px;
|
||
border-radius: 6px;
|
||
background: Theme.surface-elevated;
|
||
clip: true;
|
||
HorizontalLayout {
|
||
ToggleButton {
|
||
icon: @image-url("../assets/icons/list.svg");
|
||
active: root.view == "list";
|
||
sm: true;
|
||
clicked => { root.set-view("list"); }
|
||
}
|
||
ToggleButton {
|
||
icon: @image-url("../assets/icons/layout-grid.svg");
|
||
active: root.view == "grid";
|
||
sm: true;
|
||
clicked => { root.set-view("grid"); }
|
||
}
|
||
}
|
||
}
|
||
|
||
// --- Grid card ----------------------------------------------------------
|
||
// 208px column; 12px padding -> 184px mosaic. Muted subtitle (Typography.legal,
|
||
// no accent eyebrow) + 2-line name (Typography.body) + album-count meta.
|
||
export component MyQbzCard inherits Rectangle {
|
||
in property <MixtapeCardItem> item;
|
||
callback clicked();
|
||
width: 208px;
|
||
height: 184px + 12px + 12px + 18px + 2 * 20px + 18px + 12px;
|
||
border-radius: 8px;
|
||
background: card-ta.has-hover ? Theme.surface-hover : Theme.surface-card;
|
||
border-width: 1px;
|
||
border-color: card-ta.has-hover ? Theme.surface-hover : Theme.surface-elevated;
|
||
VerticalLayout {
|
||
padding: 12px;
|
||
spacing: 8px;
|
||
alignment: start;
|
||
CollectionMosaic {
|
||
item: root.item;
|
||
size: 184px;
|
||
}
|
||
Rectangle { height: 2px; }
|
||
// card-name — 2-line clamp (reserve 2 lines so cards align).
|
||
Text {
|
||
text: root.item.name;
|
||
color: Theme.text-primary;
|
||
font-size: Typography.body;
|
||
font-weight: Typography.semibold;
|
||
wrap: word-wrap;
|
||
overflow: elide;
|
||
height: 40px;
|
||
width: 100%;
|
||
}
|
||
// Muted subtitle: kind label + count (no uppercase accent eyebrow).
|
||
Text {
|
||
text: root.item.label + " · " + root.item.meta;
|
||
color: Theme.text-muted;
|
||
font-size: Typography.legal;
|
||
}
|
||
}
|
||
card-ta := TouchArea {
|
||
mouse-cursor: pointer;
|
||
clicked => { root.clicked(); }
|
||
}
|
||
}
|
||
|
||
// --- List row -----------------------------------------------------------
|
||
// 48px cover + name (single-line ellipsis) + sub (label · count).
|
||
export component MyQbzListRow inherits Rectangle {
|
||
in property <MixtapeCardItem> item;
|
||
callback clicked();
|
||
height: 64px;
|
||
border-radius: 6px;
|
||
background: row-ta.has-hover ? Theme.surface-hover : transparent;
|
||
HorizontalLayout {
|
||
padding-left: 12px;
|
||
padding-right: 12px;
|
||
spacing: 12px;
|
||
alignment: start;
|
||
VerticalLayout {
|
||
alignment: center;
|
||
CollectionMosaic {
|
||
item: root.item;
|
||
size: 48px;
|
||
}
|
||
}
|
||
VerticalLayout {
|
||
alignment: center;
|
||
spacing: 2px;
|
||
horizontal-stretch: 1;
|
||
Text {
|
||
text: root.item.name;
|
||
color: Theme.text-primary;
|
||
font-size: Typography.body;
|
||
font-weight: Typography.semibold;
|
||
overflow: elide;
|
||
width: 100%;
|
||
}
|
||
Text {
|
||
text: root.item.label + " · " + root.item.meta;
|
||
color: Theme.text-muted;
|
||
font-size: Typography.legal;
|
||
overflow: elide;
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
row-ta := TouchArea {
|
||
mouse-cursor: pointer;
|
||
clicked => { root.clicked(); }
|
||
}
|
||
}
|