tray.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. use crate::{cmds, config::Config, feat, utils::resolve};
  2. use anyhow::Result;
  3. use tauri::{
  4. api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
  5. SystemTraySubmenu,
  6. };
  7. pub struct Tray {}
  8. impl Tray {
  9. pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu {
  10. let zh = { Config::verge().latest().language == Some("zh".into()) };
  11. let version = app_handle.package_info().version.to_string();
  12. macro_rules! t {
  13. ($en: expr, $zh: expr) => {
  14. if zh {
  15. $zh
  16. } else {
  17. $en
  18. }
  19. };
  20. }
  21. SystemTrayMenu::new()
  22. .add_item(CustomMenuItem::new(
  23. "open_window",
  24. t!("Dashboard", "打开面板"),
  25. ))
  26. .add_native_item(SystemTrayMenuItem::Separator)
  27. .add_item(CustomMenuItem::new(
  28. "rule_mode",
  29. t!("Rule Mode", "规则模式"),
  30. ))
  31. .add_item(CustomMenuItem::new(
  32. "global_mode",
  33. t!("Global Mode", "全局模式"),
  34. ))
  35. .add_item(CustomMenuItem::new(
  36. "direct_mode",
  37. t!("Direct Mode", "直连模式"),
  38. ))
  39. .add_item(CustomMenuItem::new(
  40. "script_mode",
  41. t!("Script Mode", "脚本模式"),
  42. ))
  43. .add_native_item(SystemTrayMenuItem::Separator)
  44. .add_item(CustomMenuItem::new(
  45. "system_proxy",
  46. t!("System Proxy", "系统代理"),
  47. ))
  48. .add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
  49. .add_item(CustomMenuItem::new(
  50. "copy_env_sh",
  51. t!("Copy Env (sh)", "复制环境变量(sh)"),
  52. ))
  53. .add_item(CustomMenuItem::new(
  54. "copy_env_cmd",
  55. t!("Copy Env (CMD)", "复制环境变量(CMD)"),
  56. ))
  57. .add_item(CustomMenuItem::new(
  58. "copy_env_ps",
  59. t!("Copy Env (PS)", "复制环境变量(PS)"),
  60. ))
  61. .add_submenu(SystemTraySubmenu::new(
  62. t!("Open Dir", "打开目录"),
  63. SystemTrayMenu::new()
  64. .add_item(CustomMenuItem::new(
  65. "open_app_dir",
  66. t!("App Dir", "应用目录"),
  67. ))
  68. .add_item(CustomMenuItem::new(
  69. "open_core_dir",
  70. t!("Core Dir", "内核目录"),
  71. ))
  72. .add_item(CustomMenuItem::new(
  73. "open_logs_dir",
  74. t!("Logs Dir", "日志目录"),
  75. )),
  76. ))
  77. .add_submenu(SystemTraySubmenu::new(
  78. t!("More", "更多"),
  79. SystemTrayMenu::new()
  80. .add_item(CustomMenuItem::new(
  81. "restart_clash",
  82. t!("Restart Clash", "重启 Clash"),
  83. ))
  84. .add_item(CustomMenuItem::new(
  85. "restart_app",
  86. t!("Restart App", "重启应用"),
  87. ))
  88. .add_item(
  89. CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
  90. ),
  91. ))
  92. .add_native_item(SystemTrayMenuItem::Separator)
  93. .add_item(CustomMenuItem::new("quit", t!("Quit", "退出")).accelerator("CmdOrControl+Q"))
  94. }
  95. pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
  96. app_handle
  97. .tray_handle()
  98. .set_menu(Tray::tray_menu(app_handle))?;
  99. Tray::update_part(app_handle)?;
  100. Ok(())
  101. }
  102. pub fn update_part(app_handle: &AppHandle) -> Result<()> {
  103. let zh = { Config::verge().latest().language == Some("zh".into()) };
  104. let version = app_handle.package_info().version.to_string();
  105. macro_rules! t {
  106. ($en: expr, $zh: expr) => {
  107. if zh {
  108. $zh
  109. } else {
  110. $en
  111. }
  112. };
  113. }
  114. let mode = {
  115. Config::clash()
  116. .latest()
  117. .0
  118. .get("mode")
  119. .map(|val| val.as_str().unwrap_or("rule"))
  120. .unwrap_or("rule")
  121. .to_owned()
  122. };
  123. let tray = app_handle.tray_handle();
  124. let _ = tray.get_item("rule_mode").set_selected(mode == "rule");
  125. let _ = tray.get_item("global_mode").set_selected(mode == "global");
  126. let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
  127. let _ = tray.get_item("script_mode").set_selected(mode == "script");
  128. let verge = Config::verge();
  129. let verge = verge.latest();
  130. let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
  131. let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
  132. #[cfg(target_os = "windows")]
  133. {
  134. let indication_icon = if *system_proxy {
  135. include_bytes!("../../icons/win-tray-icon-activated.png").to_vec()
  136. } else {
  137. include_bytes!("../../icons/win-tray-icon.png").to_vec()
  138. };
  139. let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
  140. }
  141. let _ = tray.get_item("system_proxy").set_selected(*system_proxy);
  142. let _ = tray.get_item("tun_mode").set_selected(*tun_mode);
  143. let switch_map = {
  144. let mut map = std::collections::HashMap::new();
  145. map.insert(true, "on");
  146. map.insert(false, "off");
  147. map
  148. };
  149. #[cfg(not(target_os = "linux"))]
  150. let _ = tray.set_tooltip(&format!(
  151. "Clash Verge {version}\n{}: {}\n{}: {}",
  152. t!("System Proxy", "系统代理"),
  153. switch_map[system_proxy],
  154. t!("TUN Mode", "Tun 模式"),
  155. switch_map[tun_mode]
  156. ));
  157. Ok(())
  158. }
  159. pub fn on_left_click(app_handle: &AppHandle) {
  160. let tray_event = { Config::verge().latest().tray_event.clone() };
  161. let tray_event = tray_event.unwrap_or("main_window".into());
  162. match tray_event.as_str() {
  163. "system_proxy" => feat::toggle_system_proxy(),
  164. "tun_mode" => feat::toggle_tun_mode(),
  165. _ => resolve::create_window(app_handle),
  166. }
  167. }
  168. pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
  169. match event {
  170. #[cfg(not(target_os = "linux"))]
  171. SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
  172. SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
  173. mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
  174. let mode = &mode[0..mode.len() - 5];
  175. feat::change_clash_mode(mode.into());
  176. }
  177. "open_window" => resolve::create_window(app_handle),
  178. "system_proxy" => feat::toggle_system_proxy(),
  179. "tun_mode" => feat::toggle_tun_mode(),
  180. "copy_env_sh" => feat::copy_clash_env("sh"),
  181. #[cfg(target_os = "windows")]
  182. "copy_env_cmd" => feat::copy_clash_env("cmd"),
  183. #[cfg(target_os = "windows")]
  184. "copy_env_ps" => feat::copy_clash_env("ps"),
  185. "open_app_dir" => crate::log_err!(cmds::open_app_dir()),
  186. "open_core_dir" => crate::log_err!(cmds::open_core_dir()),
  187. "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
  188. "restart_clash" => feat::restart_clash_core(),
  189. "restart_app" => api::process::restart(&app_handle.env()),
  190. "quit" => {
  191. let _ = resolve::save_window_size_position(app_handle, true);
  192. resolve::resolve_reset();
  193. api::process::kill_children();
  194. app_handle.exit(0);
  195. std::process::exit(0);
  196. }
  197. _ => {}
  198. },
  199. _ => {}
  200. }
  201. }
  202. }