|
| 1 | +use std::{sync::Mutex, time::Instant}; |
| 2 | + |
| 3 | +use anyhow::Result; |
1 | 4 | use tauri::{ |
2 | | - AppHandle, Manager, State, Theme, |
| 5 | + AppHandle, DragDropEvent, Manager, State, Theme, |
3 | 6 | menu::{MenuBuilder, MenuItem}, |
| 7 | + tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}, |
| 8 | +}; |
| 9 | +use tracing::{debug, info}; |
| 10 | + |
| 11 | +use crate::{ |
| 12 | + DatabaseManager, WindowContext, |
| 13 | + constants::{self, WINDOW_MIN_HEIGHT, WINDOW_MIN_WIDTH}, |
| 14 | + db::launcher, |
| 15 | + error::OneClickLaunchError, |
| 16 | + events::{ |
| 17 | + EventDispatcher, |
| 18 | + types::{DragDropResource, DragDropResourcePaylod}, |
| 19 | + }, |
4 | 20 | }; |
5 | 21 |
|
6 | | -use crate::{DatabaseManager, WindowContext, db::launcher, error::OneClickLaunchError}; |
| 22 | +use super::{launcher_api, setting_api}; |
7 | 23 |
|
8 | 24 | pub fn hide_window(app: &AppHandle) -> Result<(), OneClickLaunchError> { |
9 | 25 | let window = app.get_webview_window("main").unwrap(); |
@@ -50,3 +66,150 @@ pub fn change_windows_theme(app: &AppHandle, theme: &str) -> Result<(), OneClick |
50 | 66 | } |
51 | 67 | Ok(()) |
52 | 68 | } |
| 69 | + |
| 70 | +pub fn handle_window_event(window: &tauri::Window, event: &tauri::WindowEvent) { |
| 71 | + match event { |
| 72 | + tauri::WindowEvent::CloseRequested { api, .. } => { |
| 73 | + api.prevent_close(); |
| 74 | + let _ = window.hide(); |
| 75 | + |
| 76 | + let app_handle = window.app_handle().clone(); |
| 77 | + tauri::async_runtime::spawn(async move { |
| 78 | + let db = app_handle.state::<DatabaseManager>(); |
| 79 | + let setting = setting_api::read_setting(db, constants::CLOSE_MAIN_PANEL_KEY).await; |
| 80 | + |
| 81 | + match setting { |
| 82 | + Ok(Some(setting)) if constants::CLOSE_MAIN_PANEL_EXIT == setting.value => { |
| 83 | + app_handle.exit(0); |
| 84 | + } |
| 85 | + _ => {} |
| 86 | + } |
| 87 | + }); |
| 88 | + } |
| 89 | + tauri::WindowEvent::ScaleFactorChanged { |
| 90 | + scale_factor, |
| 91 | + new_inner_size, |
| 92 | + .. |
| 93 | + } => { |
| 94 | + print!( |
| 95 | + "ScaleFactorChanged scale_factor: {}, new_inner_size: {:?}", |
| 96 | + scale_factor, new_inner_size |
| 97 | + ); |
| 98 | + |
| 99 | + let now = Instant::now(); |
| 100 | + let state = window.state::<ScaleFactorChangedState>(); |
| 101 | + let mut lock = state.last_reset.try_lock(); |
| 102 | + |
| 103 | + if let Ok(ref mut last_reset) = lock { |
| 104 | + // 500ms防抖间隔 |
| 105 | + if last_reset.map_or(true, |t| now.duration_since(t).as_millis() > 500) { |
| 106 | + if let Ok(physical_size) = window.inner_size() { |
| 107 | + // 如果窗口大小异常,强制调整到正常大小 |
| 108 | + if physical_size.width != WINDOW_MIN_WIDTH |
| 109 | + || physical_size.height != WINDOW_MIN_HEIGHT |
| 110 | + { |
| 111 | + let _ = window.set_size(tauri::Size::Logical(tauri::LogicalSize { |
| 112 | + width: WINDOW_MIN_WIDTH as f64, |
| 113 | + height: WINDOW_MIN_HEIGHT as f64, |
| 114 | + })); |
| 115 | + **last_reset = Some(now); |
| 116 | + debug!("DPI发生变化,触发窗口大小重置"); |
| 117 | + } |
| 118 | + } |
| 119 | + } else { |
| 120 | + debug!("DPI重置窗口防抖生效"); |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + tauri::WindowEvent::DragDrop(drag_drop_event) => match drag_drop_event { |
| 125 | + DragDropEvent::Drop { paths, .. } if !paths.is_empty() => { |
| 126 | + let _ = EventDispatcher::<DragDropResource>::send_event( |
| 127 | + window.app_handle(), |
| 128 | + DragDropResourcePaylod { |
| 129 | + paths: paths.clone(), |
| 130 | + }, |
| 131 | + ); |
| 132 | + } |
| 133 | + _ => {} |
| 134 | + }, |
| 135 | + tauri::WindowEvent::Resized(physical_size) => { |
| 136 | + if physical_size.width == 0 && physical_size.height == 0 { |
| 137 | + // 页面最小化时忽略 |
| 138 | + return; |
| 139 | + } |
| 140 | + // 如果窗口大小过小,强制调整到正常大小 |
| 141 | + if physical_size.width < WINDOW_MIN_WIDTH || physical_size.height < WINDOW_MIN_HEIGHT { |
| 142 | + let _ = window.set_size(tauri::Size::Logical(tauri::LogicalSize { |
| 143 | + width: WINDOW_MIN_WIDTH as f64, |
| 144 | + height: WINDOW_MIN_HEIGHT as f64, |
| 145 | + })); |
| 146 | + debug!("窗口大小过小,触发窗口大小重置: ps: {:?}", physical_size); |
| 147 | + } |
| 148 | + } |
| 149 | + _ => {} |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +/// 初始化窗口 |
| 154 | +pub fn setup_tray(app: &AppHandle) -> Result<()> { |
| 155 | + let tray_icon = TrayIconBuilder::new() |
| 156 | + // 设置系统托盘的提示,鼠标悬浮时会显示 |
| 157 | + .tooltip(constants::APPLICATION_NAME) |
| 158 | + // 左键系统托盘时不显示菜单 |
| 159 | + .show_menu_on_left_click(false) |
| 160 | + // 使用主窗口的icon作为系统托盘的图标 |
| 161 | + .icon(app.default_window_icon().unwrap().clone()) |
| 162 | + // 系统托盘的点击事件 |
| 163 | + .on_tray_icon_event(|tray, event| match event { |
| 164 | + TrayIconEvent::Click { |
| 165 | + button: MouseButton::Left, |
| 166 | + button_state: MouseButtonState::Up, |
| 167 | + .. |
| 168 | + } => { |
| 169 | + // 当用户点击系统托盘时使应用程序取消最小化,显示并聚焦 |
| 170 | + let app = tray.app_handle(); |
| 171 | + if let Some(window) = app.get_webview_window("main") { |
| 172 | + let _ = window.unminimize(); |
| 173 | + let _ = window.show(); |
| 174 | + let _ = window.set_focus(); |
| 175 | + } |
| 176 | + } |
| 177 | + _ => { |
| 178 | + debug!("unhandled event {event:?}"); |
| 179 | + } |
| 180 | + }) |
| 181 | + .on_menu_event(|app, event| match event.id.as_ref() { |
| 182 | + "quit" => { |
| 183 | + // 退出应用程序 |
| 184 | + info!("quit menu item was clicked"); |
| 185 | + app.exit(0); |
| 186 | + } |
| 187 | + id if id.starts_with("launch_") => { |
| 188 | + // 系统托盘的菜单id由"launch_"与启动器id拼接而成, 点击菜单后通过解析id,找到要启动的启动器触发启动 |
| 189 | + let id = &id["launch_".len()..]; |
| 190 | + if let Ok(launcher_id) = id.parse::<i64>() { |
| 191 | + let app_cloned = app.clone(); |
| 192 | + tauri::async_runtime::spawn(async move { |
| 193 | + let inner_app = app_cloned.clone(); |
| 194 | + let db = inner_app.state(); |
| 195 | + launcher_api::launch(app_cloned, db, launcher_id).await |
| 196 | + }); |
| 197 | + } |
| 198 | + } |
| 199 | + _ => { |
| 200 | + tracing::error!("menu item {:?} not handled", event.id); |
| 201 | + } |
| 202 | + }) |
| 203 | + .build(app)?; |
| 204 | + |
| 205 | + let window_context = WindowContext { tray_icon }; |
| 206 | + |
| 207 | + app.manage(window_context); |
| 208 | + |
| 209 | + Ok(()) |
| 210 | +} |
| 211 | + |
| 212 | +/// 用于保存上次处理分辨率变更事件的时间 |
| 213 | +pub struct ScaleFactorChangedState { |
| 214 | + pub last_reset: Mutex<Option<Instant>>, |
| 215 | +} |
0 commit comments