diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 05d9cfcbb..99440be48 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -785,6 +785,7 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-store", "tauri-plugin-updater", + "tauri-plugin-window-state", "tempfile", "thiserror 2.0.18", "tokio", @@ -5687,6 +5688,21 @@ dependencies = [ "zip 4.6.1", ] +[[package]] +name = "tauri-plugin-window-state" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73736611e14142408d15353e21e3cca2f12a3cfb523ad0ce85999b6d2ef1a704" +dependencies = [ + "bitflags 2.11.0", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", +] + [[package]] name = "tauri-runtime" version = "2.10.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9136b8ac7..857cacfe9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -35,6 +35,7 @@ tauri-plugin-updater = "2" tauri-plugin-dialog = "2" tauri-plugin-store = "2" tauri-plugin-deep-link = "2" +tauri-plugin-window-state = "2" dirs = "5.0" toml = "0.8" toml_edit = "0.22" diff --git a/src-tauri/src/commands/settings.rs b/src-tauri/src/commands/settings.rs index ef6f7b395..408fced23 100644 --- a/src-tauri/src/commands/settings.rs +++ b/src-tauri/src/commands/settings.rs @@ -42,6 +42,8 @@ pub async fn save_settings(settings: crate::settings::AppSettings) -> Result Result { + crate::save_window_state_before_exit(&app); + // 在后台延迟重启,让函数有时间返回响应 tauri::async_runtime::spawn(async move { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f882e21eb..03f04658c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -64,6 +64,7 @@ use tauri::image::Image; use tauri::tray::{TrayIconBuilder, TrayIconEvent}; use tauri::RunEvent; use tauri::{Emitter, Manager}; +use tauri_plugin_window_state::{AppHandleExt, StateFlags}; fn redact_url_for_log(url_str: &str) -> String { match url::Url::parse(url_str) { @@ -264,6 +265,7 @@ pub fn run() { tray::apply_tray_policy(window.app_handle(), false); } } else { + api.prevent_close(); window.app_handle().exit(0); } } @@ -272,6 +274,11 @@ pub fn run() { .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_store::Builder::new().build()) + .plugin( + tauri_plugin_window_state::Builder::default() + .with_state_flags(window_state_flags()) + .build(), + ) .setup(|app| { let _ = rustls::crypto::ring::default_provider().install_default(); @@ -1351,6 +1358,7 @@ pub fn run() { let app_handle = app_handle.clone(); tauri::async_runtime::spawn(async move { + save_window_state_before_exit(&app_handle); cleanup_before_exit(&app_handle).await; log::info!("清理完成,退出应用"); @@ -1757,3 +1765,21 @@ fn show_database_init_error_dialog( )) .blocking_show() } + +// ============================================================ +// 在应用主动退出前显式持久化窗口状态 +// ============================================================ + +fn window_state_flags() -> StateFlags { + StateFlags::POSITION | StateFlags::SIZE | StateFlags::MAXIMIZED +} + +/// 当前应用的退出路径会拦截 `ExitRequested` 并最终直接 `std::process::exit(0)`, +/// 这里需要在真正结束进程前手动落盘,避免 window-state 插件的默认退出钩子被绕过。 +pub fn save_window_state_before_exit(app_handle: &tauri::AppHandle) { + if let Err(err) = app_handle.save_window_state(window_state_flags()) { + log::error!("退出前保存窗口状态失败: {err}"); + } else { + log::info!("已在退出前保存窗口状态"); + } +} diff --git a/src-tauri/src/lightweight.rs b/src-tauri/src/lightweight.rs index 78e5c9174..74b435b99 100644 --- a/src-tauri/src/lightweight.rs +++ b/src-tauri/src/lightweight.rs @@ -17,6 +17,7 @@ pub fn enter_lightweight_mode(app: &tauri::AppHandle) -> Result<(), String> { } if let Some(window) = app.get_webview_window("main") { + crate::save_window_state_before_exit(app); window .destroy() .map_err(|e| format!("销毁主窗口失败: {e}"))?; @@ -64,11 +65,12 @@ pub fn exit_lightweight_mode(app: &tauri::AppHandle) -> Result<(), String> { WebviewWindowBuilder::from_config(app, window_config) .map_err(|e| format!("加载主窗口配置失败: {e}"))? - .visible(true) .build() .map_err(|e| format!("创建主窗口失败: {e}"))?; if let Some(window) = app.get_webview_window("main") { + let _ = window.unminimize(); + let _ = window.show(); let _ = window.set_focus(); #[cfg(target_os = "linux")] {