feat(slint): Linux system tray (ksni) + Wayland-safe close-to-tray
Port the Tauri tray to Slint on Linux via ksni: themed icon (auto/mono/ color), left-click window toggle, middle-click play/pause, scroll volume, the 5-item menu, and a live tooltip reflecting the now-playing track. Tray actions call the playback controller directly (QConnect-aware) instead of emitting webview events. Settings reuse the shared qbz-app tray store (per-user) wired through AppearanceState. Window hide/show uses Slint window.hide()/show() (winit set_visible is a no-op on Wayland) and the loop runs via run_event_loop_until_quit so a tray-hide keeps the app alive. close-to-tray is honored on both the custom titlebar close and the WM close (on_close_requested). minimize-to-tray is hidden until custom window chrome exists. cc
This commit is contained in:
22 files changed
+1084
-26
No files matched your search
@@ -18,7 +18,7 @@ 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, ShellState, 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, SidebarFolderPopupState, CreatePlaylistState, CreatePlaylistActions, EditPlaylistState, EditPlaylistActions, CreateFolderState, CreateFolderActions, DragState, DragActions, PlaylistManagerState, PlaylistManagerActions, OfflineManagerState, OfflineManagerActions, LocalLibraryState, LocalLibraryActions, LibraryFoldersState, LibFolderEditState, LibraryManageActions, LibraryScanState, LibAlbumFilterState, LocalAlbumState, LocalAlbumActions, TagEditorState, TagEditorActions, FolderEditState, FolderEditActions, ToastState, TextUtil, QconnectDevState, QconnectDevice } from "state.slint";
|
||||
export { HomeState, HomeActions, DiscoverBrowseState, DiscoverBrowseActions, ForYouState, MixState, GenreFilterState, GenreFilterActions, AlbumState, ArtistState, NavState, ShellState, 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, SidebarFolderPopupState, CreatePlaylistState, CreatePlaylistActions, EditPlaylistState, EditPlaylistActions, CreateFolderState, CreateFolderActions, DragState, DragActions, PlaylistManagerState, PlaylistManagerActions, OfflineManagerState, OfflineManagerActions, LocalLibraryState, LocalLibraryActions, LibraryFoldersState, LibFolderEditState, LibraryManageActions, LibraryScanState, LibAlbumFilterState, LocalAlbumState, LocalAlbumActions, TagEditorState, TagEditorActions, FolderEditState, FolderEditActions, ToastState, TextUtil, QconnectDevState, QconnectDevice, AppearanceState } 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`.
|
||||
|
||||
@@ -700,12 +700,11 @@ export component AppearanceSettings inherits VerticalLayout {
|
||||
Rectangle { height: 12px; }
|
||||
|
||||
// ===================================================================
|
||||
// SYSTEM TRAY
|
||||
// SYSTEM TRAY (macOS: "MENU BAR")
|
||||
// ===================================================================
|
||||
GroupHeader { text: @tr("SYSTEM TRAY"); }
|
||||
GroupHeader { text: AppearanceState.is-macos ? @tr("MENU BAR") : @tr("SYSTEM TRAY"); }
|
||||
|
||||
SettingRow {
|
||||
unsupported: true;
|
||||
label: @tr("Enable tray icon");
|
||||
description: @tr("Show icon in system tray (requires restart)");
|
||||
QbzToggle {
|
||||
@@ -716,24 +715,18 @@ export component AppearanceSettings inherits VerticalLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: a separate "Minimize to tray" toggle is intentionally absent.
|
||||
// Redirecting the window-manager minimize button to the tray requires
|
||||
// owning that button (custom window chrome), which is not built yet, and
|
||||
// Wayland forbids a client from intercepting the compositor's minimize.
|
||||
// Until custom chrome lands, the option is HIDDEN (not shown disabled) and
|
||||
// this row reads "Close to tray". When custom chrome exists, merge the two
|
||||
// into a single "Minimize/Close to tray" control.
|
||||
SettingRow {
|
||||
unsupported: true;
|
||||
label: @tr("Minimize to tray");
|
||||
description: @tr("Hide window to tray when minimizing");
|
||||
enabled: AppearanceState.tray-enable;
|
||||
QbzToggle {
|
||||
checked: AppearanceState.tray-minimize-to-tray;
|
||||
enabled: AppearanceState.tray-enable;
|
||||
toggled(v) => {
|
||||
AppearanceState.tray-minimize-to-tray = v;
|
||||
AppearanceState.appearance-bool("tray-minimize-to-tray", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingRow {
|
||||
unsupported: true;
|
||||
label: @tr("Close to tray");
|
||||
description: @tr("Hide window to tray instead of quitting");
|
||||
description: AppearanceState.is-macos
|
||||
? @tr("Keep playing in the menu bar instead of quitting when you close the window")
|
||||
: @tr("Keep playing in the tray instead of quitting when you close the window");
|
||||
enabled: AppearanceState.tray-enable;
|
||||
QbzToggle {
|
||||
checked: AppearanceState.tray-close-to-tray;
|
||||
@@ -744,8 +737,23 @@ export component AppearanceSettings inherits VerticalLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
// macOS-only: switch the activation policy to .accessory while closed to
|
||||
// the menu bar (no Dock icon). Off = Spotify-style (keep the Dock icon).
|
||||
SettingRow {
|
||||
visible: AppearanceState.is-macos;
|
||||
label: @tr("Hide Dock icon when closed to menu bar");
|
||||
description: @tr("Run as a menu-bar-only app while the window is closed. Off keeps the Dock icon (like Spotify)");
|
||||
enabled: AppearanceState.tray-enable && AppearanceState.tray-close-to-tray;
|
||||
QbzToggle {
|
||||
checked: AppearanceState.tray-mac-hide-dock;
|
||||
enabled: AppearanceState.tray-enable && AppearanceState.tray-close-to-tray;
|
||||
toggled(v) => {
|
||||
AppearanceState.tray-mac-hide-dock = v;
|
||||
AppearanceState.appearance-bool("tray-mac-hide-dock", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingRow {
|
||||
unsupported: true;
|
||||
label: @tr("Tray icon variant");
|
||||
description: @tr("Pick a mono glyph to match your panel (Plasma, GNOME's permanently dark top bar) or the full colour vinyl logo");
|
||||
QbzSelect {
|
||||
|
||||
@@ -1432,9 +1432,15 @@ export global AppearanceState {
|
||||
in-out property <bool> show-purchases: false;
|
||||
|
||||
// --- SYSTEM TRAY -------------------------------------------------------
|
||||
// `is-macos` is set once from Rust (cfg!(target_os = "macos")) so the tray
|
||||
// section can swap the header to "Menu Bar" and show macOS-only controls.
|
||||
in-out property <bool> is-macos: false;
|
||||
in-out property <bool> tray-enable: true;
|
||||
in-out property <bool> tray-minimize-to-tray: false;
|
||||
in-out property <bool> tray-close-to-tray: false;
|
||||
// macOS-only: hide the Dock icon when closed to the menu bar (.accessory
|
||||
// activation policy). Off = Spotify-style (keep the Dock icon).
|
||||
in-out property <bool> tray-mac-hide-dock: false;
|
||||
in-out property <[string]> tray-icon-themes: ["Auto", "Mono light", "Mono dark", "Color"];
|
||||
in-out property <int> tray-icon-theme-index: 0;
|
||||
|
||||
|
||||
Reference in new issue
Block a user