main.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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, window::WindowBuilder, CustomMenuItem,
  16. GlobalShortcutManager, Manager, RunEvent, SystemTray, SystemTrayEvent, SystemTrayMenu, 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. WindowBuilder::new(app, "new", WindowUrl::App("index.html".into()))
  121. .title("Tauri")
  122. .build()
  123. .unwrap();
  124. }
  125. #[cfg(target_os = "macos")]
  126. "icon_1" => {
  127. app.tray_handle().set_icon_as_template(true).unwrap();
  128. app
  129. .tray_handle()
  130. .set_icon(tauri::TrayIcon::Raw(
  131. include_bytes!("../../../.icons/tray_icon_with_transparency.png").to_vec(),
  132. ))
  133. .unwrap();
  134. }
  135. #[cfg(target_os = "macos")]
  136. "icon_2" => {
  137. app.tray_handle().set_icon_as_template(true).unwrap();
  138. app
  139. .tray_handle()
  140. .set_icon(tauri::TrayIcon::Raw(
  141. include_bytes!("../../../.icons/tray_icon_with.png").to_vec(),
  142. ))
  143. .unwrap();
  144. }
  145. #[cfg(target_os = "linux")]
  146. "icon_1" => app
  147. .tray_handle()
  148. .set_icon(tauri::TrayIcon::File(PathBuf::from(
  149. "../../../.icons/tray_icon_with_transparency.png",
  150. )))
  151. .unwrap(),
  152. #[cfg(target_os = "linux")]
  153. "icon_2" => app
  154. .tray_handle()
  155. .set_icon(tauri::TrayIcon::File(PathBuf::from(
  156. "../../../.icons/tray_icon.png",
  157. )))
  158. .unwrap(),
  159. #[cfg(target_os = "windows")]
  160. "icon_1" => app
  161. .tray_handle()
  162. .set_icon(tauri::TrayIcon::Raw(
  163. include_bytes!("../../../.icons/tray_icon_with_transparency.ico").to_vec(),
  164. ))
  165. .unwrap(),
  166. #[cfg(target_os = "windows")]
  167. "icon_2" => app
  168. .tray_handle()
  169. .set_icon(tauri::TrayIcon::Raw(
  170. include_bytes!("../../../.icons/icon.ico").to_vec(),
  171. ))
  172. .unwrap(),
  173. "switch_menu" => {
  174. let flag = is_menu1.load(Ordering::Relaxed);
  175. app
  176. .tray_handle()
  177. .set_menu(if flag {
  178. tray_menu2.clone()
  179. } else {
  180. tray_menu1.clone()
  181. })
  182. .unwrap();
  183. is_menu1.store(!flag, Ordering::Relaxed);
  184. }
  185. _ => {}
  186. }
  187. }
  188. _ => {}
  189. })
  190. .invoke_handler(tauri::generate_handler![
  191. cmd::log_operation,
  192. cmd::perform_request,
  193. menu_toggle,
  194. ])
  195. .build(tauri::generate_context!())
  196. .expect("error while building tauri application");
  197. #[cfg(target_os = "macos")]
  198. app.set_activation_policy(tauri::ActivationPolicy::Regular);
  199. app.run(|app_handle, e| match e {
  200. // Application is ready (triggered only once)
  201. RunEvent::Ready => {
  202. let app_handle = app_handle.clone();
  203. app_handle
  204. .global_shortcut_manager()
  205. .register("CmdOrCtrl+1", move || {
  206. let app_handle = app_handle.clone();
  207. let window = app_handle.get_window("main").unwrap();
  208. window.set_title("New title!").unwrap();
  209. })
  210. .unwrap();
  211. }
  212. // Triggered when a window is trying to close
  213. RunEvent::CloseRequested { label, api, .. } => {
  214. let app_handle = app_handle.clone();
  215. let window = app_handle.get_window(&label).unwrap();
  216. // use the exposed close api, and prevent the event loop to close
  217. api.prevent_close();
  218. // ask the user if he wants to quit
  219. ask(
  220. Some(&window),
  221. "Tauri API",
  222. "Are you sure that you want to close this window?",
  223. move |answer| {
  224. if answer {
  225. // .close() cannot be called on the main thread
  226. std::thread::spawn(move || {
  227. app_handle.get_window(&label).unwrap().close().unwrap();
  228. });
  229. }
  230. },
  231. );
  232. }
  233. // Keep the event loop running even if all windows are closed
  234. // This allow us to catch system tray events when there is no window
  235. RunEvent::ExitRequested { api, .. } => {
  236. api.prevent_exit();
  237. }
  238. _ => {}
  239. })
  240. }