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:
vicrodh committed 2026-06-06 01:44:38 -06:00
1 parent 5b3ba963b0
commit 8a78c2250f
22 files changed
+1084 -26

No files matched your search

+14
View File
@@ -22,6 +22,12 @@ slint = { version = "1.16", default-features = false, features = [
"accessibility",
"compat-1-2",
] }
# Direct access to the winit backend for the system tray: WinitWindowAccessor
# (hide/show the OS window without quitting the event loop) and, later, the
# CustomApplicationHandler (tray event pump + close-to-tray intercept). MUST be
# pinned to the exact version Slint resolves (1.16.1) — i-slint-backend-winit
# does not follow semver, and a mismatch makes with_winit_window() return None.
i-slint-backend-winit = "=1.16.1"
log = { workspace = true }
env_logger = "0.11"
tokio = { workspace = true }
@@ -53,5 +59,13 @@ rusqlite = { version = "0.31", features = ["bundled"] }
qbz-cache = { path = "../qbz-cache" }
qbz-credentials = { path = "../qbz-credentials" }
# Linux system tray via StatusNotifierItem. Same crate + rationale as the
# Tauri build (src-tauri/Cargo.toml): libayatana-appindicator never dispatches
# primary-activate (left-click), so we use ksni's pure-Rust SNI which exposes
# Activate / SecondaryActivate / Scroll. macOS/Windows use `tray-icon` instead
# (added with the CustomApplicationHandler slice).
[target.'cfg(target_os = "linux")'.dependencies]
ksni = { version = "0.3", features = ["blocking"] }
[build-dependencies]
slint-build = "1.16"