feat: enhance tray icon handling with caching and speed rate rendering

This commit is contained in:
Tunglies 2025-03-22 23:00:45 +08:00
parent c30f54609d
commit 8aabcd77a5
2 changed files with 66 additions and 28 deletions

View File

@ -21,6 +21,10 @@ use parking_lot::RwLock;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub use speed_rate::{SpeedRate, Traffic}; pub use speed_rate::{SpeedRate, Traffic};
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use std::collections::hash_map::DefaultHasher;
#[cfg(target_os = "macos")]
use std::hash::{Hash, Hasher};
#[cfg(target_os = "macos")]
use std::sync::Arc; use std::sync::Arc;
use tauri::{ use tauri::{
menu::{CheckMenuItem, IsMenuItem, MenuEvent, MenuItem, PredefinedMenuItem, Submenu}, menu::{CheckMenuItem, IsMenuItem, MenuEvent, MenuItem, PredefinedMenuItem, Submenu},
@ -36,6 +40,9 @@ pub struct Tray {
pub speed_rate: Arc<Mutex<Option<SpeedRate>>>, pub speed_rate: Arc<Mutex<Option<SpeedRate>>>,
shutdown_tx: Arc<RwLock<Option<broadcast::Sender<()>>>>, shutdown_tx: Arc<RwLock<Option<broadcast::Sender<()>>>>,
is_subscribed: Arc<RwLock<bool>>, is_subscribed: Arc<RwLock<bool>>,
pub icon_hash: Arc<Mutex<Option<u64>>>,
pub icon_cache: Arc<Mutex<Option<Vec<u8>>>>,
pub rate_cache: Arc<Mutex<Option<Rate>>>,
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
@ -50,6 +57,9 @@ impl Tray {
speed_rate: Arc::new(Mutex::new(None)), speed_rate: Arc::new(Mutex::new(None)),
shutdown_tx: Arc::new(RwLock::new(None)), shutdown_tx: Arc::new(RwLock::new(None)),
is_subscribed: Arc::new(RwLock::new(false)), is_subscribed: Arc::new(RwLock::new(false)),
icon_hash: Arc::new(Mutex::new(None)),
icon_cache: Arc::new(Mutex::new(None)),
rate_cache: Arc::new(Mutex::new(None)),
}); });
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
@ -228,34 +238,59 @@ impl Tray {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
let enable_tray_speed = Config::verge().latest().enable_tray_speed.unwrap_or(true); let enable_tray_speed = verge.enable_tray_speed.unwrap_or(true);
let is_colorful = tray_icon == "colorful"; let is_colorful = tray_icon == "colorful";
// 处理图标和速率 let icon_hash = {
let final_icon_bytes = if enable_tray_speed { let mut hasher = DefaultHasher::new();
let rate = rate.or_else(|| { icon_bytes.clone().hash(&mut hasher);
self.speed_rate hasher.finish()
.lock()
.as_ref()
.and_then(|speed_rate| speed_rate.get_curent_rate())
});
// 使用新的方法渲染图标和速率
SpeedRate::add_speed_text(icon_bytes, rate)?
} else {
icon_bytes
}; };
// 设置系统托盘图标 let mut icon_hash_guard = self.icon_hash.lock();
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&final_icon_bytes)?)); let mut icon_bytes_guard = self.icon_cache.lock();
// 只对单色图标使用 template 模式 if *icon_hash_guard != Some(icon_hash) {
let _ = tray.set_icon_as_template(!is_colorful); *icon_hash_guard = Some(icon_hash);
*icon_bytes_guard = Some(icon_bytes.clone());
}
if !enable_tray_speed {
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(
&(*icon_bytes_guard).clone().unwrap(),
)?));
let _ = tray.set_icon_as_template(!is_colorful);
return Ok(());
}
let rate = if let Some(rate) = rate {
Some(rate)
} else {
let guard = self.speed_rate.lock();
if let Some(rate) = guard.as_ref().unwrap().get_curent_rate() {
Some(rate)
} else {
Some(Rate::default())
}
};
let mut rate_guard = self.rate_cache.lock();
if *rate_guard != rate {
*rate_guard = rate;
let bytes = icon_bytes_guard.as_ref().unwrap();
let rate = rate_guard.as_ref();
let rate_bytes = SpeedRate::add_speed_text(bytes, rate).unwrap();
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&rate_bytes)?));
let _ = tray.set_icon_as_template(!is_colorful);
}
Ok(())
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?)); {
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?));
Ok(()) Ok(())
}
} }
/// 更新托盘提示 /// 更新托盘提示

View File

@ -15,7 +15,6 @@ use tungstenite::client::IntoClientRequest;
pub struct SpeedRate { pub struct SpeedRate {
rate: Arc<Mutex<(Rate, Rate)>>, rate: Arc<Mutex<(Rate, Rate)>>,
last_update: Arc<Mutex<std::time::Instant>>, last_update: Arc<Mutex<std::time::Instant>>,
// 移除 base_image不再缓存原始图像
} }
impl SpeedRate { impl SpeedRate {
@ -77,20 +76,24 @@ impl SpeedRate {
} }
// 分离图标加载和速率渲染 // 分离图标加载和速率渲染
pub fn add_speed_text(icon_bytes: Vec<u8>, rate: Option<Rate>) -> Result<Vec<u8>> { pub fn add_speed_text<'a>(icon_bytes: &'a Vec<u8>, rate: Option<&'a Rate>) -> Result<Vec<u8>> {
let rate = rate.unwrap_or(Rate { up: 0, down: 0 }); let rate = rate.unwrap_or(&Rate { up: 0, down: 0 });
// 加载原始图标 // 加载原始图标
let icon_image = image::load_from_memory(&icon_bytes)?; let icon_image = image::load_from_memory(icon_bytes)?;
let (icon_width, icon_height) = (icon_image.width(), icon_image.height()); let (icon_width, icon_height) = (icon_image.width(), icon_image.height());
// 判断是否为彩色图标 // 判断是否为彩色图标
let is_colorful = let is_colorful =
!crate::utils::help::is_monochrome_image_from_bytes(&icon_bytes).unwrap_or(false); !crate::utils::help::is_monochrome_image_from_bytes(icon_bytes).unwrap_or(false);
// 增加文本宽度和间距 // 增加文本宽度和间距
let text_width = 580; // 文本区域宽度 let total_width = if icon_width < 580 {
let total_width = icon_width + text_width; let text_width = 580; // 文本区域宽度
icon_width + text_width
} else {
icon_width
};
// 创建新的透明画布 // 创建新的透明画布
let mut combined_image = RgbaImage::new(total_width, icon_height); let mut combined_image = RgbaImage::new(total_width, icon_height);