refactor(tray): deduplicate icon getters, update_icon, and menu+icon combo

- Consolidate 3 near-identical icon getter functions into load_icon/default_icon with IconKind enum
- Merge two platform-gated update_icon implementations into one
- Extract update_menu_and_icon to eliminate duplicate combo in feat/config.rs and feat/clash.rs
This commit is contained in:
Tunglies 2026-03-23 21:42:22 +08:00
parent 0932de9f6c
commit 670d7bae3b
No known key found for this signature in database
GPG Key ID: B9B01B389469B3E8
3 changed files with 63 additions and 113 deletions

View File

@ -37,97 +37,74 @@ const TRAY_CLICK_DEBOUNCE_MS: u64 = 300;
#[derive(Clone)] #[derive(Clone)]
struct TrayState {} struct TrayState {}
enum IconKind {
Common,
SysProxy,
Tun,
}
pub struct Tray { pub struct Tray {
limiter: SystemLimiter, limiter: SystemLimiter,
} }
impl TrayState { impl TrayState {
async fn get_tray_icon(verge: &IVerge) -> (bool, Vec<u8>) { async fn get_tray_icon(verge: &IVerge) -> (bool, Vec<u8>) {
let system_mode = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.unwrap_or(false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); let system_mode = verge.enable_system_proxy.unwrap_or(false);
match (*system_mode, *tun_mode) { let kind = if tun_mode {
(true, true) => Self::get_tun_tray_icon(verge).await, IconKind::Tun
(true, false) => Self::get_sysproxy_tray_icon(verge).await, } else if system_mode {
(false, true) => Self::get_tun_tray_icon(verge).await, IconKind::SysProxy
(false, false) => Self::get_common_tray_icon(verge).await, } else {
} IconKind::Common
};
Self::load_icon(verge, kind).await
} }
async fn get_common_tray_icon(verge: &IVerge) -> (bool, Vec<u8>) { async fn load_icon(verge: &IVerge, kind: IconKind) -> (bool, Vec<u8>) {
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false); let (custom_enabled, icon_name) = match kind {
if is_common_tray_icon IconKind::Common => (verge.common_tray_icon.unwrap_or(false), "common"),
&& let Ok(Some(common_icon_path)) = find_target_icons("common") IconKind::SysProxy => (verge.sysproxy_tray_icon.unwrap_or(false), "sysproxy"),
&& let Ok(icon_data) = fs::read(common_icon_path).await IconKind::Tun => (verge.tun_tray_icon.unwrap_or(false), "tun"),
};
if custom_enabled
&& let Ok(Some(path)) = find_target_icons(icon_name)
&& let Ok(data) = fs::read(path).await
{ {
return (true, icon_data); return (true, data);
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or_else(|| "monochrome".into());
if tray_icon_colorful == "monochrome" {
(false, include_bytes!("../../../icons/tray-icon-mono.ico").to_vec())
} else {
(false, include_bytes!("../../../icons/tray-icon.ico").to_vec())
}
} }
#[cfg(not(target_os = "macos"))] Self::default_icon(verge, kind)
{
(false, include_bytes!("../../../icons/tray-icon.ico").to_vec())
}
} }
async fn get_sysproxy_tray_icon(verge: &IVerge) -> (bool, Vec<u8>) { fn default_icon(verge: &IVerge, kind: IconKind) -> (bool, Vec<u8>) {
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
if is_sysproxy_tray_icon
&& let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy")
&& let Ok(icon_data) = fs::read(sysproxy_icon_path).await
{
return (true, icon_data);
}
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or_else(|| "monochrome".into()); let is_mono = verge.tray_icon.as_deref().unwrap_or("monochrome") == "monochrome";
if tray_icon_colorful == "monochrome" { if is_mono {
( return (
false, false,
include_bytes!("../../../icons/tray-icon-sys-mono-new.ico").to_vec(), match kind {
) IconKind::Common => include_bytes!("../../../icons/tray-icon-mono.ico").to_vec(),
} else { IconKind::SysProxy => include_bytes!("../../../icons/tray-icon-sys-mono-new.ico").to_vec(),
(false, include_bytes!("../../../icons/tray-icon-sys.ico").to_vec()) IconKind::Tun => include_bytes!("../../../icons/tray-icon-tun-mono-new.ico").to_vec(),
},
);
} }
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
{ let _ = verge;
(false, include_bytes!("../../../icons/tray-icon-sys.ico").to_vec())
}
}
async fn get_tun_tray_icon(verge: &IVerge) -> (bool, Vec<u8>) { (
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false); false,
if is_tun_tray_icon match kind {
&& let Ok(Some(tun_icon_path)) = find_target_icons("tun") IconKind::Common => include_bytes!("../../../icons/tray-icon.ico").to_vec(),
&& let Ok(icon_data) = fs::read(tun_icon_path).await IconKind::SysProxy => include_bytes!("../../../icons/tray-icon-sys.ico").to_vec(),
{ IconKind::Tun => include_bytes!("../../../icons/tray-icon-tun.ico").to_vec(),
return (true, icon_data); },
} )
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or_else(|| "monochrome".into());
if tray_icon_colorful == "monochrome" {
(
false,
include_bytes!("../../../icons/tray-icon-tun-mono-new.ico").to_vec(),
)
} else {
(false, include_bytes!("../../../icons/tray-icon-tun.ico").to_vec())
}
}
#[cfg(not(target_os = "macos"))]
{
(false, include_bytes!("../../../icons/tray-icon-tun.ico").to_vec())
}
} }
} }
@ -248,34 +225,6 @@ impl Tray {
} }
/// 更新托盘图标 /// 更新托盘图标
#[cfg(target_os = "macos")]
pub async fn update_icon(&self, verge: &IVerge) -> Result<()> {
if handle::Handle::global().is_exiting() {
logging!(debug, Type::Tray, "应用正在退出,跳过托盘图标更新");
return Ok(());
}
let app_handle = handle::Handle::app_handle();
let Some(tray) = app_handle.tray_by_id("main") else {
logging!(warn, Type::Tray, "Failed to update tray icon: tray not found");
return Ok(());
};
let (_is_custom_icon, icon_bytes) = TrayState::get_tray_icon(verge).await;
let colorful = verge.tray_icon.clone().unwrap_or_else(|| "monochrome".into());
let is_colorful = colorful == "colorful";
logging_error!(
Type::Tray,
tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?))
);
logging_error!(Type::Tray, tray.set_icon_as_template(!is_colorful));
Ok(())
}
#[cfg(not(target_os = "macos"))]
pub async fn update_icon(&self, verge: &IVerge) -> Result<()> { pub async fn update_icon(&self, verge: &IVerge) -> Result<()> {
if handle::Handle::global().is_exiting() { if handle::Handle::global().is_exiting() {
logging!(debug, Type::Tray, "应用正在退出,跳过托盘图标更新"); logging!(debug, Type::Tray, "应用正在退出,跳过托盘图标更新");
@ -295,6 +244,13 @@ impl Tray {
Type::Tray, Type::Tray,
tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?)) tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?))
); );
#[cfg(target_os = "macos")]
{
let is_colorful = verge.tray_icon.as_deref().unwrap_or("monochrome") == "colorful";
logging_error!(Type::Tray, tray.set_icon_as_template(!is_colorful));
}
Ok(()) Ok(())
} }
@ -373,6 +329,12 @@ impl Tray {
Ok(()) Ok(())
} }
pub async fn update_menu_and_icon(&self) {
logging_error!(Type::Tray, self.update_menu().await);
let verge = Config::verge().await.data_arc();
logging_error!(Type::Tray, self.update_icon(&verge).await);
}
async fn create_tray_from_handle(&self, app_handle: &AppHandle) -> Result<()> { async fn create_tray_from_handle(&self, app_handle: &AppHandle) -> Result<()> {
if handle::Handle::global().is_exiting() { if handle::Handle::global().is_exiting() {
logging!(debug, Type::Tray, "应用正在退出,跳过托盘创建"); logging!(debug, Type::Tray, "应用正在退出,跳过托盘创建");

View File

@ -5,7 +5,7 @@ use crate::{
process::AsyncHandler, process::AsyncHandler,
utils::{self, resolve::reset_resolve_done}, utils::{self, resolve::reset_resolve_done},
}; };
use clash_verge_logging::{Type, logging, logging_error}; use clash_verge_logging::{Type, logging};
use serde_yaml_ng::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use smartstring::alias::String; use smartstring::alias::String;
@ -86,13 +86,7 @@ pub async fn change_clash_mode(mode: String) {
let clash_data = clash.data_arc(); let clash_data = clash.data_arc();
if clash_data.save_config().await.is_ok() { if clash_data.save_config().await.is_ok() {
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
logging_error!(Type::Tray, tray::Tray::global().update_menu().await); tray::Tray::global().update_menu_and_icon().await;
logging_error!(
Type::Tray,
tray::Tray::global()
.update_icon(&Config::verge().await.data_arc())
.await
);
} }
let is_auto_close_connection = Config::verge().await.data_arc().auto_close_connection.unwrap_or(false); let is_auto_close_connection = Config::verge().await.data_arc().auto_close_connection.unwrap_or(false);

View File

@ -20,13 +20,7 @@ pub async fn patch_clash(patch: &Mapping) -> Result<()> {
CoreManager::global().restart_core().await?; CoreManager::global().restart_core().await?;
} else { } else {
if patch.get("mode").is_some() { if patch.get("mode").is_some() {
logging_error!(Type::Tray, tray::Tray::global().update_menu().await); tray::Tray::global().update_menu_and_icon().await;
logging_error!(
Type::Tray,
tray::Tray::global()
.update_icon(&Config::verge().await.data_arc())
.await
);
} }
Config::runtime().await.edit_draft(|d| d.patch_config(patch)); Config::runtime().await.edit_draft(|d| d.patch_config(patch));
CoreManager::global().update_config().await?; CoreManager::global().update_config().await?;