news-classifier/client/src-tauri/src/tray.rs

151 lines
6.0 KiB
Rust

#[cfg(desktop)]
use tauri::{
image::Image,
menu::{CheckMenuItemBuilder, Menu, MenuItemBuilder},
tray::{MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent},
AppHandle, Emitter, Manager,
};
#[cfg(desktop)]
use tauri_plugin_autostart::ManagerExt;
#[cfg(desktop)]
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
#[cfg(desktop)]
fn load_tray_icon() -> Image<'static> {
let icon_data = include_bytes!("../icons/icon.ico");
// Parse ICO file and extract the largest icon
let ico_file = ico::IconDir::read(std::io::Cursor::new(icon_data))
.expect("Failed to read ICO file");
let entry = &ico_file.entries()[0];
let image = entry.decode().expect("Failed to decode ICO entry");
Image::new_owned(image.rgba_data().to_vec(), image.width(), image.height())
}
#[cfg(desktop)]
pub fn create_tray_menu(app_handle: &AppHandle) -> Result<TrayIcon, tauri::Error> {
let autostart_enabled = app_handle.autolaunch().is_enabled().unwrap_or(false);
// Create menu items
let show = MenuItemBuilder::with_id("show", "显示").build(app_handle)?;
let hide = MenuItemBuilder::with_id("hide", "隐藏").build(app_handle)?;
let autostart = CheckMenuItemBuilder::with_id("autostart", "开机自启")
.checked(autostart_enabled)
.build(app_handle)?;
let quit = MenuItemBuilder::with_id("quit", "退出").build(app_handle)?;
let about = MenuItemBuilder::with_id("about", "关于").build(app_handle)?;
// Clone handles for moving into closures
let autostart_for_event = autostart.clone();
let autostart_for_sync = autostart.clone();
// Create the menu
let tray_menu = Menu::with_items(app_handle, &[&show, &hide, &autostart, &quit, &about])?;
// Get app version
let version = app_handle.package_info().version.to_string();
// Create the tray icon
let icon = load_tray_icon();
let tray_icon = TrayIconBuilder::new()
.icon(icon)
.menu(&tray_menu)
.tooltip(&format!("News Classifier v{}", version))
.on_menu_event(move |app_handle, event| {
match event.id().as_ref() {
"quit" => {
std::process::exit(0);
}
"show" => {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
"hide" => {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.hide();
}
}
"autostart" => {
let autolaunch_manager = app_handle.autolaunch();
let autostart_item = autostart_for_event.clone();
if autolaunch_manager.is_enabled().unwrap_or(false) {
if let Err(e) = autolaunch_manager.disable() {
eprintln!("Failed to disable autostart: {}", e);
} else {
let _ = autostart_item.set_checked(false);
let _ = app_handle.emit("autostart-changed", false);
}
} else {
if let Err(e) = autolaunch_manager.enable() {
eprintln!("Failed to enable autostart: {}", e);
} else {
let _ = autostart_item.set_checked(true);
let _ = app_handle.emit("autostart-changed", true);
}
}
}
"about" => {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
window
.dialog()
.message(format!("应用名称: News Classifier\n版本: {}", version))
.title("关于 News Classifier")
.buttons(MessageDialogButtons::Ok)
.show(|_| {});
}
}
_ => {}
}
})
.on_tray_icon_event({
let app_handle = app_handle.clone();
move |_tray_id, event| {
if let TrayIconEvent::Click {
button,
button_state,
..
} = event
{
if button == MouseButton::Left && button_state == MouseButtonState::Up {
if let Some(window) = app_handle.get_webview_window("main") {
match window.is_visible() {
Ok(true) => {
let _ = window.hide();
}
Ok(false) => {
let _ = window.show();
let _ = window.set_focus();
}
Err(e) => eprintln!("Failed to get window visibility: {}", e),
}
}
}
}
}
})
.build(app_handle)?;
// Spawn a background task to sync autostart state
let app_handle = app_handle.clone();
tauri::async_runtime::spawn(async move {
loop {
// Sync autostart menu item state with actual system state
if let Ok(is_enabled) = app_handle.autolaunch().is_enabled() {
let is_checked = autostart_for_sync.is_checked().unwrap_or(false);
if is_checked != is_enabled {
let _ = autostart_for_sync.set_checked(is_enabled);
}
}
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
}
});
Ok(tray_icon)
}