From 5190bbcafccd851c36d8cf2f8a141455e6bb758d Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Wed, 22 Apr 2026 20:11:04 +0800 Subject: [PATCH] fix(macos): don't quit app on Cmd+W, only on Cmd+Q, closes #3927 (#3928) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS, closing the last window (Cmd+W or the red traffic light) was quitting the app, which is unexpected — the native convention is that Cmd+W closes the window while the app keeps running in the dock, and only Cmd+Q quits. Intercept the window CloseRequested event on macOS, prevent the close, and hide the window instead so the app remains active. Handle the Reopen event (fired when the user clicks the dock icon) to restore the hidden window. Cmd+Q continues to go through applicationWillTerminate: and exits the app normally. --- apps/readest-app/src-tauri/src/lib.rs | 59 +++++++++++++++++++++------ 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/apps/readest-app/src-tauri/src/lib.rs b/apps/readest-app/src-tauri/src/lib.rs index 6648d1ae..8e9b707a 100644 --- a/apps/readest-app/src-tauri/src/lib.rs +++ b/apps/readest-app/src-tauri/src/lib.rs @@ -411,10 +411,29 @@ pub fn run() { builder }; - win_builder.build().unwrap(); + #[cfg(not(target_os = "macos"))] + { + win_builder.build().unwrap(); + } // let win = win_builder.build().unwrap(); // win.open_devtools(); + #[cfg(target_os = "macos")] + { + let window = win_builder.build().unwrap(); + // On macOS, closing a window (via Cmd+W or the red traffic light) should + // not quit the app — only Cmd+Q should. Hide the window instead so the + // app keeps running in the dock, and restore it when the user reopens + // the app from the dock. + let window_for_close = window.clone(); + window.on_window_event(move |event| { + if let tauri::WindowEvent::CloseRequested { api, .. } = event { + api.prevent_close(); + let _ = window_for_close.hide(); + } + }); + } + #[cfg(target_os = "macos")] macos::menu::setup_macos_menu(app.handle())?; @@ -428,18 +447,34 @@ pub fn run() { #[allow(unused_variables)] |app_handle, event| { #[cfg(target_os = "macos")] - if let tauri::RunEvent::Opened { urls } = event { - let files = urls - .into_iter() - .filter_map(|url| url.to_file_path().ok()) - .collect::>(); + match event { + tauri::RunEvent::Opened { urls } => { + let files = urls + .into_iter() + .filter_map(|url| url.to_file_path().ok()) + .collect::>(); - let app_handler_clone = app_handle.clone(); - allow_file_in_scopes(app_handle, files.clone()); - app_handle.listen("window-ready", move |_| { - println!("Window is ready, proceeding to handle files."); - set_window_open_with_files(&app_handler_clone, files.clone()); - }); + let app_handler_clone = app_handle.clone(); + allow_file_in_scopes(app_handle, files.clone()); + app_handle.listen("window-ready", move |_| { + println!("Window is ready, proceeding to handle files."); + set_window_open_with_files(&app_handler_clone, files.clone()); + }); + } + // When the user reopens the app from the dock after closing all + // windows, re-show the main window instead of leaving the dock + // icon inert. + tauri::RunEvent::Reopen { + has_visible_windows: false, + .. + } => { + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.show(); + let _ = window.set_focus(); + let _ = window.unminimize(); + } + } + _ => {} } }, );