main.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. #![cfg_attr(
  5. all(not(debug_assertions), target_os = "windows"),
  6. windows_subsystem = "windows"
  7. )]
  8. mod cmd;
  9. mod menu;
  10. #[cfg(target_os = "linux")]
  11. use std::path::PathBuf;
  12. use std::sync::atomic::{AtomicBool, Ordering};
  13. use serde::{Deserialize, Serialize};
  14. use tauri::{
  15. api::dialog::ask, http::ResponseBuilder, CustomMenuItem, GlobalShortcutManager, Manager,
  16. RunEvent, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowBuilder, WindowUrl,
  17. };
  18. #[derive(Clone, Serialize)]
  19. struct Reply {
  20. data: String,
  21. }
  22. #[derive(Serialize, Deserialize)]
  23. struct HttpPost {
  24. foo: String,
  25. bar: String,
  26. }
  27. #[derive(Serialize)]
  28. struct HttpReply {
  29. msg: String,
  30. request: HttpPost,
  31. }
  32. #[tauri::command]
  33. async fn menu_toggle(window: tauri::Window) {
  34. window.menu_handle().toggle().unwrap();
  35. }
  36. fn main() {
  37. let tray_menu1 = SystemTrayMenu::new()
  38. .add_item(CustomMenuItem::new("toggle", "Toggle"))
  39. .add_item(CustomMenuItem::new("new", "New window"))
  40. .add_item(CustomMenuItem::new("icon_1", "Tray Icon 1"))
  41. .add_item(CustomMenuItem::new("icon_2", "Tray Icon 2"))
  42. .add_item(CustomMenuItem::new("switch_menu", "Switch Menu"))
  43. .add_item(CustomMenuItem::new("exit_app", "Quit"));
  44. let tray_menu2 = SystemTrayMenu::new()
  45. .add_item(CustomMenuItem::new("toggle", "Toggle"))
  46. .add_item(CustomMenuItem::new("new", "New window"))
  47. .add_item(CustomMenuItem::new("switch_menu", "Switch Menu"))
  48. .add_item(CustomMenuItem::new("exit_app", "Quit"));
  49. let is_menu1 = AtomicBool::new(true);
  50. #[allow(unused_mut)]
  51. let mut app = tauri::Builder::default()
  52. .setup(|app| {
  53. #[cfg(debug_assertions)]
  54. app.get_window("main").unwrap().open_devtools();
  55. Ok(())
  56. })
  57. .on_page_load(|window, _| {
  58. let window_ = window.clone();
  59. window.listen("js-event", move |event| {
  60. println!("got js-event with message '{:?}'", event.payload());
  61. let reply = Reply {
  62. data: "something else".to_string(),
  63. };
  64. window_
  65. .emit("rust-event", Some(reply))
  66. .expect("failed to emit");
  67. });
  68. })
  69. .register_uri_scheme_protocol("customprotocol", move |_app_handle, request| {
  70. if request.method() == "POST" {
  71. let request: HttpPost = serde_json::from_slice(request.body()).unwrap();
  72. return ResponseBuilder::new()
  73. .mimetype("application/json")
  74. .header("Access-Control-Allow-Origin", "*")
  75. .status(200)
  76. .body(serde_json::to_vec(&HttpReply {
  77. request,
  78. msg: "Hello from rust!".to_string(),
  79. })?);
  80. }
  81. ResponseBuilder::new()
  82. .mimetype("text/html")
  83. .status(404)
  84. .body(Vec::new())
  85. })
  86. .menu(menu::get_menu())
  87. .on_menu_event(|event| {
  88. println!("{:?}", event.menu_item_id());
  89. })
  90. .system_tray(SystemTray::new().with_menu(tray_menu1.clone()))
  91. .on_system_tray_event(move |app, event| match event {
  92. SystemTrayEvent::LeftClick {
  93. position: _,
  94. size: _,
  95. ..
  96. } => {
  97. let window = app.get_window("main").unwrap();
  98. window.show().unwrap();
  99. window.set_focus().unwrap();
  100. }
  101. SystemTrayEvent::MenuItemClick { id, .. } => {
  102. let item_handle = app.tray_handle().get_item(&id);
  103. match id.as_str() {
  104. "exit_app" => {
  105. // exit the app
  106. app.exit(0);
  107. }
  108. "toggle" => {
  109. let window = app.get_window("main").unwrap();
  110. let new_title = if window.is_visible().unwrap() {
  111. window.hide().unwrap();
  112. "Show"
  113. } else {
  114. window.show().unwrap();
  115. "Hide"
  116. };
  117. item_handle.set_title(new_title).unwrap();
  118. }
  119. "new" => {
  120. app
  121. .create_window(
  122. "new",
  123. WindowUrl::App("index.html".into()),
  124. |window_builder, webview_attributes| {
  125. (window_builder.title("Tauri"), webview_attributes)
  126. },
  127. )
  128. .unwrap();
  129. }
  130. #[cfg(target_os = "macos")]
  131. "icon_1" => {
  132. app.tray_handle().set_icon_as_template(true).unwrap();
  133. app
  134. .tray_handle()
  135. .set_icon(tauri::Icon::Raw(
  136. include_bytes!("../../../.icons/tray_icon_with_transparency.png").to_vec(),
  137. ))
  138. .unwrap();
  139. }
  140. #[cfg(target_os = "macos")]
  141. "icon_2" => {
  142. app.tray_handle().set_icon_as_template(true).unwrap();
  143. app
  144. .tray_handle()
  145. .set_icon(tauri::Icon::Raw(
  146. include_bytes!("../../../.icons/tray_icon.png").to_vec(),
  147. ))
  148. .unwrap();
  149. }
  150. #[cfg(target_os = "linux")]
  151. "icon_1" => app
  152. .tray_handle()
  153. .set_icon(tauri::Icon::File(PathBuf::from(
  154. "../../../.icons/tray_icon_with_transparency.png",
  155. )))
  156. .unwrap(),
  157. #[cfg(target_os = "linux")]
  158. "icon_2" => app
  159. .tray_handle()
  160. .set_icon(tauri::Icon::File(PathBuf::from(
  161. "../../../.icons/tray_icon.png",
  162. )))
  163. .unwrap(),
  164. #[cfg(target_os = "windows")]
  165. "icon_1" => app
  166. .tray_handle()
  167. .set_icon(tauri::Icon::Raw(
  168. include_bytes!("../../../.icons/tray_icon_with_transparency.ico").to_vec(),
  169. ))
  170. .unwrap(),
  171. #[cfg(target_os = "windows")]
  172. "icon_2" => app
  173. .tray_handle()
  174. .set_icon(tauri::Icon::Raw(
  175. include_bytes!("../../../.icons/icon.ico").to_vec(),
  176. ))
  177. .unwrap(),
  178. "switch_menu" => {
  179. let flag = is_menu1.load(Ordering::Relaxed);
  180. app
  181. .tray_handle()
  182. .set_menu(if flag {
  183. tray_menu2.clone()
  184. } else {
  185. tray_menu1.clone()
  186. })
  187. .unwrap();
  188. is_menu1.store(!flag, Ordering::Relaxed);
  189. }
  190. _ => {}
  191. }
  192. }
  193. _ => {}
  194. })
  195. .invoke_handler(tauri::generate_handler![
  196. cmd::log_operation,
  197. cmd::perform_request,
  198. menu_toggle,
  199. ])
  200. .build(tauri::generate_context!())
  201. .expect("error while building tauri application");
  202. #[cfg(target_os = "macos")]
  203. app.set_activation_policy(tauri::ActivationPolicy::Regular);
  204. app.run(|app_handle, e| match e {
  205. // Application is ready (triggered only once)
  206. RunEvent::Ready => {
  207. let app_handle = app_handle.clone();
  208. app_handle
  209. .global_shortcut_manager()
  210. .register("CmdOrCtrl+1", move || {
  211. let app_handle = app_handle.clone();
  212. let window = app_handle.get_window("main").unwrap();
  213. window.set_title("New title!").unwrap();
  214. })
  215. .unwrap();
  216. }
  217. // Triggered when a window is trying to close
  218. RunEvent::CloseRequested { label, api, .. } => {
  219. let app_handle = app_handle.clone();
  220. let window = app_handle.get_window(&label).unwrap();
  221. // use the exposed close api, and prevent the event loop to close
  222. api.prevent_close();
  223. // ask the user if he wants to quit
  224. ask(
  225. Some(&window),
  226. "Tauri API",
  227. "Are you sure that you want to close this window?",
  228. move |answer| {
  229. if answer {
  230. // .close() cannot be called on the main thread
  231. std::thread::spawn(move || {
  232. app_handle.get_window(&label).unwrap().close().unwrap();
  233. });
  234. }
  235. },
  236. );
  237. }
  238. // Keep the event loop running even if all windows are closed
  239. // This allow us to catch system tray events when there is no window
  240. RunEvent::ExitRequested { api, .. } => {
  241. api.prevent_exit();
  242. }
  243. _ => {}
  244. })
  245. }