app.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use crate::{
  5. api::assets::Assets,
  6. api::config::WindowUrl,
  7. hooks::{InvokeHandler, OnPageLoad, PageLoadPayload, SetupHook},
  8. manager::{Args, WindowManager},
  9. plugin::{Plugin, PluginStore},
  10. runtime::{
  11. tag::Tag,
  12. webview::{CustomProtocol, WebviewAttributes, WindowBuilder},
  13. window::{PendingWindow, WindowEvent},
  14. Dispatch, MenuId, Params, Runtime,
  15. },
  16. sealed::{ManagerBase, RuntimeOrDispatch},
  17. Context, Invoke, Manager, StateManager, Window,
  18. };
  19. use std::{collections::HashMap, sync::Arc};
  20. #[cfg(feature = "menu")]
  21. use crate::runtime::menu::Menu;
  22. #[cfg(feature = "system-tray")]
  23. use crate::runtime::{menu::SystemTrayMenuItem, Icon};
  24. #[cfg(feature = "updater")]
  25. use crate::updater;
  26. #[cfg(feature = "menu")]
  27. pub(crate) type GlobalMenuEventListener<P> = Box<dyn Fn(WindowMenuEvent<P>) + Send + Sync>;
  28. pub(crate) type GlobalWindowEventListener<P> = Box<dyn Fn(GlobalWindowEvent<P>) + Send + Sync>;
  29. #[cfg(feature = "system-tray")]
  30. type SystemTrayEventListener<P> =
  31. Box<dyn Fn(&AppHandle<P>, SystemTrayEvent<<P as Params>::SystemTrayMenuId>) + Send + Sync>;
  32. /// System tray event.
  33. #[cfg(feature = "system-tray")]
  34. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  35. pub struct SystemTrayEvent<I: MenuId> {
  36. menu_item_id: I,
  37. }
  38. #[cfg(feature = "system-tray")]
  39. impl<I: MenuId> SystemTrayEvent<I> {
  40. /// The menu item id.
  41. pub fn menu_item_id(&self) -> &I {
  42. &self.menu_item_id
  43. }
  44. }
  45. crate::manager::default_args! {
  46. /// A menu event that was triggered on a window.
  47. #[cfg(feature = "menu")]
  48. #[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
  49. pub struct WindowMenuEvent<P: Params> {
  50. pub(crate) menu_item_id: P::MenuId,
  51. pub(crate) window: Window<P>,
  52. }
  53. }
  54. #[cfg(feature = "menu")]
  55. impl<P: Params> WindowMenuEvent<P> {
  56. /// The menu item id.
  57. pub fn menu_item_id(&self) -> &P::MenuId {
  58. &self.menu_item_id
  59. }
  60. /// The window that the menu belongs to.
  61. pub fn window(&self) -> &Window<P> {
  62. &self.window
  63. }
  64. }
  65. crate::manager::default_args! {
  66. /// A window event that was triggered on the specified window.
  67. pub struct GlobalWindowEvent<P: Params> {
  68. pub(crate) event: WindowEvent,
  69. pub(crate) window: Window<P>,
  70. }
  71. }
  72. impl<P: Params> GlobalWindowEvent<P> {
  73. /// The eventpayload.
  74. pub fn event(&self) -> &WindowEvent {
  75. &self.event
  76. }
  77. /// The window that the menu belongs to.
  78. pub fn window(&self) -> &Window<P> {
  79. &self.window
  80. }
  81. }
  82. crate::manager::default_args! {
  83. /// A handle to the currently running application.
  84. ///
  85. /// This type implements [`Manager`] which allows for manipulation of global application items.
  86. pub struct AppHandle<P: Params> {
  87. runtime_handle: <P::Runtime as Runtime>::Handle,
  88. manager: WindowManager<P>,
  89. }
  90. }
  91. impl<P: Params> Clone for AppHandle<P> {
  92. fn clone(&self) -> Self {
  93. Self {
  94. runtime_handle: self.runtime_handle.clone(),
  95. manager: self.manager.clone(),
  96. }
  97. }
  98. }
  99. impl<P: Params> Manager<P> for AppHandle<P> {}
  100. impl<P: Params> ManagerBase<P> for AppHandle<P> {
  101. fn manager(&self) -> &WindowManager<P> {
  102. &self.manager
  103. }
  104. fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
  105. RuntimeOrDispatch::RuntimeHandle(self.runtime_handle.clone())
  106. }
  107. }
  108. crate::manager::default_args! {
  109. /// The instance of the currently running application.
  110. ///
  111. /// This type implements [`Manager`] which allows for manipulation of global application items.
  112. pub struct App<P: Params> {
  113. runtime: Option<P::Runtime>,
  114. manager: WindowManager<P>,
  115. #[cfg(shell_execute)]
  116. cleanup_on_drop: bool,
  117. }
  118. }
  119. impl<P: Params> Drop for App<P> {
  120. fn drop(&mut self) {
  121. #[cfg(shell_execute)]
  122. {
  123. if self.cleanup_on_drop {
  124. crate::api::process::kill_children();
  125. }
  126. }
  127. }
  128. }
  129. impl<P: Params> Manager<P> for App<P> {}
  130. impl<P: Params> ManagerBase<P> for App<P> {
  131. fn manager(&self) -> &WindowManager<P> {
  132. &self.manager
  133. }
  134. fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
  135. RuntimeOrDispatch::Runtime(self.runtime.as_ref().unwrap())
  136. }
  137. }
  138. macro_rules! shared_app_impl {
  139. ($app: ty) => {
  140. impl<P: Params> $app {
  141. /// Creates a new webview window.
  142. pub fn create_window<F>(&self, label: P::Label, url: WindowUrl, setup: F) -> crate::Result<()>
  143. where
  144. F: FnOnce(
  145. <<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
  146. WebviewAttributes,
  147. ) -> (
  148. <<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
  149. WebviewAttributes,
  150. ),
  151. {
  152. let (window_builder, webview_attributes) = setup(
  153. <<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
  154. WebviewAttributes::new(url),
  155. );
  156. self.create_new_window(PendingWindow::new(
  157. window_builder,
  158. webview_attributes,
  159. label,
  160. ))?;
  161. Ok(())
  162. }
  163. }
  164. };
  165. }
  166. shared_app_impl!(App<P>);
  167. shared_app_impl!(AppHandle<P>);
  168. impl<P: Params> App<P> {
  169. /// Gets a handle to the application instance.
  170. pub fn handle(&self) -> AppHandle<P> {
  171. AppHandle {
  172. runtime_handle: self.runtime.as_ref().unwrap().handle(),
  173. manager: self.manager.clone(),
  174. }
  175. }
  176. /// Runs a iteration of the runtime event loop and immediately return.
  177. ///
  178. /// # Example
  179. /// ```rust,ignore
  180. /// fn main() {
  181. /// let mut app = tauri::Builder::default()
  182. /// .build(tauri::generate_context!())
  183. /// .expect("error while building tauri application");
  184. /// loop {
  185. /// let iteration = app.run_iteration();
  186. /// if iteration.webview_count == 0 {
  187. /// break;
  188. /// }
  189. /// }
  190. /// }
  191. #[cfg(any(target_os = "windows", target_os = "macos"))]
  192. pub fn run_iteration(&mut self) -> crate::runtime::RunIteration {
  193. self.runtime.as_mut().unwrap().run_iteration()
  194. }
  195. }
  196. #[cfg(feature = "updater")]
  197. impl<P: Params> App<P> {
  198. /// Runs the updater hook with built-in dialog.
  199. fn run_updater_dialog(&self, window: Window<P>) {
  200. let updater_config = self.manager.config().tauri.updater.clone();
  201. let package_info = self.manager.package_info().clone();
  202. crate::async_runtime::spawn(async move {
  203. updater::check_update_with_dialog(updater_config, package_info, window).await
  204. });
  205. }
  206. /// Listen updater events when dialog are disabled.
  207. fn listen_updater_events(&self, window: Window<P>) {
  208. let updater_config = self.manager.config().tauri.updater.clone();
  209. updater::listener(updater_config, self.manager.package_info().clone(), &window);
  210. }
  211. fn run_updater(&self, main_window: Option<Window<P>>) {
  212. if let Some(main_window) = main_window {
  213. let event_window = main_window.clone();
  214. let updater_config = self.manager.config().tauri.updater.clone();
  215. // check if updater is active or not
  216. if updater_config.dialog && updater_config.active {
  217. // if updater dialog is enabled spawn a new task
  218. self.run_updater_dialog(main_window.clone());
  219. let config = self.manager.config().tauri.updater.clone();
  220. let package_info = self.manager.package_info().clone();
  221. // When dialog is enabled, if user want to recheck
  222. // if an update is available after first start
  223. // invoke the Event `tauri://update` from JS or rust side.
  224. main_window.listen(
  225. updater::EVENT_CHECK_UPDATE
  226. .parse::<P::Event>()
  227. .unwrap_or_else(|_| panic!("bad label")),
  228. move |_msg| {
  229. let window = event_window.clone();
  230. let package_info = package_info.clone();
  231. let config = config.clone();
  232. // re-spawn task inside tokyo to launch the download
  233. // we don't need to emit anything as everything is handled
  234. // by the process (user is asked to restart at the end)
  235. // and it's handled by the updater
  236. crate::async_runtime::spawn(async move {
  237. updater::check_update_with_dialog(config, package_info, window).await
  238. });
  239. },
  240. );
  241. } else if updater_config.active {
  242. // we only listen for `tauri://update`
  243. // once we receive the call, we check if an update is available or not
  244. // if there is a new update we emit `tauri://update-available` with details
  245. // this is the user responsabilities to display dialog and ask if user want to install
  246. // to install the update you need to invoke the Event `tauri://update-install`
  247. self.listen_updater_events(main_window);
  248. }
  249. }
  250. }
  251. }
  252. /// Builds a Tauri application.
  253. #[allow(clippy::type_complexity)]
  254. pub struct Builder<E, L, MID, TID, A, R>
  255. where
  256. E: Tag,
  257. L: Tag,
  258. MID: MenuId,
  259. TID: MenuId,
  260. A: Assets,
  261. R: Runtime,
  262. {
  263. /// The JS message handler.
  264. invoke_handler: Box<InvokeHandler<Args<E, L, MID, TID, A, R>>>,
  265. /// The setup hook.
  266. setup: SetupHook<Args<E, L, MID, TID, A, R>>,
  267. /// Page load hook.
  268. on_page_load: Box<OnPageLoad<Args<E, L, MID, TID, A, R>>>,
  269. /// windows to create when starting up.
  270. pending_windows: Vec<PendingWindow<Args<E, L, MID, TID, A, R>>>,
  271. /// All passed plugins
  272. plugins: PluginStore<Args<E, L, MID, TID, A, R>>,
  273. /// The webview protocols available to all windows.
  274. uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
  275. /// App state.
  276. state: StateManager,
  277. /// The menu set to all windows.
  278. #[cfg(feature = "menu")]
  279. menu: Vec<Menu<MID>>,
  280. /// Menu event handlers that listens to all windows.
  281. #[cfg(feature = "menu")]
  282. menu_event_listeners: Vec<GlobalMenuEventListener<Args<E, L, MID, TID, A, R>>>,
  283. /// Window event handlers that listens to all windows.
  284. window_event_listeners: Vec<GlobalWindowEventListener<Args<E, L, MID, TID, A, R>>>,
  285. /// The app system tray menu items.
  286. #[cfg(feature = "system-tray")]
  287. system_tray: Vec<SystemTrayMenuItem<TID>>,
  288. /// System tray event handlers.
  289. #[cfg(feature = "system-tray")]
  290. system_tray_event_listeners: Vec<SystemTrayEventListener<Args<E, L, MID, TID, A, R>>>,
  291. #[cfg(shell_execute)]
  292. cleanup_on_drop: bool,
  293. }
  294. impl<E, L, MID, TID, A, R> Builder<E, L, MID, TID, A, R>
  295. where
  296. E: Tag,
  297. L: Tag,
  298. MID: MenuId,
  299. TID: MenuId,
  300. A: Assets,
  301. R: Runtime,
  302. {
  303. /// Creates a new App builder.
  304. pub fn new() -> Self {
  305. Self {
  306. setup: Box::new(|_| Ok(())),
  307. invoke_handler: Box::new(|_| ()),
  308. on_page_load: Box::new(|_, _| ()),
  309. pending_windows: Default::default(),
  310. plugins: PluginStore::default(),
  311. uri_scheme_protocols: Default::default(),
  312. state: StateManager::new(),
  313. #[cfg(feature = "menu")]
  314. menu: Vec::new(),
  315. #[cfg(feature = "menu")]
  316. menu_event_listeners: Vec::new(),
  317. window_event_listeners: Vec::new(),
  318. #[cfg(feature = "system-tray")]
  319. system_tray: Vec::new(),
  320. #[cfg(feature = "system-tray")]
  321. system_tray_event_listeners: Vec::new(),
  322. #[cfg(shell_execute)]
  323. cleanup_on_drop: true,
  324. }
  325. }
  326. /// Defines the JS message handler callback.
  327. pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
  328. where
  329. F: Fn(Invoke<Args<E, L, MID, TID, A, R>>) + Send + Sync + 'static,
  330. {
  331. self.invoke_handler = Box::new(invoke_handler);
  332. self
  333. }
  334. /// Defines the setup hook.
  335. pub fn setup<F>(mut self, setup: F) -> Self
  336. where
  337. F: Fn(&mut App<Args<E, L, MID, TID, A, R>>) -> Result<(), Box<dyn std::error::Error + Send>>
  338. + Send
  339. + 'static,
  340. {
  341. self.setup = Box::new(setup);
  342. self
  343. }
  344. /// Defines the page load hook.
  345. pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
  346. where
  347. F: Fn(Window<Args<E, L, MID, TID, A, R>>, PageLoadPayload) + Send + Sync + 'static,
  348. {
  349. self.on_page_load = Box::new(on_page_load);
  350. self
  351. }
  352. /// Adds a plugin to the runtime.
  353. pub fn plugin<P: Plugin<Args<E, L, MID, TID, A, R>> + 'static>(mut self, plugin: P) -> Self {
  354. self.plugins.register(plugin);
  355. self
  356. }
  357. /// Add `state` to the state managed by the application.
  358. ///
  359. /// This method can be called any number of times as long as each call
  360. /// refers to a different `T`.
  361. ///
  362. /// Managed state can be retrieved by any request handler via the
  363. /// [`State`](crate::State) request guard. In particular, if a value of type `T`
  364. /// is managed by Tauri, adding `State<T>` to the list of arguments in a
  365. /// request handler instructs Tauri to retrieve the managed value.
  366. ///
  367. /// # Panics
  368. ///
  369. /// Panics if state of type `T` is already being managed.
  370. ///
  371. /// # Mutability
  372. ///
  373. /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
  374. ///
  375. /// ```rust,ignore
  376. /// use std::{collections::HashMap, sync::Mutex};
  377. /// use tauri::State;
  378. /// // here we use Mutex to achieve interior mutability
  379. /// struct Storage(Mutex<HashMap<u64, String>>);
  380. /// struct Connection;
  381. /// struct DbConnection(Mutex<Option<Connection>>);
  382. ///
  383. /// #[tauri::command]
  384. /// fn connect(connection: State<DbConnection>) {
  385. /// // initialize the connection, mutating the state with interior mutability
  386. /// *connection.0.lock().unwrap() = Some(Connection {});
  387. /// }
  388. ///
  389. /// #[tauri::command]
  390. /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
  391. /// // mutate the storage behind the Mutex
  392. /// storage.0.lock().unwrap().insert(key, value);
  393. /// }
  394. ///
  395. /// fn main() {
  396. /// Builder::default()
  397. /// .manage(Storage(Default::default()))
  398. /// .manage(DbConnection(Default::default()))
  399. /// .invoke_handler(tauri::generate_handler![connect, storage_insert])
  400. /// .run(tauri::generate_context!())
  401. /// .expect("error while running tauri application");
  402. /// }
  403. /// ```
  404. ///
  405. /// # Example
  406. ///
  407. /// ```rust,ignore
  408. /// use tauri::State;
  409. ///
  410. /// struct MyInt(isize);
  411. /// struct MyString(String);
  412. ///
  413. /// #[tauri::command]
  414. /// fn int_command(state: State<MyInt>) -> String {
  415. /// format!("The stateful int is: {}", state.0)
  416. /// }
  417. ///
  418. /// #[tauri::command]
  419. /// fn string_command<'r>(state: State<'r, MyString>) {
  420. /// println!("state: {}", state.inner().0);
  421. /// }
  422. ///
  423. /// fn main() {
  424. /// tauri::Builder::default()
  425. /// .manage(MyInt(10))
  426. /// .manage(MyString("Hello, managed state!".to_string()))
  427. /// .invoke_handler(tauri::generate_handler![int_command, string_command])
  428. /// .run(tauri::generate_context!())
  429. /// .expect("error while running tauri application");
  430. /// }
  431. /// ```
  432. pub fn manage<T>(self, state: T) -> Self
  433. where
  434. T: Send + Sync + 'static,
  435. {
  436. let type_name = std::any::type_name::<T>();
  437. if !self.state.set(state) {
  438. panic!("state for type '{}' is already being managed", type_name);
  439. }
  440. self
  441. }
  442. /// Creates a new webview window.
  443. pub fn create_window<F>(mut self, label: L, url: WindowUrl, setup: F) -> Self
  444. where
  445. F: FnOnce(
  446. <R::Dispatcher as Dispatch>::WindowBuilder,
  447. WebviewAttributes,
  448. ) -> (
  449. <R::Dispatcher as Dispatch>::WindowBuilder,
  450. WebviewAttributes,
  451. ),
  452. {
  453. let (window_builder, webview_attributes) = setup(
  454. <R::Dispatcher as Dispatch>::WindowBuilder::new(),
  455. WebviewAttributes::new(url),
  456. );
  457. self.pending_windows.push(PendingWindow::new(
  458. window_builder,
  459. webview_attributes,
  460. label,
  461. ));
  462. self
  463. }
  464. /// Adds the icon configured on `tauri.conf.json` to the system tray with the specified menu items.
  465. #[cfg(feature = "system-tray")]
  466. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  467. pub fn system_tray(mut self, items: Vec<SystemTrayMenuItem<TID>>) -> Self {
  468. self.system_tray = items;
  469. self
  470. }
  471. /// Sets the menu to use on all windows.
  472. #[cfg(feature = "menu")]
  473. #[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
  474. pub fn menu(mut self, menu: Vec<Menu<MID>>) -> Self {
  475. self.menu = menu;
  476. self
  477. }
  478. /// Registers a menu event handler for all windows.
  479. #[cfg(feature = "menu")]
  480. #[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
  481. pub fn on_menu_event<
  482. F: Fn(WindowMenuEvent<Args<E, L, MID, TID, A, R>>) + Send + Sync + 'static,
  483. >(
  484. mut self,
  485. handler: F,
  486. ) -> Self {
  487. self.menu_event_listeners.push(Box::new(handler));
  488. self
  489. }
  490. /// Registers a window event handler for all windows.
  491. pub fn on_window_event<
  492. F: Fn(GlobalWindowEvent<Args<E, L, MID, TID, A, R>>) + Send + Sync + 'static,
  493. >(
  494. mut self,
  495. handler: F,
  496. ) -> Self {
  497. self.window_event_listeners.push(Box::new(handler));
  498. self
  499. }
  500. /// Registers a system tray event handler.
  501. #[cfg(feature = "system-tray")]
  502. #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
  503. pub fn on_system_tray_event<
  504. F: Fn(&AppHandle<Args<E, L, MID, TID, A, R>>, SystemTrayEvent<TID>) + Send + Sync + 'static,
  505. >(
  506. mut self,
  507. handler: F,
  508. ) -> Self {
  509. self.system_tray_event_listeners.push(Box::new(handler));
  510. self
  511. }
  512. /// Registers a URI scheme protocol available to all webviews.
  513. /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS,
  514. /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows
  515. /// and [webkit-web-context-register-uri-scheme](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebContext.html#webkit-web-context-register-uri-scheme) on Linux.
  516. ///
  517. /// # Arguments
  518. ///
  519. /// * `uri_scheme` The URI scheme to register, such as `example`.
  520. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
  521. pub fn register_global_uri_scheme_protocol<
  522. N: Into<String>,
  523. H: Fn(&str) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync + 'static,
  524. >(
  525. mut self,
  526. uri_scheme: N,
  527. protocol: H,
  528. ) -> Self {
  529. self.uri_scheme_protocols.insert(
  530. uri_scheme.into(),
  531. Arc::new(CustomProtocol {
  532. protocol: Box::new(protocol),
  533. }),
  534. );
  535. self
  536. }
  537. /// Skips Tauri cleanup on [`App`] drop. Useful if your application has multiple [`App`] instances.
  538. ///
  539. /// The cleanup calls [`crate::api::process::kill_children`] so you may want to call that function before exiting the application.
  540. #[cfg(shell_execute)]
  541. pub fn skip_cleanup_on_drop(mut self) -> Self {
  542. self.cleanup_on_drop = false;
  543. self
  544. }
  545. /// Builds the application.
  546. #[allow(clippy::type_complexity)]
  547. pub fn build(mut self, context: Context<A>) -> crate::Result<App<Args<E, L, MID, TID, A, R>>> {
  548. #[cfg(feature = "system-tray")]
  549. let system_tray_icon = {
  550. let icon = context.system_tray_icon.clone();
  551. // check the icon format if the system tray is supposed to be ran
  552. if !self.system_tray.is_empty() {
  553. use std::io::{Error, ErrorKind};
  554. #[cfg(target_os = "linux")]
  555. if let Some(Icon::Raw(_)) = icon {
  556. return Err(crate::Error::InvalidIcon(Box::new(Error::new(
  557. ErrorKind::InvalidInput,
  558. "system tray icons on linux must be a file path",
  559. ))));
  560. }
  561. #[cfg(not(target_os = "linux"))]
  562. if let Some(Icon::File(_)) = icon {
  563. return Err(crate::Error::InvalidIcon(Box::new(Error::new(
  564. ErrorKind::InvalidInput,
  565. "system tray icons on non-linux platforms must be the raw bytes",
  566. ))));
  567. }
  568. }
  569. icon
  570. };
  571. let manager = WindowManager::with_handlers(
  572. context,
  573. self.plugins,
  574. self.invoke_handler,
  575. self.on_page_load,
  576. self.uri_scheme_protocols,
  577. self.state,
  578. self.window_event_listeners,
  579. #[cfg(feature = "menu")]
  580. (self.menu, self.menu_event_listeners),
  581. );
  582. // set up all the windows defined in the config
  583. for config in manager.config().tauri.windows.clone() {
  584. let url = config.url.clone();
  585. let label = config
  586. .label
  587. .parse()
  588. .unwrap_or_else(|_| panic!("bad label found in config: {}", config.label));
  589. self.pending_windows.push(PendingWindow::with_config(
  590. config,
  591. WebviewAttributes::new(url),
  592. label,
  593. ));
  594. }
  595. let mut app = App {
  596. runtime: Some(R::new()?),
  597. manager,
  598. #[cfg(shell_execute)]
  599. cleanup_on_drop: self.cleanup_on_drop,
  600. };
  601. app.manager.initialize_plugins(&app)?;
  602. let pending_labels = self
  603. .pending_windows
  604. .iter()
  605. .map(|p| p.label.clone())
  606. .collect::<Vec<_>>();
  607. #[cfg(feature = "updater")]
  608. let mut main_window = None;
  609. for pending in self.pending_windows {
  610. let pending = app.manager.prepare_window(pending, &pending_labels)?;
  611. let detached = app.runtime.as_ref().unwrap().create_window(pending)?;
  612. let _window = app.manager.attach_window(detached);
  613. #[cfg(feature = "updater")]
  614. if main_window.is_none() {
  615. main_window = Some(_window);
  616. }
  617. }
  618. #[cfg(feature = "updater")]
  619. app.run_updater(main_window);
  620. (self.setup)(&mut app).map_err(|e| crate::Error::Setup(e))?;
  621. #[cfg(feature = "system-tray")]
  622. if !self.system_tray.is_empty() {
  623. let ids = get_menu_ids(&self.system_tray);
  624. app
  625. .runtime
  626. .as_ref()
  627. .unwrap()
  628. .system_tray(
  629. system_tray_icon.expect("tray icon not found; please configure it on tauri.conf.json"),
  630. self.system_tray,
  631. )
  632. .expect("failed to run tray");
  633. for listener in self.system_tray_event_listeners {
  634. let app_handle = app.handle();
  635. let ids = ids.clone();
  636. let listener = Arc::new(std::sync::Mutex::new(listener));
  637. app
  638. .runtime
  639. .as_mut()
  640. .unwrap()
  641. .on_system_tray_event(move |event| {
  642. let app_handle = app_handle.clone();
  643. let menu_item_id = ids.get(&event.menu_item_id).unwrap().clone();
  644. let listener = listener.clone();
  645. crate::async_runtime::spawn(async move {
  646. listener.lock().unwrap()(&app_handle, SystemTrayEvent { menu_item_id });
  647. });
  648. });
  649. }
  650. }
  651. Ok(app)
  652. }
  653. /// Runs the configured Tauri application.
  654. pub fn run(self, context: Context<A>) -> crate::Result<()> {
  655. let mut app = self.build(context)?;
  656. app.runtime.take().unwrap().run();
  657. Ok(())
  658. }
  659. }
  660. #[cfg(feature = "system-tray")]
  661. fn get_menu_ids<I: MenuId>(items: &[SystemTrayMenuItem<I>]) -> HashMap<u32, I> {
  662. let mut map = HashMap::new();
  663. for item in items {
  664. if let SystemTrayMenuItem::Custom(i) = item {
  665. map.insert(i.id_value(), i.id.clone());
  666. }
  667. }
  668. map
  669. }
  670. /// Make `Wry` the default `Runtime` for `Builder`
  671. #[cfg(feature = "wry")]
  672. impl<A: Assets> Default for Builder<String, String, String, String, A, crate::Wry> {
  673. fn default() -> Self {
  674. Self::new()
  675. }
  676. }
  677. #[cfg(not(feature = "wry"))]
  678. impl<A: Assets, R: Runtime> Default for Builder<String, String, String, String, A, R> {
  679. fn default() -> Self {
  680. Self::new()
  681. }
  682. }