tray.rs 7.7 KB

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