fix(slint): album view layout and a working track search

Four album-view corrections from review:

- Explicit badge: the title row now uses `alignment: start`, so the E
  badge sits right after the track name instead of being stretched out
  to a fake column position.
- Label/awards sidebar: dropped `alignment: start` on the track/sidebar
  row — it had defeated the track list's stretch and left the sidebar
  floating mid-row with dead space. The list now stretches and the
  254px sidebar is flush right; it rides the content region inward when
  a main sidebar opens.
- Track search: now a real client-side filter — a TextInput drives the
  AlbumActions.search global callback; album.rs keeps the unfiltered
  list and filter_tracks() matches title/artist case-insensitively.
- Description modal: the Flickable is vertical-stretch bounded so long
  text scrolls, and strip_html keeps paragraph breaks as newlines.

cc
This commit is contained in:
vicrodh committed 2026-05-18 19:51:47 -06:00
1 parent e766bed21a
commit 9bd8692518
5 files changed
+123 -28

No files matched your search

+49 -21
View File
@@ -9,7 +9,7 @@ import { Theme } from "../foundation/semantic-colors.slint";
import { Typography } from "../foundation/typography.slint";
import { Radius } from "../foundation/radius.slint";
import { Spacing } from "../foundation/spacing.slint";
import { AlbumState, AlbumTrackItem, NavState, ContentView } from "../state.slint";
import { AlbumState, AlbumTrackItem, AlbumActions, NavState, ContentView } from "../state.slint";
import { QbzIcon } from "../primitives/QbzIcon.slint";
import { ContextMenu } from "../primitives/ContextMenu.slint";
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
@@ -83,7 +83,11 @@ component TrackRow inherits Rectangle {
horizontal-stretch: 1;
spacing: 2px;
// alignment: start packs the title and the explicit badge at
// their natural sizes, so the badge sits right after the name
// instead of being stretched out to a column position.
HorizontalLayout {
alignment: start;
spacing: 6px;
Text {
text: root.track.title;
@@ -93,19 +97,21 @@ component TrackRow inherits Rectangle {
vertical-alignment: center;
overflow: elide;
}
if root.track.explicit: Rectangle {
width: 16px;
height: 16px;
y: (parent.height - self.height) / 2;
border-radius: 3px;
background: Theme.surface-elevated;
Text {
text: "E";
color: Theme.text-muted;
font-size: 9px;
font-weight: Typography.semibold;
horizontal-alignment: center;
vertical-alignment: center;
if root.track.explicit: VerticalLayout {
alignment: center;
Rectangle {
width: 16px;
height: 16px;
border-radius: 3px;
background: Theme.surface-elevated;
Text {
text: "E";
color: Theme.text-muted;
font-size: 9px;
font-weight: Typography.semibold;
horizontal-alignment: center;
vertical-alignment: center;
}
}
}
}
@@ -347,7 +353,6 @@ export component AlbumPageView inherits Rectangle {
// --- Album header ------------------------------------------------
HorizontalLayout {
spacing: 32px;
alignment: start;
Rectangle {
width: 224px;
@@ -498,9 +503,14 @@ export component AlbumPageView inherits Rectangle {
Rectangle { height: 8px; }
// --- Track list + label/awards sidebar --------------------------
// No `alignment: start` here: it would defeat the track list's
// horizontal-stretch and leave the sidebar floating mid-row
// with dead space to its right. The track list stretches and
// the 254px sidebar is pushed flush to the right edge; when a
// main right sidebar opens, the whole content region narrows
// and the sidebar rides along with it.
HorizontalLayout {
spacing: 32px;
alignment: start;
VerticalLayout {
horizontal-stretch: 1;
@@ -547,12 +557,27 @@ export component AlbumPageView inherits Rectangle {
tint: Theme.text-muted;
}
}
Text {
text: @tr("Search tracks...");
color: Theme.text-muted;
font-size: 13px;
vertical-alignment: center;
// Functional client-side filter.
Rectangle {
horizontal-stretch: 1;
track-search := TextInput {
width: 100%;
height: 100%;
color: Theme.text-primary;
font-size: 13px;
vertical-alignment: center;
single-line: true;
edited => {
AlbumActions.search(self.text);
}
}
Text {
visible: track-search.text == "";
text: @tr("Search tracks...");
color: Theme.text-muted;
font-size: 13px;
vertical-alignment: center;
}
}
}
}
@@ -723,7 +748,10 @@ export component AlbumPageView inherits Rectangle {
}
}
// vertical-stretch bounds the Flickable to the leftover
// panel height so a long description actually scrolls.
Flickable {
vertical-stretch: 1;
viewport-width: self.width;
viewport-height: desc-text.preferred-height;
desc-text := Text {
+1 -1
View File
@@ -10,7 +10,7 @@ import { LoginScreen } from "login/LoginScreen.slint";
import { AppShell } from "shell/AppShell.slint";
// Re-export the state globals so the Rust layer can populate them.
export { HomeState, AlbumState, ArtistState, NavState, SessionState, SettingsState } from "state.slint";
export { HomeState, AlbumState, ArtistState, NavState, SessionState, SettingsState, AlbumActions } from "state.slint";
// Which top-level screen is shown. The app starts on `splash` while it
// restores a saved session, then resolves to `shell` or `login`.
+6
View File
@@ -73,6 +73,12 @@ export global AlbumState {
in property <bool> loading: false;
}
// Album-view actions wired to Rust callbacks.
export global AlbumActions {
// Filter the visible track list by a query (client-side).
callback search(string);
}
// Which content view the shell shows.
export enum ContentView { home, album, artist, settings }