tray.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. use crate::{
  2. cmds,
  3. config::Config,
  4. feat,
  5. utils::{dirs, resolve},
  6. };
  7. use anyhow::Result;
  8. use tauri::{
  9. api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
  10. SystemTraySubmenu,
  11. };
  12. pub struct Tray {}
  13. impl Tray {
  14. pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu {
  15. let zh = { Config::verge().latest().language == Some("zh".into()) };
  16. let version = app_handle.package_info().version.to_string();
  17. macro_rules! t {
  18. ($en: expr, $zh: expr) => {
  19. if zh {
  20. $zh
  21. } else {
  22. $en
  23. }
  24. };
  25. }
  26. SystemTrayMenu::new()
  27. .add_item(CustomMenuItem::new(
  28. "open_window",
  29. t!("Dashboard", "打开面板"),
  30. ))
  31. .add_native_item(SystemTrayMenuItem::Separator)
  32. .add_item(CustomMenuItem::new(
  33. "rule_mode",
  34. t!("Rule Mode", "规则模式"),
  35. ))
  36. .add_item(CustomMenuItem::new(
  37. "global_mode",
  38. t!("Global Mode", "全局模式"),
  39. ))
  40. .add_item(CustomMenuItem::new(
  41. "direct_mode",
  42. t!("Direct Mode", "直连模式"),
  43. ))
  44. .add_native_item(SystemTrayMenuItem::Separator)
  45. .add_item(CustomMenuItem::new(
  46. "system_proxy",
  47. t!("System Proxy", "系统代理"),
  48. ))
  49. .add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
  50. .add_item(CustomMenuItem::new(
  51. "copy_env",
  52. t!("Copy Env", "复制环境变量"),
  53. ))
  54. .add_submenu(SystemTraySubmenu::new(
  55. t!("Open Dir", "打开目录"),
  56. SystemTrayMenu::new()
  57. .add_item(CustomMenuItem::new(
  58. "open_app_dir",
  59. t!("App Dir", "应用目录"),
  60. ))
  61. .add_item(CustomMenuItem::new(
  62. "open_core_dir",
  63. t!("Core Dir", "内核目录"),
  64. ))
  65. .add_item(CustomMenuItem::new(
  66. "open_logs_dir",
  67. t!("Logs Dir", "日志目录"),
  68. )),
  69. ))
  70. .add_submenu(SystemTraySubmenu::new(
  71. t!("More", "更多"),
  72. SystemTrayMenu::new()
  73. .add_item(CustomMenuItem::new(
  74. "restart_clash",
  75. t!("Restart Clash", "重启 Clash"),
  76. ))
  77. .add_item(CustomMenuItem::new(
  78. "restart_app",
  79. t!("Restart App", "重启应用"),
  80. ))
  81. .add_item(
  82. CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
  83. ),
  84. ))
  85. .add_native_item(SystemTrayMenuItem::Separator)
  86. .add_item(CustomMenuItem::new("quit", t!("Quit", "退出")).accelerator("CmdOrControl+Q"))
  87. }
  88. pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
  89. app_handle
  90. .tray_handle()
  91. .set_menu(Tray::tray_menu(app_handle))?;
  92. Tray::update_part(app_handle)?;
  93. Ok(())
  94. }
  95. pub fn update_part(app_handle: &AppHandle) -> Result<()> {
  96. let zh = { Config::verge().latest().language == Some("zh".into()) };
  97. let version = app_handle.package_info().version.to_string();
  98. macro_rules! t {
  99. ($en: expr, $zh: expr) => {
  100. if zh {
  101. $zh
  102. } else {
  103. $en
  104. }
  105. };
  106. }
  107. let mode = {
  108. Config::clash()
  109. .latest()
  110. .0
  111. .get("mode")
  112. .map(|val| val.as_str().unwrap_or("rule"))
  113. .unwrap_or("rule")
  114. .to_owned()
  115. };
  116. let tray = app_handle.tray_handle();
  117. let _ = tray.get_item("rule_mode").set_selected(mode == "rule");
  118. let _ = tray.get_item("global_mode").set_selected(mode == "global");
  119. let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
  120. #[cfg(target_os = "linux")]
  121. match mode.as_str() {
  122. "rule" => {
  123. let _ = tray
  124. .get_item("rule_mode")
  125. .set_title(t!("Rule Mode ✔", "规则模式 ✔"));
  126. let _ = tray
  127. .get_item("global_mode")
  128. .set_title(t!("Global Mode", "全局模式"));
  129. let _ = tray
  130. .get_item("direct_mode")
  131. .set_title(t!("Direct Mode", "直连模式"));
  132. }
  133. "global" => {
  134. let _ = tray
  135. .get_item("rule_mode")
  136. .set_title(t!("Rule Mode", "规则模式"));
  137. let _ = tray
  138. .get_item("global_mode")
  139. .set_title(t!("Global Mode ✔", "全局模式 ✔"));
  140. let _ = tray
  141. .get_item("direct_mode")
  142. .set_title(t!("Direct Mode", "直连模式"));
  143. }
  144. "direct" => {
  145. let _ = tray
  146. .get_item("rule_mode")
  147. .set_title(t!("Rule Mode", "规则模式"));
  148. let _ = tray
  149. .get_item("global_mode")
  150. .set_title(t!("Global Mode", "全局模式"));
  151. let _ = tray
  152. .get_item("direct_mode")
  153. .set_title(t!("Direct Mode ✔", "直连模式 ✔"));
  154. }
  155. _ => {}
  156. }
  157. let verge = Config::verge();
  158. let verge = verge.latest();
  159. let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
  160. let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
  161. let common_tray_icon = verge.common_tray_icon.as_ref().unwrap_or(&false);
  162. let sysproxy_tray_icon = verge.sysproxy_tray_icon.as_ref().unwrap_or(&false);
  163. let tun_tray_icon = verge.tun_tray_icon.as_ref().unwrap_or(&false);
  164. let mut indication_icon = if *system_proxy {
  165. #[cfg(not(target_os = "macos"))]
  166. let mut icon = include_bytes!("../../icons/tray-icon-sys.png").to_vec();
  167. #[cfg(target_os = "macos")]
  168. let mut icon = include_bytes!("../../icons/mac-tray-icon-sys.png").to_vec();
  169. if *sysproxy_tray_icon {
  170. let path = dirs::app_home_dir()?.join("icons").join("sysproxy.png");
  171. if path.exists() {
  172. icon = std::fs::read(path).unwrap();
  173. }
  174. }
  175. icon
  176. } else {
  177. #[cfg(not(target_os = "macos"))]
  178. let mut icon = include_bytes!("../../icons/tray-icon.png").to_vec();
  179. #[cfg(target_os = "macos")]
  180. let mut icon = include_bytes!("../../icons/mac-tray-icon.png").to_vec();
  181. if *common_tray_icon {
  182. let path = dirs::app_home_dir()?.join("icons").join("common.png");
  183. if path.exists() {
  184. icon = std::fs::read(path).unwrap();
  185. }
  186. }
  187. icon
  188. };
  189. if *tun_mode {
  190. #[cfg(not(target_os = "macos"))]
  191. let mut icon = include_bytes!("../../icons/tray-icon-tun.png").to_vec();
  192. #[cfg(target_os = "macos")]
  193. let mut icon = include_bytes!("../../icons/mac-tray-icon-tun.png").to_vec();
  194. if *tun_tray_icon {
  195. let path = dirs::app_home_dir()?.join("icons").join("tun.png");
  196. if path.exists() {
  197. icon = std::fs::read(path).unwrap();
  198. }
  199. }
  200. indication_icon = icon
  201. }
  202. let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
  203. let _ = tray.get_item("system_proxy").set_selected(*system_proxy);
  204. let _ = tray.get_item("tun_mode").set_selected(*tun_mode);
  205. #[cfg(target_os = "linux")]
  206. {
  207. if *system_proxy {
  208. let _ = tray
  209. .get_item("system_proxy")
  210. .set_title(t!("System Proxy ✔", "系统代理 ✔"));
  211. } else {
  212. let _ = tray
  213. .get_item("system_proxy")
  214. .set_title(t!("System Proxy", "系统代理"));
  215. }
  216. if *tun_mode {
  217. let _ = tray
  218. .get_item("tun_mode")
  219. .set_title(t!("TUN Mode ✔", "Tun 模式 ✔"));
  220. } else {
  221. let _ = tray
  222. .get_item("tun_mode")
  223. .set_title(t!("TUN Mode", "Tun 模式"));
  224. }
  225. }
  226. let switch_map = {
  227. let mut map = std::collections::HashMap::new();
  228. map.insert(true, "on");
  229. map.insert(false, "off");
  230. map
  231. };
  232. let _ = tray.set_tooltip(&format!(
  233. "Clash Verge {version}\n{}: {}\n{}: {}",
  234. t!("System Proxy", "系统代理"),
  235. switch_map[system_proxy],
  236. t!("TUN Mode", "Tun 模式"),
  237. switch_map[tun_mode]
  238. ));
  239. Ok(())
  240. }
  241. pub fn on_left_click(app_handle: &AppHandle) {
  242. let tray_event = { Config::verge().latest().tray_event.clone() };
  243. let tray_event = tray_event.unwrap_or("main_window".into());
  244. match tray_event.as_str() {
  245. "system_proxy" => feat::toggle_system_proxy(),
  246. "tun_mode" => feat::toggle_tun_mode(),
  247. "main_window" => resolve::create_window(app_handle),
  248. _ => {}
  249. }
  250. }
  251. pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
  252. match event {
  253. SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
  254. SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
  255. mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
  256. let mode = &mode[0..mode.len() - 5];
  257. feat::change_clash_mode(mode.into());
  258. }
  259. "open_window" => resolve::create_window(app_handle),
  260. "system_proxy" => feat::toggle_system_proxy(),
  261. "tun_mode" => feat::toggle_tun_mode(),
  262. "copy_env" => feat::copy_clash_env(app_handle),
  263. "open_app_dir" => crate::log_err!(cmds::open_app_dir()),
  264. "open_core_dir" => crate::log_err!(cmds::open_core_dir()),
  265. "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
  266. "restart_clash" => feat::restart_clash_core(),
  267. "restart_app" => api::process::restart(&app_handle.env()),
  268. "quit" => cmds::exit_app(app_handle.clone()),
  269. _ => {}
  270. },
  271. _ => {}
  272. }
  273. }
  274. }