性能优化
This commit is contained in:
+46
-102
@@ -20,14 +20,12 @@ use tauri::{
|
||||
/// 记录窗口最后显示时间,用于失焦防抖(避免显示瞬间因焦点未稳定而被立即隐藏)
|
||||
static LAST_SHOW_TIME: Mutex<Option<Instant>> = Mutex::new(None);
|
||||
|
||||
/// 保存最近一次右键时计算出的定位参数(逻辑坐标),供 `tray_menu_ready` 使用
|
||||
/// (x, tray_top_l, wa_top_l, wa_bottom_l, scale)
|
||||
/// 保存最近一次右键时计算出的定位参数(物理坐标),供 `tray_menu_ready` 使用
|
||||
/// (x, tray_top, wa_top, wa_bottom, scale)
|
||||
static LAST_MENU_LAYOUT: Mutex<Option<(f64, f64, f64, f64, f64)>> = Mutex::new(None);
|
||||
|
||||
use crate::clipboard::popup::{get_work_area, get_work_area_at_point, get_dpi_for_point};
|
||||
use crate::clipboard::ClipboardManager;
|
||||
use crate::download_engine::DownloadEngine;
|
||||
use crate::mihomo_manager::MihomoManager;
|
||||
use crate::win32_util::{get_work_area, get_work_area_at_point, get_dpi_for_point};
|
||||
use crate::mihomo_manager::{MihomoManager, is_pseudo_node};
|
||||
use crate::monitor_kernel::MonitorKernel;
|
||||
use crate::process_manager::{ProcessManager, ProcessStatus};
|
||||
|
||||
@@ -54,37 +52,6 @@ pub struct TrayMenuState {
|
||||
pub proxy_current: Option<String>,
|
||||
}
|
||||
|
||||
// ===== 伪节点过滤(与前端 ProxyModule 保持一致) =====
|
||||
|
||||
const PSEUDO_KEYWORDS: &[&str] = &[
|
||||
"DIRECT",
|
||||
"REJECT",
|
||||
"PASS",
|
||||
"COMPATIBLE",
|
||||
"流量",
|
||||
"套餐",
|
||||
"到期",
|
||||
"续费",
|
||||
"官网",
|
||||
"网站",
|
||||
"刷新",
|
||||
"更新",
|
||||
"⭐",
|
||||
"★",
|
||||
"☆",
|
||||
"✕",
|
||||
"✖",
|
||||
"×",
|
||||
];
|
||||
|
||||
fn is_pseudo_node(name: &str) -> bool {
|
||||
let upper = name.trim().to_uppercase();
|
||||
if upper == "DIRECT" || upper == "REJECT" || upper == "PASS" || upper == "GLOBAL" {
|
||||
return true;
|
||||
}
|
||||
PSEUDO_KEYWORDS.iter().any(|kw| name.contains(kw))
|
||||
}
|
||||
|
||||
// ===== 状态查询 =====
|
||||
|
||||
fn is_proxy_running(app: &AppHandle) -> bool {
|
||||
@@ -252,7 +219,7 @@ pub fn precreate_tray_menu_window(app: &AppHandle) {
|
||||
{
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
eprintln!("[tray-menu] 预创建菜单窗口失败: {}", e);
|
||||
crate::logger::log_error("tray", &format!("预创建菜单窗口失败: {}", e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -262,7 +229,7 @@ pub fn precreate_tray_menu_window(app: &AppHandle) {
|
||||
win.on_window_event(move |event| {
|
||||
if let tauri::WindowEvent::Focused(false) = event {
|
||||
let should_hide = {
|
||||
let t = LAST_SHOW_TIME.lock().unwrap();
|
||||
let t = LAST_SHOW_TIME.lock().unwrap_or_else(|e| e.into_inner());
|
||||
match *t {
|
||||
Some(time) => time.elapsed() > Duration::from_millis(300),
|
||||
None => true,
|
||||
@@ -274,7 +241,7 @@ pub fn precreate_tray_menu_window(app: &AppHandle) {
|
||||
}
|
||||
});
|
||||
|
||||
eprintln!("[tray-menu] 菜单窗口已预创建(隐藏渲染)");
|
||||
crate::logger::log_info("tray", "菜单窗口已预创建(隐藏渲染)");
|
||||
}
|
||||
|
||||
/// 右键托盘时调用:计算定位参数、发送状态给前端,但不立即显示窗口。
|
||||
@@ -289,29 +256,23 @@ pub fn show_tray_menu(app: &AppHandle, cursor_pos: (f64, f64), tray_rect: (f64,
|
||||
let (wa_left, wa_top, wa_right, wa_bottom) = get_work_area_at_point(mx as i32, my as i32)
|
||||
.unwrap_or((0, 0, 1920, 1040));
|
||||
|
||||
// 获取光标所在显示器的 DPI,将物理坐标转为逻辑坐标(DIP)
|
||||
// 光标所在显示器的 DPI:菜单宽度按物理像素换算
|
||||
let dpi = get_dpi_for_point(mx as i32, my as i32).unwrap_or(96);
|
||||
let scale = dpi as f64 / 96.0;
|
||||
let menu_w_px = MENU_W * scale;
|
||||
|
||||
let mx_l = mx / scale;
|
||||
let tray_top_l = tray_top / scale;
|
||||
let wa_left_l = wa_left as f64 / scale;
|
||||
let wa_right_l = wa_right as f64 / scale;
|
||||
let wa_top_l = wa_top as f64 / scale;
|
||||
let wa_bottom_l = wa_bottom as f64 / scale;
|
||||
// 水平:菜单左边缘对齐鼠标 X(向右延伸),超出右边界则左移(物理坐标)
|
||||
let x = mx.max(wa_left as f64).min(wa_right as f64 - menu_w_px);
|
||||
|
||||
// 水平:菜单左边缘对齐鼠标 X(向右延伸),超出右边界则左移
|
||||
let x = mx_l.max(wa_left_l).min(wa_right_l - MENU_W);
|
||||
|
||||
// 保存布局参数,供 tray_menu_ready 使用
|
||||
// 保存布局参数(全部物理坐标 + scale,供 tray_menu_ready 换算前端上报的逻辑高度)
|
||||
{
|
||||
let mut layout = LAST_MENU_LAYOUT.lock().unwrap();
|
||||
*layout = Some((x, tray_top_l, wa_top_l, wa_bottom_l, scale));
|
||||
let mut layout = LAST_MENU_LAYOUT.lock().unwrap_or_else(|e| e.into_inner());
|
||||
*layout = Some((x, tray_top, wa_top as f64, wa_bottom as f64, scale));
|
||||
}
|
||||
|
||||
// 记录显示时间,用于失焦防抖
|
||||
{
|
||||
let mut t = LAST_SHOW_TIME.lock().unwrap();
|
||||
let mut t = LAST_SHOW_TIME.lock().unwrap_or_else(|e| e.into_inner());
|
||||
*t = Some(Instant::now());
|
||||
}
|
||||
|
||||
@@ -324,7 +285,7 @@ pub fn show_tray_menu(app: &AppHandle, cursor_pos: (f64, f64), tray_rect: (f64,
|
||||
let app_clone = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = get_tray_menu_state(&app_clone).await;
|
||||
let _ = app_clone.emit("tray-menu-show", state);
|
||||
let _ = app_clone.emit(crate::constants::events::TRAY_MENU_SHOW, state);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -338,7 +299,7 @@ pub fn hide_tray_menu(app: &AppHandle) {
|
||||
/// 刷新菜单状态并发送给前端
|
||||
async fn refresh_and_emit_state(app: &AppHandle) {
|
||||
let state = get_tray_menu_state(app).await;
|
||||
let _ = app.emit("tray-menu-state-updated", state);
|
||||
let _ = app.emit(crate::constants::events::TRAY_MENU_STATE_UPDATED, state);
|
||||
}
|
||||
|
||||
// ===== Tauri 命令 =====
|
||||
@@ -353,13 +314,13 @@ pub async fn tray_menu_action(
|
||||
match action.as_str() {
|
||||
"proxy_enable" => {
|
||||
if let Err(e) = enable_proxy(&app).await {
|
||||
eprintln!("[tray] 开启代理失败: {}", e);
|
||||
crate::logger::log_error("tray", &format!("开启代理失败: {}", e));
|
||||
send_notification(&app, "代理启动失败", &e);
|
||||
}
|
||||
}
|
||||
"proxy_disable" => {
|
||||
if let Err(e) = disable_proxy(&app).await {
|
||||
eprintln!("[tray] 关闭代理失败: {}", e);
|
||||
crate::logger::log_error("tray", &format!("关闭代理失败: {}", e));
|
||||
send_notification(&app, "代理关闭失败", &e);
|
||||
}
|
||||
}
|
||||
@@ -378,28 +339,28 @@ pub async fn tray_menu_action(
|
||||
}
|
||||
}
|
||||
"osd_toggle" => {
|
||||
let _ = app.emit("tray:toggle-osd", ());
|
||||
let _ = app.emit(crate::constants::events::TRAY_TOGGLE_OSD, ());
|
||||
}
|
||||
"kernel_restart" => {
|
||||
if let Err(e) = restart_kernel(&app).await {
|
||||
eprintln!("[tray] 重启 Kernel 失败: {}", e);
|
||||
crate::logger::log_error("tray", &format!("重启 Kernel 失败: {}", e));
|
||||
send_notification(&app, "Kernel 重启失败", &e);
|
||||
}
|
||||
}
|
||||
"download_new" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if let Some(window) = app.get_webview_window(crate::constants::windows::MAIN) {
|
||||
window.show().ok();
|
||||
window.set_focus().ok();
|
||||
}
|
||||
let _ = app.emit("tray:new-download", ());
|
||||
let _ = app.emit(crate::constants::events::TRAY_NEW_DOWNLOAD, ());
|
||||
hide_tray_menu(&app);
|
||||
}
|
||||
"settings" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if let Some(window) = app.get_webview_window(crate::constants::windows::MAIN) {
|
||||
window.show().ok();
|
||||
window.set_focus().ok();
|
||||
}
|
||||
let _ = app.emit("tray:open-settings", ());
|
||||
let _ = app.emit(crate::constants::events::TRAY_OPEN_SETTINGS, ());
|
||||
hide_tray_menu(&app);
|
||||
}
|
||||
"quit" => {
|
||||
@@ -431,24 +392,29 @@ pub async fn tray_menu_ready(content_height: f64, app: AppHandle) -> Result<(),
|
||||
let win = app.get_webview_window(TRAY_MENU_LABEL)
|
||||
.ok_or("tray-menu window not found")?;
|
||||
|
||||
let (x, tray_top_l, wa_top_l, wa_bottom_l, _scale) = {
|
||||
let layout = LAST_MENU_LAYOUT.lock().unwrap();
|
||||
let (x, tray_top, wa_top, wa_bottom, scale) = {
|
||||
let layout = LAST_MENU_LAYOUT.lock().unwrap_or_else(|e| e.into_inner());
|
||||
layout.unwrap_or((0.0, 1040.0, 0.0, 1040.0, 1.0))
|
||||
};
|
||||
|
||||
// 将内容高度限制在合理范围内
|
||||
// 将内容高度限制在合理范围内(前端上报为逻辑像素)
|
||||
let h = content_height.max(100.0).min(520.0);
|
||||
|
||||
// 调整窗口尺寸
|
||||
let _ = win.set_size(tauri::Size::Logical(tauri::LogicalSize {
|
||||
width: MENU_W,
|
||||
height: h,
|
||||
// 调整窗口尺寸(物理像素,与物理坐标定位保持一致,避免混合 DPI 换算偏移)
|
||||
let win_w_px = MENU_W * scale;
|
||||
let win_h_px = h * scale;
|
||||
let _ = win.set_size(tauri::Size::Physical(tauri::PhysicalSize {
|
||||
width: win_w_px as u32,
|
||||
height: win_h_px as u32,
|
||||
}));
|
||||
|
||||
// 垂直:菜单下边缘紧贴托盘图标顶部(向上弹出)
|
||||
let y = (tray_top_l - h).max(wa_top_l).min(wa_bottom_l - h);
|
||||
// 垂直:菜单下边缘紧贴托盘图标顶部(向上弹出,物理坐标)
|
||||
let y = (tray_top - win_h_px).max(wa_top).min(wa_bottom - win_h_px);
|
||||
|
||||
let pos = tauri::Position::Logical(tauri::LogicalPosition { x, y });
|
||||
let pos = tauri::Position::Physical(tauri::PhysicalPosition {
|
||||
x: x as i32,
|
||||
y: y as i32,
|
||||
});
|
||||
let _ = win.set_position(pos);
|
||||
let _ = win.show();
|
||||
let _ = win.set_focus();
|
||||
@@ -605,7 +571,7 @@ async fn select_proxy_node(app: &AppHandle, group: &str, name: &str) {
|
||||
send_notification(app, "节点已切换", &format!("{}\n延迟: {}", name, delay_text));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[tray] 切换节点失败: {}", e);
|
||||
crate::logger::log_error("tray", &format!("切换节点失败: {}", e));
|
||||
send_notification(app, "切换节点失败", &e);
|
||||
}
|
||||
}
|
||||
@@ -657,34 +623,12 @@ fn send_notification(app: &AppHandle, title: &str, message: &str) {
|
||||
.show();
|
||||
}
|
||||
|
||||
// ===== 退出清理 =====
|
||||
// ===== 退出入口 =====
|
||||
|
||||
/// 托盘菜单"退出"调用。
|
||||
/// 资源清理统一收敛到 RunEvent::ExitRequested(覆盖所有退出路径),此处仅请求退出,
|
||||
/// 避免清理逻辑双份执行(monitor 的 cleanup 涉及子进程/网络,重复执行有竞态风险)。
|
||||
pub fn quit_cleanup(app: &AppHandle) {
|
||||
if let Some(mihomo) = app.try_state::<MihomoManager>() {
|
||||
mihomo.cleanup_on_exit();
|
||||
}
|
||||
if let Some(engine) = app.try_state::<DownloadEngine>() {
|
||||
engine.cleanup_on_exit();
|
||||
}
|
||||
// monitor.cleanup_on_exit 是 async,在 tokio worker 线程中直接 block_on 会 panic,
|
||||
// 放到独立 OS 线程执行 block_on,避免嵌套 runtime。
|
||||
if let Some(monitor) = app.try_state::<MonitorKernel>() {
|
||||
let app_clone = app.clone();
|
||||
let monitor_clone = monitor.inner().clone();
|
||||
std::thread::spawn(move || {
|
||||
tauri::async_runtime::block_on(async move {
|
||||
monitor_clone.cleanup_on_exit(&app_clone).await;
|
||||
});
|
||||
})
|
||||
.join()
|
||||
.ok();
|
||||
}
|
||||
if let Some(clip) = app.try_state::<ClipboardManager>() {
|
||||
clip.stop();
|
||||
}
|
||||
if let Some(pm) = app.try_state::<ProcessManager>() {
|
||||
pm.stop_all();
|
||||
}
|
||||
app.exit(0);
|
||||
}
|
||||
|
||||
@@ -704,7 +648,7 @@ pub fn create_tray_menu(app: &AppHandle) -> Result<(), tauri::Error> {
|
||||
..
|
||||
} => {
|
||||
// 左键:显示主窗口
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if let Some(window) = app.get_webview_window(crate::constants::windows::MAIN) {
|
||||
window.show().ok();
|
||||
window.set_focus().ok();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user