window.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! A layer between raw [`Runtime`] webview windows and Tauri.
  5. use crate::{
  6. http::{Request as HttpRequest, Response as HttpResponse},
  7. menu::{Menu, MenuEntry, MenuHash, MenuId},
  8. webview::{FileDropHandler, WebviewAttributes, WebviewIpcHandler},
  9. Dispatch, Runtime, WindowBuilder,
  10. };
  11. use serde::Serialize;
  12. use tauri_utils::config::WindowConfig;
  13. use std::{
  14. collections::{HashMap, HashSet},
  15. hash::{Hash, Hasher},
  16. sync::{mpsc::Sender, Arc, Mutex},
  17. };
  18. type UriSchemeProtocol =
  19. dyn Fn(&HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> + Send + Sync + 'static;
  20. /// UI scaling utilities.
  21. pub mod dpi;
  22. /// An event from a window.
  23. #[derive(Debug, Clone)]
  24. #[non_exhaustive]
  25. pub enum WindowEvent {
  26. /// The size of the window has changed. Contains the client area's new dimensions.
  27. Resized(dpi::PhysicalSize<u32>),
  28. /// The position of the window has changed. Contains the window's new position.
  29. Moved(dpi::PhysicalPosition<i32>),
  30. /// The window has been requested to close.
  31. CloseRequested {
  32. /// The window label.
  33. label: String,
  34. /// A signal sender. If a `true` value is emitted, the window won't be closed.
  35. signal_tx: Sender<bool>,
  36. },
  37. /// The window has been destroyed.
  38. Destroyed,
  39. /// The window gained or lost focus.
  40. ///
  41. /// The parameter is true if the window has gained focus, and false if it has lost focus.
  42. Focused(bool),
  43. /// The window's scale factor has changed.
  44. ///
  45. /// The following user actions can cause DPI changes:
  46. ///
  47. /// - Changing the display's resolution.
  48. /// - Changing the display's scale factor (e.g. in Control Panel on Windows).
  49. /// - Moving the window to a display with a different scale factor.
  50. ScaleFactorChanged {
  51. /// The new scale factor.
  52. scale_factor: f64,
  53. /// The window inner size.
  54. new_inner_size: dpi::PhysicalSize<u32>,
  55. },
  56. }
  57. /// A menu event.
  58. #[derive(Debug, Serialize)]
  59. #[serde(rename_all = "camelCase")]
  60. pub struct MenuEvent {
  61. pub menu_item_id: u16,
  62. }
  63. fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &Menu) {
  64. for item in &menu.items {
  65. match item {
  66. MenuEntry::CustomItem(c) => {
  67. map.insert(c.id, c.id_str.clone());
  68. }
  69. MenuEntry::Submenu(s) => get_menu_ids(map, &s.inner),
  70. _ => {}
  71. }
  72. }
  73. }
  74. /// A webview window that has yet to be built.
  75. pub struct PendingWindow<R: Runtime> {
  76. /// The label that the window will be named.
  77. pub label: String,
  78. /// The [`WindowBuilder`] that the window will be created with.
  79. pub window_builder: <R::Dispatcher as Dispatch>::WindowBuilder,
  80. /// The [`WebviewAttributes`] that the webview will be created with.
  81. pub webview_attributes: WebviewAttributes,
  82. pub uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocol>>,
  83. /// How to handle IPC calls on the webview window.
  84. pub ipc_handler: Option<WebviewIpcHandler<R>>,
  85. /// How to handle a file dropping onto the webview window.
  86. pub file_drop_handler: Option<FileDropHandler<R>>,
  87. /// The resolved URL to load on the webview.
  88. pub url: String,
  89. /// Maps runtime id to a string menu id.
  90. pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
  91. /// A HashMap mapping JS event names with listener ids associated.
  92. pub js_event_listeners: Arc<Mutex<HashMap<String, HashSet<u64>>>>,
  93. }
  94. impl<R: Runtime> PendingWindow<R> {
  95. /// Create a new [`PendingWindow`] with a label and starting url.
  96. pub fn new(
  97. window_builder: <R::Dispatcher as Dispatch>::WindowBuilder,
  98. webview_attributes: WebviewAttributes,
  99. label: impl Into<String>,
  100. ) -> Self {
  101. let mut menu_ids = HashMap::new();
  102. if let Some(menu) = window_builder.get_menu() {
  103. get_menu_ids(&mut menu_ids, menu);
  104. }
  105. Self {
  106. window_builder,
  107. webview_attributes,
  108. uri_scheme_protocols: Default::default(),
  109. label: label.into(),
  110. ipc_handler: None,
  111. file_drop_handler: None,
  112. url: "tauri://localhost".to_string(),
  113. menu_ids: Arc::new(Mutex::new(menu_ids)),
  114. js_event_listeners: Default::default(),
  115. }
  116. }
  117. /// Create a new [`PendingWindow`] from a [`WindowConfig`] with a label and starting url.
  118. pub fn with_config(
  119. window_config: WindowConfig,
  120. webview_attributes: WebviewAttributes,
  121. label: impl Into<String>,
  122. ) -> Self {
  123. let window_builder = <<R::Dispatcher as Dispatch>::WindowBuilder>::with_config(window_config);
  124. let mut menu_ids = HashMap::new();
  125. if let Some(menu) = window_builder.get_menu() {
  126. get_menu_ids(&mut menu_ids, menu);
  127. }
  128. Self {
  129. window_builder,
  130. webview_attributes,
  131. uri_scheme_protocols: Default::default(),
  132. label: label.into(),
  133. ipc_handler: None,
  134. file_drop_handler: None,
  135. url: "tauri://localhost".to_string(),
  136. menu_ids: Arc::new(Mutex::new(menu_ids)),
  137. js_event_listeners: Default::default(),
  138. }
  139. }
  140. pub fn set_menu(mut self, menu: Menu) -> Self {
  141. let mut menu_ids = HashMap::new();
  142. get_menu_ids(&mut menu_ids, &menu);
  143. *self.menu_ids.lock().unwrap() = menu_ids;
  144. self.window_builder = self.window_builder.menu(menu);
  145. self
  146. }
  147. pub fn register_uri_scheme_protocol<
  148. N: Into<String>,
  149. H: Fn(&HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> + Send + Sync + 'static,
  150. >(
  151. &mut self,
  152. uri_scheme: N,
  153. protocol: H,
  154. ) {
  155. let uri_scheme = uri_scheme.into();
  156. self
  157. .uri_scheme_protocols
  158. .insert(uri_scheme, Box::new(move |data| (protocol)(data)));
  159. }
  160. }
  161. /// A webview window that is not yet managed by Tauri.
  162. #[derive(Debug)]
  163. pub struct DetachedWindow<R: Runtime> {
  164. /// Name of the window
  165. pub label: String,
  166. /// The [`Dispatch`](crate::Dispatch) associated with the window.
  167. pub dispatcher: R::Dispatcher,
  168. /// Maps runtime id to a string menu id.
  169. pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
  170. /// A HashMap mapping JS event names with listener ids associated.
  171. pub js_event_listeners: Arc<Mutex<HashMap<String, HashSet<u64>>>>,
  172. }
  173. impl<R: Runtime> Clone for DetachedWindow<R> {
  174. fn clone(&self) -> Self {
  175. Self {
  176. label: self.label.clone(),
  177. dispatcher: self.dispatcher.clone(),
  178. menu_ids: self.menu_ids.clone(),
  179. js_event_listeners: self.js_event_listeners.clone(),
  180. }
  181. }
  182. }
  183. impl<R: Runtime> Hash for DetachedWindow<R> {
  184. /// Only use the [`DetachedWindow`]'s label to represent its hash.
  185. fn hash<H: Hasher>(&self, state: &mut H) {
  186. self.label.hash(state)
  187. }
  188. }
  189. impl<R: Runtime> Eq for DetachedWindow<R> {}
  190. impl<R: Runtime> PartialEq for DetachedWindow<R> {
  191. /// Only use the [`DetachedWindow`]'s label to compare equality.
  192. fn eq(&self, other: &Self) -> bool {
  193. self.label.eq(&other.label)
  194. }
  195. }