Slices 3 + 4 of the offline-cache port. Slice 3 (controller, offline_cache.rs): - remove_album (maintenance::remove_album_cached_tracks + library rows + live row update), redownload_track / redownload_album(failed_only) (via select_redownload_targets + reset_track_for_redownload + spawn), clear_all (purge), open_folder (xdg-open / open the cache dir). Slice 4 (manager + Settings): - OfflineManagerView.slint: a standalone route (NavEntry::OfflineManager -> ContentView.offline-manager, mounted in AppShell, mirrors PlaylistManager). Stats bar (tracks count / size / limit / usage bar + edit-limit GB input + open-folder + clear-all) over a scrolling album-header + track-row list with a spinning/check/alert status glyph and per-row re-download / remove. - offline_manager.rs: builds the album->track rollup + stats from index.db (get_all_tracks), pushes [OfflineRow] + stats to OfflineManagerState; set_limit (in-memory, session-scoped) + reload_soon (refresh after a mutation). - OfflineManagerActions wired in main.rs (open/refresh/remove/redownload/ set-limit/clear-all/open-folder/play). - Settings gains an "Offline" section (OfflineSettings.slint): Manage offline cache (-> the manager), Open cache folder, Clear all. The Local-Library rows (show-in-library, repair) arrive with the Local Library port. qbz-slint compiles clean. cc
120 lines
4.5 KiB
Plaintext
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, 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%;
|
|
}
|
|
}
|