From 615bc0cd4b4b339de524d6ddb1aba5b9eab7b514 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 27 May 2026 02:18:28 -0700 Subject: [PATCH] Tray: minimize-to-tray is opt-in via config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make ui.minimize_to_tray default-off so users on desktops without a working StatusNotifier host (GNOME sans AppIndicator extension being the common case) don't get stuck — without an opt-in, clicking X would hide an invisible window with no obvious way back. The tray menu's Show/Hide/Quit and Ctrl+Q still work regardless of the flag; the toggle only affects what happens when the user clicks the window's close button. Added a settings-screen checkbox for the flag with a tooltip explaining the SNI dependency. Co-Authored-By: Claude Opus 4.7 --- src/app.rs | 24 ++++++++++++++++-------- src/config.rs | 12 +++++++++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/app.rs b/src/app.rs index 5fee580..ddf4890 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2126,6 +2126,11 @@ impl App { }); ui.end_row(); + ui.label("Minimize to tray:"); + ui.checkbox(&mut self.config.ui.minimize_to_tray, "hide window on close instead of quitting") + .on_hover_text("Requires a working StatusNotifier host (KDE native, GNOME with AppIndicator extension). Ctrl+Q always quits."); + ui.end_row(); + ui.label("Auto-check channels:"); ui.checkbox(&mut self.config.scheduler.enabled, "enabled"); ui.end_row(); @@ -3082,14 +3087,17 @@ impl eframe::App for App { } } } - // Minimize-to-tray: if the user clicked the close button but - // hasn't explicitly chosen Quit, cancel the close and hide - // the window instead. Without a tray we fall through to the - // normal close flow so the app actually exits. - let close_requested = ctx.input(|i| i.viewport().close_requested()); - if close_requested && !self.quitting { - ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose); - ctx.send_viewport_cmd(egui::ViewportCommand::Visible(false)); + // Minimize-to-tray: only intercept the close button when the + // user has opted in via config. Default-off because users on + // desktops without a working StatusNotifier host (e.g. GNOME + // sans AppIndicator extension) would otherwise lose access + // to the window entirely. Ctrl+Q is always an escape. + if self.config.ui.minimize_to_tray { + let close_requested = ctx.input(|i| i.viewport().close_requested()); + if close_requested && !self.quitting { + ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose); + ctx.send_viewport_cmd(egui::ViewportCommand::Visible(false)); + } } } diff --git a/src/config.rs b/src/config.rs index 2cb8dc6..eac6d0c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -61,11 +61,21 @@ impl Default for PlayerSection { pub struct UiSection { #[serde(default = "default_theme")] pub theme: String, + /// When true, clicking the window close button hides the window + /// into the system tray instead of exiting. The tray menu's Quit + /// item (or Ctrl+Q) still terminates the app. Off by default so + /// users without a working SNI host don't get stuck in an invisible + /// hidden window. + #[serde(default)] + pub minimize_to_tray: bool, } impl Default for UiSection { fn default() -> Self { - Self { theme: default_theme() } + Self { + theme: default_theme(), + minimize_to_tray: false, + } } }