Files
qbz/crates/qbz-slint/ui/app.slint
T
vicrodh c50ae17102 feat(locallibrary): nav scaffolding for the Local Library Slint view
Greenfield Local Library port, slice 1 — the view now mounts and navigates
end to end (empty placeholder body; per-tab data lands next):

- state.slint: `ContentView::local-library` + `LocalLibraryState`
  (active-tab / loading / load-error / per-category counts) +
  `LocalLibraryActions` (select-tab, open-settings). Re-exported from
  app.slint so the globals reach the Rust API.
- ui/locallibrary/LocalLibraryView.slint: header (title + stats + gear) +
  4-tab bar (Albums / Artists / Folders / Tracks) + placeholder body.
- shell: AppShell mounts the view on `local-library`; HeaderBar's
  Local Library menu items now carry routes (local-albums / -artists /
  -folders / -tracks) instead of being inert.
- nav.rs: `NavEntry::LocalLibrary { tab }` — per-tab history entry,
  mirroring Favorites / Discover (back/forward moves between tabs).
- src/local_library.rs: `LibTab` controller stub (route/tab-id parsing,
  round-trip tested).
- main.rs: apply_entry arm + `navigate_local_library` + header-route
  branch + in-view tab-bar (select-tab) and gear (open-settings ->
  Settings; management lives under Settings > Local Library) wiring.

qbz-slint cargo check green. Built blind — needs a runtime smoke test
(./scripts/slint-dev.sh): click the Local Library header menu, confirm the
view shows and the 4 tabs switch.

cc
2026-05-28 17:51:52 -06:00

120 lines
4.5 KiB
Plaintext

// QBZ Slint MVP — UI entry point.
//
// Stays thin: imports the feature screens and exports the single
// top-level window. No raw visual constants or domain logic here
// (MVP ADR, sections 13 and 21).
// Bundles + registers the Inter 18pt faces (compile-time embed) so the
// app can render with Inter — see the FONT_TEST_INTER toggle in main.rs.
import "assets/fonts/Inter_18pt-Regular.ttf";
import "assets/fonts/Inter_18pt-Medium.ttf";
import "assets/fonts/Inter_18pt-SemiBold.ttf";
import "assets/fonts/Inter_18pt-Bold.ttf";
import { Theme } from "foundation/semantic-colors.slint";
import { SplashScreen } from "splash/SplashScreen.slint";
import { LoginScreen } from "login/LoginScreen.slint";
import { AppShell } from "shell/AppShell.slint";
import { Toast } from "primitives/Toast.slint";
// Re-export the state globals so the Rust layer can populate them.
export { HomeState, HomeActions, DiscoverBrowseState, DiscoverBrowseActions, ForYouState, MixState, GenreFilterState, GenreFilterActions, AlbumState, ArtistState, NavState, SessionState, SettingsState, AlbumActions, ArtistActions, ArtworkActions, NowPlayingState, QueueState, SearchState, SearchActions, NetworkSidebarState, NetworkSidebarActions, MusicianState, MusicianActions, LabelState, LabelActions, LocationViewState, LocationViewActions, FavoritesState, FavoritesActions, PlaylistPickerState, PlaylistPickerActions, PlaylistState, PlaylistActions, SidebarState, SidebarActions, CreatePlaylistState, CreatePlaylistActions, EditPlaylistState, EditPlaylistActions, CreateFolderState, CreateFolderActions, DragState, DragActions, PlaylistManagerState, PlaylistManagerActions, OfflineManagerState, OfflineManagerActions, LocalLibraryState, LocalLibraryActions, FolderEditState, FolderEditActions, ToastState } 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`.
export enum AppScreen { splash, login, shell }
export component AppWindow inherits Window {
title: "QBZ";
preferred-width: 1180px;
preferred-height: 760px;
min-width: 940px;
min-height: 600px;
background: Theme.surface-main;
// Resolved by the Rust layer from the desktop environment (e.g. the
// KDE Plasma general font). Empty falls back to the Slint default.
in property <string> system-font: "";
default-font-family: root.system-font;
in-out property <AppScreen> screen: AppScreen.splash;
// Forwarded to the Rust layer.
callback sign-in-via-browser();
callback use-system-browser();
callback start-offline();
callback open-tos();
callback open-album(string);
callback open-artist(string);
callback media-action(string, string, string);
callback header-menu-navigate(string);
callback discover-view-all(string, string);
callback settings-bool(string, bool);
callback settings-select(string, int);
callback settings-slider(string, int);
callback settings-reset();
callback logout();
callback close-app();
if root.screen == AppScreen.splash: SplashScreen { }
if root.screen == AppScreen.login: LoginScreen {
sign-in-via-browser => {
root.sign-in-via-browser();
}
use-system-browser => {
root.use-system-browser();
}
start-offline => {
root.start-offline();
}
open-tos => {
root.open-tos();
}
}
if root.screen == AppScreen.shell: AppShell {
open-album(id) => {
root.open-album(id);
}
open-artist(id) => {
root.open-artist(id);
}
media-action(kind, id, action) => {
root.media-action(kind, id, action);
}
header-menu-navigate(route) => {
root.header-menu-navigate(route);
}
discover-view-all(e, t) => {
root.discover-view-all(e, t);
}
settings-bool(k, v) => {
root.settings-bool(k, v);
}
settings-select(k, i) => {
root.settings-select(k, i);
}
settings-slider(k, v) => {
root.settings-slider(k, v);
}
settings-reset => {
root.settings-reset();
}
logout => {
root.logout();
}
close-app => {
root.close-app();
}
}
// App-global in-app toast overlay. Declared last so it floats over
// every screen (splash / login / shell). Driven by ToastState via
// crate::toast.
Toast {
width: 100%;
height: 100%;
}
}