Skip to content
Snippets Groups Projects
tray.rs 6.42 KiB
Newer Older
GyDi's avatar
GyDi committed
use crate::{cmds, config::Config, feat, utils::resolve};
GyDi's avatar
GyDi committed
use anyhow::Result;
use tauri::{
GyDi's avatar
GyDi committed
    api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
    SystemTraySubmenu,
};

pub struct Tray {}

impl Tray {
GyDi's avatar
GyDi committed
    pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu {
GyDi's avatar
GyDi committed
        let zh = { Config::verge().latest().language == Some("zh".into()) };
GyDi's avatar
GyDi committed
        let version = app_handle.package_info().version.to_string();
GyDi's avatar
GyDi committed

        macro_rules! t {
            ($en: expr, $zh: expr) => {
                if zh {
                    $zh
                } else {
                    $en
                }
            };
GyDi's avatar
GyDi committed
        }

        SystemTrayMenu::new()
            .add_item(CustomMenuItem::new(
                "open_window",
                t!("Dashboard", "打开面板"),
            ))
            .add_native_item(SystemTrayMenuItem::Separator)
            .add_item(CustomMenuItem::new(
                "rule_mode",
                t!("Rule Mode", "规则模式"),
            ))
            .add_item(CustomMenuItem::new(
                "global_mode",
                t!("Global Mode", "全局模式"),
            ))
            .add_item(CustomMenuItem::new(
                "direct_mode",
                t!("Direct Mode", "直连模式"),
            ))
            .add_item(CustomMenuItem::new(
                "script_mode",
                t!("Script Mode", "脚本模式"),
            ))
            .add_native_item(SystemTrayMenuItem::Separator)
            .add_item(CustomMenuItem::new(
                "system_proxy",
                t!("System Proxy", "系统代理"),
            ))
            .add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
            .add_item(CustomMenuItem::new(
                "copy_env",
                t!("Copy Env", "复制环境变量"),
            ))
            .add_submenu(SystemTraySubmenu::new(
                t!("Open Dir", "打开目录"),
                SystemTrayMenu::new()
                    .add_item(CustomMenuItem::new(
                        "open_app_dir",
                        t!("App Dir", "应用目录"),
                    ))
                    .add_item(CustomMenuItem::new(
                        "open_core_dir",
                        t!("Core Dir", "内核目录"),
                    ))
                    .add_item(CustomMenuItem::new(
                        "open_logs_dir",
                        t!("Logs Dir", "日志目录"),
                    )),
            ))
            .add_submenu(SystemTraySubmenu::new(
                t!("More", "更多"),
                SystemTrayMenu::new()
                    .add_item(CustomMenuItem::new(
                        "restart_clash",
                        t!("Restart Clash", "重启 Clash"),
                    ))
                    .add_item(CustomMenuItem::new(
                        "restart_app",
                        t!("Restart App", "重启应用"),
                    ))
                    .add_item(
                        CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
                    ),
            ))
            .add_native_item(SystemTrayMenuItem::Separator)
            .add_item(CustomMenuItem::new("quit", t!("Quit", "退出")).accelerator("CmdOrControl+Q"))
GyDi's avatar
GyDi committed
    pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
        app_handle
            .tray_handle()
            .set_menu(Tray::tray_menu(app_handle))?;
        Tray::update_part(app_handle)?;
        Ok(())
    }
GyDi's avatar
GyDi committed
    pub fn update_part(app_handle: &AppHandle) -> Result<()> {
GyDi's avatar
GyDi committed
        let mode = {
GyDi's avatar
GyDi committed
            Config::clash()
                .latest()
                .0
GyDi's avatar
GyDi committed
                .get("mode")
                .map(|val| val.as_str().unwrap_or("rule"))
                .unwrap_or("rule")
                .to_owned()
        };
GyDi's avatar
GyDi committed
        let tray = app_handle.tray_handle();
GyDi's avatar
GyDi committed
        let _ = tray.get_item("rule_mode").set_selected(mode == "rule");
        let _ = tray.get_item("global_mode").set_selected(mode == "global");
        let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
        let _ = tray.get_item("script_mode").set_selected(mode == "script");
GyDi's avatar
GyDi committed
        let verge = Config::verge();
        let verge = verge.latest();
GyDi's avatar
GyDi committed
        let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
        let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
GyDi's avatar
GyDi committed
        #[cfg(target_os = "windows")]
GyDi's avatar
GyDi committed
        {
            let indication_icon = if *system_proxy {
                include_bytes!("../../icons/win-tray-icon-activated.png").to_vec()
GyDi's avatar
GyDi committed
            } else {
                include_bytes!("../../icons/win-tray-icon.png").to_vec()
GyDi's avatar
GyDi committed
            };
GyDi's avatar
GyDi committed
            let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
        }
GyDi's avatar
GyDi committed
        let _ = tray.get_item("system_proxy").set_selected(*system_proxy);
        let _ = tray.get_item("tun_mode").set_selected(*tun_mode);
GyDi's avatar
GyDi committed
        Ok(())
    }
GyDi's avatar
GyDi committed
    pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
        match event {
            SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
                mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
                    let mode = &mode[0..mode.len() - 5];
GyDi's avatar
GyDi committed
                    feat::change_clash_mode(mode.into());
GyDi's avatar
GyDi committed
                }
GyDi's avatar
GyDi committed
                "open_window" => resolve::create_window(app_handle),
GyDi's avatar
GyDi committed
                "system_proxy" => feat::toggle_system_proxy(),
                "tun_mode" => feat::toggle_tun_mode(),
                "copy_env" => feat::copy_clash_env(),
GyDi's avatar
GyDi committed
                "open_app_dir" => crate::log_err!(cmds::open_app_dir()),
                "open_core_dir" => crate::log_err!(cmds::open_core_dir()),
                "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
GyDi's avatar
GyDi committed
                "restart_clash" => feat::restart_clash_core(),
                "restart_app" => api::process::restart(&app_handle.env()),
                "quit" => {
                    let _ = resolve::save_window_size_position(app_handle, true);
GyDi's avatar
GyDi committed
                    resolve::resolve_reset();
                    api::process::kill_children();
                    app_handle.exit(0);
GyDi's avatar
GyDi committed
                    std::process::exit(0);
GyDi's avatar
GyDi committed
                }
                _ => {}
            },
            #[cfg(target_os = "windows")]
            SystemTrayEvent::LeftClick { .. } => {
                resolve::create_window(app_handle);
            }
GyDi's avatar
GyDi committed
            _ => {}