tray.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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",
  51. t!("Copy Env", "复制环境变量"),
  52. ))
  53. .add_submenu(SystemTraySubmenu::new(
  54. t!("Open Dir", "打开目录"),
  55. SystemTrayMenu::new()
  56. .add_item(CustomMenuItem::new(
  57. "open_app_dir",
  58. t!("App Dir", "应用目录"),
  59. ))
  60. .add_item(CustomMenuItem::new(
  61. "open_core_dir",
  62. t!("Core Dir", "内核目录"),
  63. ))
  64. .add_item(CustomMenuItem::new(
  65. "open_logs_dir",
  66. t!("Logs Dir", "日志目录"),
  67. )),
  68. ))
  69. .add_submenu(SystemTraySubmenu::new(
  70. t!("More", "更多"),
  71. SystemTrayMenu::new()
  72. .add_item(CustomMenuItem::new(
  73. "restart_clash",
  74. t!("Restart Clash", "重启 Clash"),
  75. ))
  76. .add_item(CustomMenuItem::new(
  77. "restart_app",
  78. t!("Restart App", "重启应用"),
  79. ))
  80. .add_item(
  81. CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
  82. ),
  83. ))
  84. .add_native_item(SystemTrayMenuItem::Separator)
  85. .add_item(CustomMenuItem::new("quit", t!("Quit", "退出")).accelerator("CmdOrControl+Q"))
  86. }
  87. pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
  88. app_handle
  89. .tray_handle()
  90. .set_menu(Tray::tray_menu(app_handle))?;
  91. Tray::update_part(app_handle)?;
  92. Ok(())
  93. }
  94. pub fn update_part(app_handle: &AppHandle) -> Result<()> {
  95. let zh = { Config::verge().latest().language == Some("zh".into()) };
  96. let version = app_handle.package_info().version.to_string();
  97. macro_rules! t {
  98. ($en: expr, $zh: expr) => {
  99. if zh {
  100. $zh
  101. } else {
  102. $en
  103. }
  104. };
  105. }
  106. let mode = {
  107. Config::clash()
  108. .latest()
  109. .0
  110. .get("mode")
  111. .map(|val| val.as_str().unwrap_or("rule"))
  112. .unwrap_or("rule")
  113. .to_owned()
  114. };
  115. let tray = app_handle.tray_handle();
  116. let _ = tray.get_item("rule_mode").set_selected(mode == "rule");
  117. let _ = tray.get_item("global_mode").set_selected(mode == "global");
  118. let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
  119. let _ = tray.get_item("script_mode").set_selected(mode == "script");
  120. let verge = Config::verge();
  121. let verge = verge.latest();
  122. let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
  123. let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
  124. let mut indication_icon = if *system_proxy {
  125. #[cfg(not(target_os = "macos"))]
  126. let icon = include_bytes!("../../icons/tray-icon-sys.png").to_vec();
  127. #[cfg(target_os = "macos")]
  128. let icon = include_bytes!("../../icons/mac-tray-icon-sys.png").to_vec();
  129. icon
  130. } else {
  131. include_bytes!("../../icons/tray-icon.png").to_vec()
  132. };
  133. if *tun_mode {
  134. #[cfg(not(target_os = "macos"))]
  135. let icon = include_bytes!("../../icons/tray-icon-tun.png").to_vec();
  136. #[cfg(target_os = "macos")]
  137. let icon = include_bytes!("../../icons/mac-tray-icon-tun.png").to_vec();
  138. indication_icon = icon
  139. }
  140. let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
  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. let _ = tray.set_tooltip(&format!(
  150. "Clash Verge {version}\n{}: {}\n{}: {}",
  151. t!("System Proxy", "系统代理"),
  152. switch_map[system_proxy],
  153. t!("TUN Mode", "Tun 模式"),
  154. switch_map[tun_mode]
  155. ));
  156. Ok(())
  157. }
  158. pub fn on_left_click(app_handle: &AppHandle) {
  159. let tray_event = { Config::verge().latest().tray_event.clone() };
  160. let tray_event = tray_event.unwrap_or("main_window".into());
  161. match tray_event.as_str() {
  162. "system_proxy" => feat::toggle_system_proxy(),
  163. "tun_mode" => feat::toggle_tun_mode(),
  164. "main_window" => resolve::create_window(app_handle),
  165. _ => {}
  166. }
  167. }
  168. pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
  169. match event {
  170. SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
  171. SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
  172. mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
  173. let mode = &mode[0..mode.len() - 5];
  174. feat::change_clash_mode(mode.into());
  175. }
  176. "open_window" => resolve::create_window(app_handle),
  177. "system_proxy" => feat::toggle_system_proxy(),
  178. "tun_mode" => feat::toggle_tun_mode(),
  179. "copy_env" => feat::copy_clash_env(app_handle),
  180. "open_app_dir" => crate::log_err!(cmds::open_app_dir()),
  181. "open_core_dir" => crate::log_err!(cmds::open_core_dir()),
  182. "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
  183. "restart_clash" => feat::restart_clash_core(),
  184. "restart_app" => api::process::restart(&app_handle.env()),
  185. "quit" => {
  186. let _ = resolve::save_window_size_position(app_handle, true);
  187. resolve::resolve_reset();
  188. api::process::kill_children();
  189. app_handle.exit(0);
  190. std::process::exit(0);
  191. }
  192. _ => {}
  193. },
  194. _ => {}
  195. }
  196. }
  197. }