fix(desktop): macos quit flash, first click and press feedback

This commit is contained in:
知食分子 authored and GitHub committed 2026-09-01 01:19:47 +08:00
1 parent 23f511de22
commit 554be11aea
5 files changed
+91 -11

No files matched your search

+9
View File
@@ -86,6 +86,15 @@ pub async fn complete_app_close(app: AppHandle, window: Window, action: String)
if let Some(state) = app.try_state::<CloseBehaviorState>() {
state.allow_next_exit();
}
#[cfg(target_os = "macos")]
{
// Take the whole app off-screen first so WKWebView teardown during
// process exit cannot flash the window red on macOS 12. `AppHandle::hide`
// is macOS-only, so this block is compiled per-platform.
let _ = window.hide();
let _ = app.hide();
tokio::time::sleep(std::time::Duration::from_millis(crate::EXIT_HIDE_GRACE_MS)).await;
}
app.exit(0);
}
"hide" => {
+31 -4
View File
@@ -115,6 +115,18 @@ fn should_hide_window_on_close(target_os: &str) -> bool {
matches!(target_os, "macos" | "windows")
}
/// How long to keep the app off-screen after hiding it and before the process
/// exits, so WindowServer has removed the window before WKWebView teardown.
#[cfg(target_os = "macos")]
pub(crate) const EXIT_HIDE_GRACE_MS: u64 = 250;
/// On macOS, tearing down WKWebView while the window is still on screen can
/// paint the window red for a frame before the process exits, so the app is
/// hidden first. Windows and Linux keep their existing exit behavior.
fn should_hide_window_before_exit(target_os: &str) -> bool {
target_os == "macos"
}
fn should_setup_desktop_tray(target_os: &str, show_tray_icon: bool, linux_appindicator_available: bool) -> bool {
show_tray_icon
&& (matches!(target_os, "macos" | "windows") || (target_os == "linux" && linux_appindicator_available))
@@ -973,10 +985,10 @@ mod tests {
linux_drm_render_devices_from_paths, linux_nvidia_driver_from_state, linux_pci_id_from_sysfs_value,
linux_selected_drm_render_device, linux_uses_native_wayland, linux_webkit_environment_override,
linux_webkit_rendering_workarounds, native_window_decorations_override, should_confirm_app_exit_request,
should_enable_single_instance, should_fallback_to_native_quit, should_hide_window_on_close,
should_setup_desktop_tray, should_show_main_window_after_setup, should_show_main_window_before_setup_tasks,
startup_data_dir_mode, tray_menu_labels_for_locale, uses_application_level_icon, LinuxDrmRenderDevice,
LinuxNvidiaDriver,
should_enable_single_instance, should_fallback_to_native_quit, should_hide_window_before_exit,
should_hide_window_on_close, should_setup_desktop_tray, should_show_main_window_after_setup,
should_show_main_window_before_setup_tasks, startup_data_dir_mode, tray_menu_labels_for_locale,
uses_application_level_icon, LinuxDrmRenderDevice, LinuxNvidiaDriver,
};
use crate::data_dir::DataDirMode;
use std::ffi::OsStr;
@@ -1027,6 +1039,13 @@ mod tests {
assert!(!should_hide_window_on_close("linux"));
}
#[test]
fn hides_window_before_exit_only_on_macos() {
assert!(should_hide_window_before_exit("macos"));
assert!(!should_hide_window_before_exit("windows"));
assert!(!should_hide_window_before_exit("linux"));
}
#[test]
fn sets_up_desktop_tray_for_windows_macos_and_linux() {
assert!(should_setup_desktop_tray("windows", true, false));
@@ -2528,6 +2547,14 @@ pub fn run() {
api.prevent_exit();
request_app_close(app_handle, "quit");
} else {
// Restart exits and the no-frontend native quit bypass
// `complete_app_close`, so hide the window here too; the
// shutdown below gives WindowServer time to remove it.
if should_hide_window_before_exit(std::env::consts::OS) {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.hide();
}
}
tauri::async_runtime::block_on(async {
if let Some(server) = app_handle.try_state::<commands::redis_pubsub_server::PubSubServerState>()
{