window.rs 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! The Tauri window types and functions.
  5. pub(crate) mod menu;
  6. pub use menu::{MenuEvent, MenuHandle};
  7. use url::Url;
  8. #[cfg(target_os = "macos")]
  9. use crate::TitleBarStyle;
  10. use crate::{
  11. app::AppHandle,
  12. command::{CommandArg, CommandItem},
  13. event::{Event, EventHandler},
  14. hooks::{InvokePayload, InvokeResponder},
  15. manager::WindowManager,
  16. runtime::{
  17. http::{Request as HttpRequest, Response as HttpResponse},
  18. monitor::Monitor as RuntimeMonitor,
  19. webview::{WebviewAttributes, WindowBuilder as _},
  20. window::{
  21. dpi::{PhysicalPosition, PhysicalSize},
  22. DetachedWindow, JsEventListenerKey, PendingWindow,
  23. },
  24. Dispatch, RuntimeHandle,
  25. },
  26. sealed::ManagerBase,
  27. sealed::RuntimeOrDispatch,
  28. utils::config::{WindowConfig, WindowUrl},
  29. EventLoopMessage, Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, PageLoadPayload,
  30. Runtime, Theme, WindowEvent,
  31. };
  32. #[cfg(desktop)]
  33. use crate::{
  34. runtime::{
  35. menu::Menu,
  36. window::dpi::{Position, Size},
  37. UserAttentionType,
  38. },
  39. CursorIcon, Icon,
  40. };
  41. use serde::Serialize;
  42. #[cfg(windows)]
  43. use windows::Win32::Foundation::HWND;
  44. use tauri_macros::default_runtime;
  45. use std::{
  46. fmt,
  47. hash::{Hash, Hasher},
  48. path::PathBuf,
  49. sync::Arc,
  50. };
  51. pub(crate) type WebResourceRequestHandler = dyn Fn(&HttpRequest, &mut HttpResponse) + Send + Sync;
  52. pub(crate) type NavigationHandler = dyn Fn(Url) -> bool + Send;
  53. #[derive(Clone, Serialize)]
  54. struct WindowCreatedEvent {
  55. label: String,
  56. }
  57. /// Monitor descriptor.
  58. #[derive(Debug, Clone, Serialize)]
  59. #[serde(rename_all = "camelCase")]
  60. pub struct Monitor {
  61. pub(crate) name: Option<String>,
  62. pub(crate) size: PhysicalSize<u32>,
  63. pub(crate) position: PhysicalPosition<i32>,
  64. pub(crate) scale_factor: f64,
  65. }
  66. impl From<RuntimeMonitor> for Monitor {
  67. fn from(monitor: RuntimeMonitor) -> Self {
  68. Self {
  69. name: monitor.name,
  70. size: monitor.size,
  71. position: monitor.position,
  72. scale_factor: monitor.scale_factor,
  73. }
  74. }
  75. }
  76. impl Monitor {
  77. /// Returns a human-readable name of the monitor.
  78. /// Returns None if the monitor doesn't exist anymore.
  79. pub fn name(&self) -> Option<&String> {
  80. self.name.as_ref()
  81. }
  82. /// Returns the monitor's resolution.
  83. pub fn size(&self) -> &PhysicalSize<u32> {
  84. &self.size
  85. }
  86. /// Returns the top-left corner position of the monitor relative to the larger full screen area.
  87. pub fn position(&self) -> &PhysicalPosition<i32> {
  88. &self.position
  89. }
  90. /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
  91. pub fn scale_factor(&self) -> f64 {
  92. self.scale_factor
  93. }
  94. }
  95. /// A builder for a webview window managed by Tauri.
  96. #[default_runtime(crate::Wry, wry)]
  97. pub struct WindowBuilder<'a, R: Runtime> {
  98. manager: WindowManager<R>,
  99. runtime: RuntimeOrDispatch<'a, R>,
  100. app_handle: AppHandle<R>,
  101. label: String,
  102. pub(crate) window_builder: <R::Dispatcher as Dispatch<EventLoopMessage>>::WindowBuilder,
  103. pub(crate) webview_attributes: WebviewAttributes,
  104. web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
  105. navigation_handler: Option<Box<NavigationHandler>>,
  106. }
  107. impl<'a, R: Runtime> fmt::Debug for WindowBuilder<'a, R> {
  108. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  109. f.debug_struct("WindowBuilder")
  110. .field("manager", &self.manager)
  111. .field("app_handle", &self.app_handle)
  112. .field("label", &self.label)
  113. .field("window_builder", &self.window_builder)
  114. .field("webview_attributes", &self.webview_attributes)
  115. .finish()
  116. }
  117. }
  118. impl<'a, R: Runtime> WindowBuilder<'a, R> {
  119. /// Initializes a webview window builder with the given window label and URL to load on the webview.
  120. ///
  121. /// # Known issues
  122. ///
  123. /// On Windows, this function deadlocks when used in a synchronous command, see [the Webview2 issue].
  124. /// You should use `async` commands when creating windows.
  125. ///
  126. /// # Examples
  127. ///
  128. /// - Create a window in the setup hook:
  129. ///
  130. /// ```
  131. /// tauri::Builder::default()
  132. /// .setup(|app| {
  133. /// let window = tauri::WindowBuilder::new(app, "label", tauri::WindowUrl::App("index.html".into()))
  134. /// .build()?;
  135. /// Ok(())
  136. /// });
  137. /// ```
  138. ///
  139. /// - Create a window in a separate thread:
  140. ///
  141. /// ```
  142. /// tauri::Builder::default()
  143. /// .setup(|app| {
  144. /// let handle = app.handle();
  145. /// std::thread::spawn(move || {
  146. /// let window = tauri::WindowBuilder::new(&handle, "label", tauri::WindowUrl::App("index.html".into()))
  147. /// .build()
  148. /// .unwrap();
  149. /// });
  150. /// Ok(())
  151. /// });
  152. /// ```
  153. ///
  154. /// - Create a window in a command:
  155. ///
  156. /// ```
  157. /// #[tauri::command]
  158. /// async fn create_window(app: tauri::AppHandle) {
  159. /// let window = tauri::WindowBuilder::new(&app, "label", tauri::WindowUrl::External("https://tauri.app/".parse().unwrap()))
  160. /// .build()
  161. /// .unwrap();
  162. /// }
  163. /// ```
  164. ///
  165. /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
  166. pub fn new<M: Manager<R>, L: Into<String>>(manager: &'a M, label: L, url: WindowUrl) -> Self {
  167. let runtime = manager.runtime();
  168. let app_handle = manager.app_handle();
  169. Self {
  170. manager: manager.manager().clone(),
  171. runtime,
  172. app_handle,
  173. label: label.into(),
  174. window_builder: <R::Dispatcher as Dispatch<EventLoopMessage>>::WindowBuilder::new(),
  175. webview_attributes: WebviewAttributes::new(url),
  176. web_resource_request_handler: None,
  177. navigation_handler: None,
  178. }
  179. }
  180. /// Initializes a webview window builder from a window config from tauri.conf.json.
  181. /// Keep in mind that you can't create 2 windows with the same `label` so make sure
  182. /// that the initial window was closed or change the label of the new `WindowBuilder`.
  183. ///
  184. /// # Known issues
  185. ///
  186. /// On Windows, this function deadlocks when used in a synchronous command, see [the Webview2 issue].
  187. /// You should use `async` commands when creating windows.
  188. ///
  189. /// # Examples
  190. ///
  191. /// - Create a window in a command:
  192. ///
  193. /// ```
  194. /// #[tauri::command]
  195. /// async fn reopen_window(app: tauri::AppHandle) {
  196. /// let window = tauri::WindowBuilder::from_config(&app, app.config().tauri.windows.get(0).unwrap().clone())
  197. /// .build()
  198. /// .unwrap();
  199. /// }
  200. /// ```
  201. ///
  202. /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
  203. pub fn from_config<M: Manager<R>>(manager: &'a M, config: WindowConfig) -> Self {
  204. let runtime = manager.runtime();
  205. let app_handle = manager.app_handle();
  206. let url = config.url.clone();
  207. let file_drop_enabled = config.file_drop_enabled;
  208. let mut builder = Self {
  209. manager: manager.manager().clone(),
  210. runtime,
  211. app_handle,
  212. label: config.label.clone(),
  213. window_builder: <R::Dispatcher as Dispatch<EventLoopMessage>>::WindowBuilder::with_config(
  214. config,
  215. ),
  216. webview_attributes: WebviewAttributes::new(url),
  217. web_resource_request_handler: None,
  218. navigation_handler: None,
  219. };
  220. if !file_drop_enabled {
  221. builder = builder.disable_file_drop_handler();
  222. }
  223. builder
  224. }
  225. /// Defines a closure to be executed when the webview makes an HTTP request for a web resource, allowing you to modify the response.
  226. ///
  227. /// Currently only implemented for the `tauri` URI protocol.
  228. ///
  229. /// **NOTE:** Currently this is **not** executed when using external URLs such as a development server,
  230. /// but it might be implemented in the future. **Always** check the request URL.
  231. ///
  232. /// # Examples
  233. ///
  234. /// ```rust,no_run
  235. /// use tauri::{
  236. /// utils::config::{Csp, CspDirectiveSources, WindowUrl},
  237. /// http::header::HeaderValue,
  238. /// window::WindowBuilder,
  239. /// };
  240. /// use std::collections::HashMap;
  241. /// tauri::Builder::default()
  242. /// .setup(|app| {
  243. /// WindowBuilder::new(app, "core", WindowUrl::App("index.html".into()))
  244. /// .on_web_resource_request(|request, response| {
  245. /// if request.uri().starts_with("tauri://") {
  246. /// // if we have a CSP header, Tauri is loading an HTML file
  247. /// // for this example, let's dynamically change the CSP
  248. /// if let Some(csp) = response.headers_mut().get_mut("Content-Security-Policy") {
  249. /// // use the tauri helper to parse the CSP policy to a map
  250. /// let mut csp_map: HashMap<String, CspDirectiveSources> = Csp::Policy(csp.to_str().unwrap().to_string()).into();
  251. /// csp_map.entry("script-src".to_string()).or_insert_with(Default::default).push("'unsafe-inline'");
  252. /// // use the tauri helper to get a CSP string from the map
  253. /// let csp_string = Csp::from(csp_map).to_string();
  254. /// *csp = HeaderValue::from_str(&csp_string).unwrap();
  255. /// }
  256. /// }
  257. /// })
  258. /// .build()?;
  259. /// Ok(())
  260. /// });
  261. /// ```
  262. pub fn on_web_resource_request<F: Fn(&HttpRequest, &mut HttpResponse) + Send + Sync + 'static>(
  263. mut self,
  264. f: F,
  265. ) -> Self {
  266. self.web_resource_request_handler.replace(Box::new(f));
  267. self
  268. }
  269. /// Defines a closure to be executed when the webview navigates to a URL. Returning `false` cancels the navigation.
  270. ///
  271. /// # Examples
  272. ///
  273. /// ```rust,no_run
  274. /// use tauri::{
  275. /// utils::config::{Csp, CspDirectiveSources, WindowUrl},
  276. /// http::header::HeaderValue,
  277. /// window::WindowBuilder,
  278. /// };
  279. /// use std::collections::HashMap;
  280. /// tauri::Builder::default()
  281. /// .setup(|app| {
  282. /// WindowBuilder::new(app, "core", WindowUrl::App("index.html".into()))
  283. /// .on_navigation(|url| {
  284. /// // allow the production URL or localhost on dev
  285. /// url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
  286. /// })
  287. /// .build()?;
  288. /// Ok(())
  289. /// });
  290. /// ```
  291. pub fn on_navigation<F: Fn(Url) -> bool + Send + 'static>(mut self, f: F) -> Self {
  292. self.navigation_handler.replace(Box::new(f));
  293. self
  294. }
  295. /// Creates a new webview window.
  296. pub fn build(mut self) -> crate::Result<Window<R>> {
  297. let web_resource_request_handler = self.web_resource_request_handler.take();
  298. let pending = PendingWindow::new(
  299. self.window_builder.clone(),
  300. self.webview_attributes.clone(),
  301. self.label.clone(),
  302. )?;
  303. let labels = self.manager.labels().into_iter().collect::<Vec<_>>();
  304. let mut pending = self.manager.prepare_window(
  305. self.app_handle.clone(),
  306. pending,
  307. &labels,
  308. web_resource_request_handler,
  309. )?;
  310. pending.navigation_handler = self.navigation_handler.take();
  311. let window = match &mut self.runtime {
  312. RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending),
  313. RuntimeOrDispatch::RuntimeHandle(handle) => handle.create_window(pending),
  314. RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_window(pending),
  315. }
  316. .map(|window| self.manager.attach_window(self.app_handle.clone(), window))?;
  317. self.manager.eval_script_all(format!(
  318. "window.__TAURI_METADATA__.__windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }})",
  319. window_labels_array = serde_json::to_string(&self.manager.labels())?,
  320. ))?;
  321. self.manager.emit_filter(
  322. "tauri://window-created",
  323. None,
  324. Some(WindowCreatedEvent {
  325. label: window.label().into(),
  326. }),
  327. |w| w != &window,
  328. )?;
  329. Ok(window)
  330. }
  331. }
  332. /// Desktop APIs.
  333. #[cfg(desktop)]
  334. impl<'a, R: Runtime> WindowBuilder<'a, R> {
  335. /// Sets the menu for the window.
  336. #[must_use]
  337. pub fn menu(mut self, menu: Menu) -> Self {
  338. self.window_builder = self.window_builder.menu(menu);
  339. self
  340. }
  341. /// Show window in the center of the screen.
  342. #[must_use]
  343. pub fn center(mut self) -> Self {
  344. self.window_builder = self.window_builder.center();
  345. self
  346. }
  347. /// The initial position of the window's.
  348. #[must_use]
  349. pub fn position(mut self, x: f64, y: f64) -> Self {
  350. self.window_builder = self.window_builder.position(x, y);
  351. self
  352. }
  353. /// Window size.
  354. #[must_use]
  355. pub fn inner_size(mut self, width: f64, height: f64) -> Self {
  356. self.window_builder = self.window_builder.inner_size(width, height);
  357. self
  358. }
  359. /// Window min inner size.
  360. #[must_use]
  361. pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
  362. self.window_builder = self.window_builder.min_inner_size(min_width, min_height);
  363. self
  364. }
  365. /// Window max inner size.
  366. #[must_use]
  367. pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
  368. self.window_builder = self.window_builder.max_inner_size(max_width, max_height);
  369. self
  370. }
  371. /// Whether the window is resizable or not.
  372. #[must_use]
  373. pub fn resizable(mut self, resizable: bool) -> Self {
  374. self.window_builder = self.window_builder.resizable(resizable);
  375. self
  376. }
  377. /// The title of the window in the title bar.
  378. #[must_use]
  379. pub fn title<S: Into<String>>(mut self, title: S) -> Self {
  380. self.window_builder = self.window_builder.title(title);
  381. self
  382. }
  383. /// Whether to start the window in fullscreen or not.
  384. #[must_use]
  385. pub fn fullscreen(mut self, fullscreen: bool) -> Self {
  386. self.window_builder = self.window_builder.fullscreen(fullscreen);
  387. self
  388. }
  389. /// Sets the window to be initially focused.
  390. #[must_use]
  391. #[deprecated(
  392. since = "1.2.0",
  393. note = "The window is automatically focused by default. This function Will be removed in 2.0.0. Use `focused` instead."
  394. )]
  395. pub fn focus(mut self) -> Self {
  396. self.window_builder = self.window_builder.focused(true);
  397. self
  398. }
  399. /// Whether the window will be initially focused or not.
  400. #[must_use]
  401. pub fn focused(mut self, focused: bool) -> Self {
  402. self.window_builder = self.window_builder.focused(focused);
  403. self
  404. }
  405. /// Whether the window should be maximized upon creation.
  406. #[must_use]
  407. pub fn maximized(mut self, maximized: bool) -> Self {
  408. self.window_builder = self.window_builder.maximized(maximized);
  409. self
  410. }
  411. /// Whether the window should be immediately visible upon creation.
  412. #[must_use]
  413. pub fn visible(mut self, visible: bool) -> Self {
  414. self.window_builder = self.window_builder.visible(visible);
  415. self
  416. }
  417. /// Forces a theme or uses the system settings if None was provided.
  418. ///
  419. /// ## Platform-specific
  420. ///
  421. /// - **macOS**: Only supported on macOS 10.14+.
  422. #[must_use]
  423. pub fn theme(mut self, theme: Option<Theme>) -> Self {
  424. self.window_builder = self.window_builder.theme(theme);
  425. self
  426. }
  427. /// Whether the window should be transparent. If this is true, writing colors
  428. /// with alpha values different than `1.0` will produce a transparent window.
  429. #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
  430. #[cfg_attr(
  431. doc_cfg,
  432. doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
  433. )]
  434. #[must_use]
  435. pub fn transparent(mut self, transparent: bool) -> Self {
  436. self.window_builder = self.window_builder.transparent(transparent);
  437. self
  438. }
  439. /// Whether the window should have borders and bars.
  440. #[must_use]
  441. pub fn decorations(mut self, decorations: bool) -> Self {
  442. self.window_builder = self.window_builder.decorations(decorations);
  443. self
  444. }
  445. /// Whether the window should always be on top of other windows.
  446. #[must_use]
  447. pub fn always_on_top(mut self, always_on_top: bool) -> Self {
  448. self.window_builder = self.window_builder.always_on_top(always_on_top);
  449. self
  450. }
  451. /// Whether the window should always be on top of other windows.
  452. #[must_use]
  453. pub fn content_protected(mut self, protected: bool) -> Self {
  454. self.window_builder = self.window_builder.content_protected(protected);
  455. self
  456. }
  457. /// Sets the window icon.
  458. pub fn icon(mut self, icon: Icon) -> crate::Result<Self> {
  459. self.window_builder = self.window_builder.icon(icon.try_into()?)?;
  460. Ok(self)
  461. }
  462. /// Sets whether or not the window icon should be hidden from the taskbar.
  463. ///
  464. /// ## Platform-specific
  465. ///
  466. /// - **macOS**: Unsupported.
  467. #[must_use]
  468. pub fn skip_taskbar(mut self, skip: bool) -> Self {
  469. self.window_builder = self.window_builder.skip_taskbar(skip);
  470. self
  471. }
  472. /// Sets whether or not the window has shadow.
  473. ///
  474. /// ## Platform-specific
  475. ///
  476. /// - **Windows:**
  477. /// - `false` has no effect on decorated window, shadows are always ON.
  478. /// - `true` will make ndecorated window have a 1px white border,
  479. /// and on Windows 11, it will have a rounded corners.
  480. /// - **Linux:** Unsupported.
  481. #[must_use]
  482. pub fn shadow(mut self, enable: bool) -> Self {
  483. self.window_builder = self.window_builder.shadow(enable);
  484. self
  485. }
  486. /// Sets a parent to the window to be created.
  487. ///
  488. /// A child window has the WS_CHILD style and is confined to the client area of its parent window.
  489. ///
  490. /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
  491. #[cfg(windows)]
  492. #[must_use]
  493. pub fn parent_window(mut self, parent: HWND) -> Self {
  494. self.window_builder = self.window_builder.parent_window(parent);
  495. self
  496. }
  497. /// Sets a parent to the window to be created.
  498. #[cfg(target_os = "macos")]
  499. #[must_use]
  500. pub fn parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
  501. self.window_builder = self.window_builder.parent_window(parent);
  502. self
  503. }
  504. /// Set an owner to the window to be created.
  505. ///
  506. /// From MSDN:
  507. /// - An owned window is always above its owner in the z-order.
  508. /// - The system automatically destroys an owned window when its owner is destroyed.
  509. /// - An owned window is hidden when its owner is minimized.
  510. ///
  511. /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
  512. #[cfg(windows)]
  513. #[must_use]
  514. pub fn owner_window(mut self, owner: HWND) -> Self {
  515. self.window_builder = self.window_builder.owner_window(owner);
  516. self
  517. }
  518. /// Sets the [`TitleBarStyle`].
  519. #[cfg(target_os = "macos")]
  520. #[must_use]
  521. pub fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
  522. self.window_builder = self.window_builder.title_bar_style(style);
  523. self
  524. }
  525. /// Hide the window title.
  526. #[cfg(target_os = "macos")]
  527. #[must_use]
  528. pub fn hidden_title(mut self, hidden: bool) -> Self {
  529. self.window_builder = self.window_builder.hidden_title(hidden);
  530. self
  531. }
  532. /// Defines the window [tabbing identifier] for macOS.
  533. ///
  534. /// Windows with matching tabbing identifiers will be grouped together.
  535. /// If the tabbing identifier is not set, automatic tabbing will be disabled.
  536. ///
  537. /// [tabbing identifier]: <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
  538. #[cfg(target_os = "macos")]
  539. #[must_use]
  540. pub fn tabbing_identifier(mut self, identifier: &str) -> Self {
  541. self.window_builder = self.window_builder.tabbing_identifier(identifier);
  542. self
  543. }
  544. /// Sets whether clicking an inactive window also clicks through to the webview.
  545. #[must_use]
  546. pub fn accept_first_mouse(mut self, accept: bool) -> Self {
  547. self.webview_attributes.accept_first_mouse = accept;
  548. self
  549. }
  550. }
  551. /// Webview attributes.
  552. impl<'a, R: Runtime> WindowBuilder<'a, R> {
  553. /// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
  554. /// but before the HTML document has been parsed and before any other script included by the HTML document is run.
  555. ///
  556. /// Since it runs on all top-level document and child frame page navigations,
  557. /// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
  558. ///
  559. /// # Examples
  560. ///
  561. /// ```rust
  562. /// use tauri::{WindowBuilder, Runtime};
  563. ///
  564. /// const INIT_SCRIPT: &str = r#"
  565. /// if (window.location.origin === 'https://tauri.app') {
  566. /// console.log("hello world from js init script");
  567. ///
  568. /// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
  569. /// }
  570. /// "#;
  571. ///
  572. /// fn main() {
  573. /// tauri::Builder::default()
  574. /// .setup(|app| {
  575. /// let window = tauri::WindowBuilder::new(app, "label", tauri::WindowUrl::App("index.html".into()))
  576. /// .initialization_script(INIT_SCRIPT)
  577. /// .build()?;
  578. /// Ok(())
  579. /// });
  580. /// }
  581. /// ```
  582. #[must_use]
  583. pub fn initialization_script(mut self, script: &str) -> Self {
  584. self
  585. .webview_attributes
  586. .initialization_scripts
  587. .push(script.to_string());
  588. self
  589. }
  590. /// Set the user agent for the webview
  591. #[must_use]
  592. pub fn user_agent(mut self, user_agent: &str) -> Self {
  593. self.webview_attributes.user_agent = Some(user_agent.to_string());
  594. self
  595. }
  596. /// Set additional arguments for the webview.
  597. ///
  598. /// ## Platform-specific
  599. ///
  600. /// - **macOS / Linux / Android / iOS**: Unsupported.
  601. ///
  602. /// ## Warning
  603. ///
  604. /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
  605. /// so if you use this method, you also need to disable these components by yourself if you want.
  606. #[must_use]
  607. pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
  608. self.webview_attributes.additional_browser_args = Some(additional_args.to_string());
  609. self
  610. }
  611. /// Data directory for the webview.
  612. #[must_use]
  613. pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
  614. self
  615. .webview_attributes
  616. .data_directory
  617. .replace(data_directory);
  618. self
  619. }
  620. /// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows.
  621. #[must_use]
  622. pub fn disable_file_drop_handler(mut self) -> Self {
  623. self.webview_attributes.file_drop_handler_enabled = false;
  624. self
  625. }
  626. /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
  627. ///
  628. /// **macOS** doesn't provide such method and is always enabled by default,
  629. /// but you still need to add menu item accelerators to use shortcuts.
  630. #[must_use]
  631. pub fn enable_clipboard_access(mut self) -> Self {
  632. self.webview_attributes.clipboard = true;
  633. self
  634. }
  635. }
  636. // TODO: expand these docs since this is a pretty important type
  637. /// A webview window managed by Tauri.
  638. ///
  639. /// This type also implements [`Manager`] which allows you to manage other windows attached to
  640. /// the same application.
  641. #[default_runtime(crate::Wry, wry)]
  642. #[derive(Debug)]
  643. pub struct Window<R: Runtime> {
  644. /// The webview window created by the runtime.
  645. window: DetachedWindow<EventLoopMessage, R>,
  646. /// The manager to associate this webview window with.
  647. manager: WindowManager<R>,
  648. pub(crate) app_handle: AppHandle<R>,
  649. }
  650. unsafe impl<R: Runtime> raw_window_handle::HasRawWindowHandle for Window<R> {
  651. fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
  652. self.window.dispatcher.raw_window_handle().unwrap()
  653. }
  654. }
  655. impl<R: Runtime> Clone for Window<R> {
  656. fn clone(&self) -> Self {
  657. Self {
  658. window: self.window.clone(),
  659. manager: self.manager.clone(),
  660. app_handle: self.app_handle.clone(),
  661. }
  662. }
  663. }
  664. impl<R: Runtime> Hash for Window<R> {
  665. /// Only use the [`Window`]'s label to represent its hash.
  666. fn hash<H: Hasher>(&self, state: &mut H) {
  667. self.window.label.hash(state)
  668. }
  669. }
  670. impl<R: Runtime> Eq for Window<R> {}
  671. impl<R: Runtime> PartialEq for Window<R> {
  672. /// Only use the [`Window`]'s label to compare equality.
  673. fn eq(&self, other: &Self) -> bool {
  674. self.window.label.eq(&other.window.label)
  675. }
  676. }
  677. impl<R: Runtime> Manager<R> for Window<R> {
  678. fn emit_to<S: Serialize + Clone>(
  679. &self,
  680. label: &str,
  681. event: &str,
  682. payload: S,
  683. ) -> crate::Result<()> {
  684. self
  685. .manager()
  686. .emit_filter(event, Some(self.label()), payload, |w| label == w.label())
  687. }
  688. fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
  689. self
  690. .manager()
  691. .emit_filter(event, Some(self.label()), payload, |_| true)
  692. }
  693. }
  694. impl<R: Runtime> ManagerBase<R> for Window<R> {
  695. fn manager(&self) -> &WindowManager<R> {
  696. &self.manager
  697. }
  698. fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
  699. RuntimeOrDispatch::Dispatch(self.dispatcher())
  700. }
  701. fn managed_app_handle(&self) -> AppHandle<R> {
  702. self.app_handle.clone()
  703. }
  704. }
  705. impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
  706. /// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
  707. fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
  708. Ok(command.message.window())
  709. }
  710. }
  711. /// The platform webview handle. Accessed with [`Window#method.with_webview`];
  712. #[cfg(feature = "wry")]
  713. #[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
  714. pub struct PlatformWebview(tauri_runtime_wry::Webview);
  715. #[cfg(feature = "wry")]
  716. impl PlatformWebview {
  717. /// Returns [`webkit2gtk::WebView`] handle.
  718. #[cfg(any(
  719. target_os = "linux",
  720. target_os = "dragonfly",
  721. target_os = "freebsd",
  722. target_os = "netbsd",
  723. target_os = "openbsd"
  724. ))]
  725. #[cfg_attr(
  726. doc_cfg,
  727. doc(cfg(any(
  728. target_os = "linux",
  729. target_os = "dragonfly",
  730. target_os = "freebsd",
  731. target_os = "netbsd",
  732. target_os = "openbsd"
  733. )))
  734. )]
  735. pub fn inner(&self) -> std::rc::Rc<webkit2gtk::WebView> {
  736. self.0.clone()
  737. }
  738. /// Returns the WebView2 controller.
  739. #[cfg(windows)]
  740. #[cfg_attr(doc_cfg, doc(cfg(windows)))]
  741. pub fn controller(
  742. &self,
  743. ) -> webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller {
  744. self.0.controller.clone()
  745. }
  746. /// Returns the [WKWebView] handle.
  747. ///
  748. /// [WKWebView]: https://developer.apple.com/documentation/webkit/wkwebview
  749. #[cfg(any(target_os = "macos", target_os = "ios"))]
  750. #[cfg_attr(doc_cfg, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
  751. pub fn inner(&self) -> cocoa::base::id {
  752. self.0.webview
  753. }
  754. /// Returns WKWebView [controller] handle.
  755. ///
  756. /// [controller]: https://developer.apple.com/documentation/webkit/wkusercontentcontroller
  757. #[cfg(any(target_os = "macos", target_os = "ios"))]
  758. #[cfg_attr(doc_cfg, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
  759. pub fn controller(&self) -> cocoa::base::id {
  760. self.0.manager
  761. }
  762. /// Returns [NSWindow] associated with the WKWebView webview.
  763. ///
  764. /// [NSWindow]: https://developer.apple.com/documentation/appkit/nswindow
  765. #[cfg(target_os = "macos")]
  766. #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
  767. pub fn ns_window(&self) -> cocoa::base::id {
  768. self.0.ns_window
  769. }
  770. /// Returns [UIViewController] used by the WKWebView webview NSWindow.
  771. ///
  772. /// [UIViewController]: https://developer.apple.com/documentation/uikit/uiviewcontroller
  773. #[cfg(target_os = "ios")]
  774. #[cfg_attr(doc_cfg, doc(cfg(target_os = "ios")))]
  775. pub fn view_controller(&self) -> cocoa::base::id {
  776. self.0.view_controller
  777. }
  778. /// Returns handle for JNI execution.
  779. #[cfg(target_os = "android")]
  780. pub fn jni_handle(&self) -> tauri_runtime_wry::wry::webview::JniHandle {
  781. self.0
  782. }
  783. }
  784. /// Base window functions.
  785. impl<R: Runtime> Window<R> {
  786. /// Create a new window that is attached to the manager.
  787. pub(crate) fn new(
  788. manager: WindowManager<R>,
  789. window: DetachedWindow<EventLoopMessage, R>,
  790. app_handle: AppHandle<R>,
  791. ) -> Self {
  792. Self {
  793. window,
  794. manager,
  795. app_handle,
  796. }
  797. }
  798. /// Initializes a webview window builder with the given window label and URL to load on the webview.
  799. ///
  800. /// Data URLs are only supported with the `window-data-url` feature flag.
  801. pub fn builder<'a, M: Manager<R>, L: Into<String>>(
  802. manager: &'a M,
  803. label: L,
  804. url: WindowUrl,
  805. ) -> WindowBuilder<'a, R> {
  806. WindowBuilder::<'a, R>::new(manager, label.into(), url)
  807. }
  808. pub(crate) fn invoke_responder(&self) -> Arc<InvokeResponder<R>> {
  809. self.manager.invoke_responder()
  810. }
  811. /// The current window's dispatcher.
  812. pub(crate) fn dispatcher(&self) -> R::Dispatcher {
  813. self.window.dispatcher.clone()
  814. }
  815. /// Runs the given closure on the main thread.
  816. pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
  817. self
  818. .window
  819. .dispatcher
  820. .run_on_main_thread(f)
  821. .map_err(Into::into)
  822. }
  823. /// The label of this window.
  824. pub fn label(&self) -> &str {
  825. &self.window.label
  826. }
  827. /// Registers a window event listener.
  828. pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
  829. self
  830. .window
  831. .dispatcher
  832. .on_window_event(move |event| f(&event.clone().into()));
  833. }
  834. /// Registers a menu event listener.
  835. pub fn on_menu_event<F: Fn(MenuEvent) + Send + 'static>(&self, f: F) -> uuid::Uuid {
  836. let menu_ids = self.window.menu_ids.clone();
  837. self.window.dispatcher.on_menu_event(move |event| {
  838. let id = menu_ids
  839. .lock()
  840. .unwrap()
  841. .get(&event.menu_item_id)
  842. .unwrap()
  843. .clone();
  844. f(MenuEvent { menu_item_id: id })
  845. })
  846. }
  847. /// Executes a closure, providing it with the webview handle that is specific to the current platform.
  848. ///
  849. /// The closure is executed on the main thread.
  850. ///
  851. /// # Examples
  852. ///
  853. /// ```rust,no_run
  854. /// #[cfg(target_os = "macos")]
  855. /// #[macro_use]
  856. /// extern crate objc;
  857. /// use tauri::Manager;
  858. ///
  859. /// fn main() {
  860. /// tauri::Builder::default()
  861. /// .setup(|app| {
  862. /// let main_window = app.get_window("main").unwrap();
  863. /// main_window.with_webview(|webview| {
  864. /// #[cfg(target_os = "linux")]
  865. /// {
  866. /// // see https://docs.rs/webkit2gtk/0.18.2/webkit2gtk/struct.WebView.html
  867. /// // and https://docs.rs/webkit2gtk/0.18.2/webkit2gtk/trait.WebViewExt.html
  868. /// use webkit2gtk::traits::WebViewExt;
  869. /// webview.inner().set_zoom_level(4.);
  870. /// }
  871. ///
  872. /// #[cfg(windows)]
  873. /// unsafe {
  874. /// // see https://docs.rs/webview2-com/0.19.1/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html
  875. /// webview.controller().SetZoomFactor(4.).unwrap();
  876. /// }
  877. ///
  878. /// #[cfg(target_os = "macos")]
  879. /// unsafe {
  880. /// let () = msg_send![webview.inner(), setPageZoom: 4.];
  881. /// let () = msg_send![webview.controller(), removeAllUserScripts];
  882. /// let bg_color: cocoa::base::id = msg_send![class!(NSColor), colorWithDeviceRed:0.5 green:0.2 blue:0.4 alpha:1.];
  883. /// let () = msg_send![webview.ns_window(), setBackgroundColor: bg_color];
  884. /// }
  885. ///
  886. /// #[cfg(target_os = "android")]
  887. /// {
  888. /// use jni::objects::JValue;
  889. /// webview.jni_handle().exec(|env, _, webview| {
  890. /// env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap();
  891. /// })
  892. /// }
  893. /// });
  894. /// Ok(())
  895. /// });
  896. /// }
  897. /// ```
  898. #[cfg(all(feature = "wry"))]
  899. #[cfg_attr(doc_cfg, doc(all(feature = "wry")))]
  900. pub fn with_webview<F: FnOnce(PlatformWebview) + Send + 'static>(
  901. &self,
  902. f: F,
  903. ) -> crate::Result<()> {
  904. self
  905. .window
  906. .dispatcher
  907. .with_webview(|w| f(PlatformWebview(*w.downcast().unwrap())))
  908. .map_err(Into::into)
  909. }
  910. }
  911. /// Window getters.
  912. impl<R: Runtime> Window<R> {
  913. /// Gets a handle to the window menu.
  914. pub fn menu_handle(&self) -> MenuHandle<R> {
  915. MenuHandle {
  916. ids: self.window.menu_ids.clone(),
  917. dispatcher: self.dispatcher(),
  918. }
  919. }
  920. /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
  921. pub fn scale_factor(&self) -> crate::Result<f64> {
  922. self.window.dispatcher.scale_factor().map_err(Into::into)
  923. }
  924. /// Returns the position of the top-left hand corner of the window's client area relative to the top-left hand corner of the desktop.
  925. pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
  926. self.window.dispatcher.inner_position().map_err(Into::into)
  927. }
  928. /// Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
  929. pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
  930. self.window.dispatcher.outer_position().map_err(Into::into)
  931. }
  932. /// Returns the physical size of the window's client area.
  933. ///
  934. /// The client area is the content of the window, excluding the title bar and borders.
  935. pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
  936. self.window.dispatcher.inner_size().map_err(Into::into)
  937. }
  938. /// Returns the physical size of the entire window.
  939. ///
  940. /// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
  941. pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
  942. self.window.dispatcher.outer_size().map_err(Into::into)
  943. }
  944. /// Gets the window's current fullscreen state.
  945. pub fn is_fullscreen(&self) -> crate::Result<bool> {
  946. self.window.dispatcher.is_fullscreen().map_err(Into::into)
  947. }
  948. /// Gets the window's current minimized state.
  949. pub fn is_minimized(&self) -> crate::Result<bool> {
  950. self.window.dispatcher.is_minimized().map_err(Into::into)
  951. }
  952. /// Gets the window's current maximized state.
  953. pub fn is_maximized(&self) -> crate::Result<bool> {
  954. self.window.dispatcher.is_maximized().map_err(Into::into)
  955. }
  956. /// Gets the window’s current decoration state.
  957. pub fn is_decorated(&self) -> crate::Result<bool> {
  958. self.window.dispatcher.is_decorated().map_err(Into::into)
  959. }
  960. /// Gets the window’s current resizable state.
  961. pub fn is_resizable(&self) -> crate::Result<bool> {
  962. self.window.dispatcher.is_resizable().map_err(Into::into)
  963. }
  964. /// Gets the window's current visibility state.
  965. pub fn is_visible(&self) -> crate::Result<bool> {
  966. self.window.dispatcher.is_visible().map_err(Into::into)
  967. }
  968. /// Gets the window's current title.
  969. pub fn title(&self) -> crate::Result<String> {
  970. self.window.dispatcher.title().map_err(Into::into)
  971. }
  972. /// Returns the monitor on which the window currently resides.
  973. ///
  974. /// Returns None if current monitor can't be detected.
  975. pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
  976. self
  977. .window
  978. .dispatcher
  979. .current_monitor()
  980. .map(|m| m.map(Into::into))
  981. .map_err(Into::into)
  982. }
  983. /// Returns the primary monitor of the system.
  984. ///
  985. /// Returns None if it can't identify any monitor as a primary one.
  986. pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
  987. self
  988. .window
  989. .dispatcher
  990. .primary_monitor()
  991. .map(|m| m.map(Into::into))
  992. .map_err(Into::into)
  993. }
  994. /// Returns the list of all the monitors available on the system.
  995. pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
  996. self
  997. .window
  998. .dispatcher
  999. .available_monitors()
  1000. .map(|m| m.into_iter().map(Into::into).collect())
  1001. .map_err(Into::into)
  1002. }
  1003. /// Returns the native handle that is used by this window.
  1004. #[cfg(target_os = "macos")]
  1005. pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
  1006. self
  1007. .window
  1008. .dispatcher
  1009. .raw_window_handle()
  1010. .map_err(Into::into)
  1011. .and_then(|handle| {
  1012. if let raw_window_handle::RawWindowHandle::AppKit(h) = handle {
  1013. Ok(h.ns_window)
  1014. } else {
  1015. Err(crate::Error::InvalidWindowHandle)
  1016. }
  1017. })
  1018. }
  1019. /// Returns the native handle that is used by this window.
  1020. #[cfg(windows)]
  1021. pub fn hwnd(&self) -> crate::Result<HWND> {
  1022. self
  1023. .window
  1024. .dispatcher
  1025. .raw_window_handle()
  1026. .map_err(Into::into)
  1027. .and_then(|handle| {
  1028. if let raw_window_handle::RawWindowHandle::Win32(h) = handle {
  1029. Ok(HWND(h.hwnd as _))
  1030. } else {
  1031. Err(crate::Error::InvalidWindowHandle)
  1032. }
  1033. })
  1034. }
  1035. /// Returns the `ApplicationWindow` from gtk crate that is used by this window.
  1036. ///
  1037. /// Note that this can only be used on the main thread.
  1038. #[cfg(any(
  1039. target_os = "linux",
  1040. target_os = "dragonfly",
  1041. target_os = "freebsd",
  1042. target_os = "netbsd",
  1043. target_os = "openbsd"
  1044. ))]
  1045. pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
  1046. self.window.dispatcher.gtk_window().map_err(Into::into)
  1047. }
  1048. /// Returns the current window theme.
  1049. ///
  1050. /// ## Platform-specific
  1051. ///
  1052. /// - **macOS**: Only supported on macOS 10.14+.
  1053. pub fn theme(&self) -> crate::Result<Theme> {
  1054. self.window.dispatcher.theme().map_err(Into::into)
  1055. }
  1056. }
  1057. /// Desktop window setters and actions.
  1058. #[cfg(desktop)]
  1059. impl<R: Runtime> Window<R> {
  1060. /// Centers the window.
  1061. pub fn center(&self) -> crate::Result<()> {
  1062. self.window.dispatcher.center().map_err(Into::into)
  1063. }
  1064. /// Requests user attention to the window, this has no effect if the application
  1065. /// is already focused. How requesting for user attention manifests is platform dependent,
  1066. /// see `UserAttentionType` for details.
  1067. ///
  1068. /// Providing `None` will unset the request for user attention. Unsetting the request for
  1069. /// user attention might not be done automatically by the WM when the window receives input.
  1070. ///
  1071. /// ## Platform-specific
  1072. ///
  1073. /// - **macOS:** `None` has no effect.
  1074. /// - **Linux:** Urgency levels have the same effect.
  1075. pub fn request_user_attention(
  1076. &self,
  1077. request_type: Option<UserAttentionType>,
  1078. ) -> crate::Result<()> {
  1079. self
  1080. .window
  1081. .dispatcher
  1082. .request_user_attention(request_type)
  1083. .map_err(Into::into)
  1084. }
  1085. /// Opens the dialog to prints the contents of the webview.
  1086. /// Currently only supported on macOS on `wry`.
  1087. /// `window.print()` works on all platforms.
  1088. pub fn print(&self) -> crate::Result<()> {
  1089. self.window.dispatcher.print().map_err(Into::into)
  1090. }
  1091. /// Determines if this window should be resizable.
  1092. pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
  1093. self
  1094. .window
  1095. .dispatcher
  1096. .set_resizable(resizable)
  1097. .map_err(Into::into)
  1098. }
  1099. /// Set this window's title.
  1100. pub fn set_title(&self, title: &str) -> crate::Result<()> {
  1101. self
  1102. .window
  1103. .dispatcher
  1104. .set_title(title.to_string())
  1105. .map_err(Into::into)
  1106. }
  1107. /// Maximizes this window.
  1108. pub fn maximize(&self) -> crate::Result<()> {
  1109. self.window.dispatcher.maximize().map_err(Into::into)
  1110. }
  1111. /// Un-maximizes this window.
  1112. pub fn unmaximize(&self) -> crate::Result<()> {
  1113. self.window.dispatcher.unmaximize().map_err(Into::into)
  1114. }
  1115. /// Minimizes this window.
  1116. pub fn minimize(&self) -> crate::Result<()> {
  1117. self.window.dispatcher.minimize().map_err(Into::into)
  1118. }
  1119. /// Un-minimizes this window.
  1120. pub fn unminimize(&self) -> crate::Result<()> {
  1121. self.window.dispatcher.unminimize().map_err(Into::into)
  1122. }
  1123. /// Show this window.
  1124. pub fn show(&self) -> crate::Result<()> {
  1125. self.window.dispatcher.show().map_err(Into::into)
  1126. }
  1127. /// Hide this window.
  1128. pub fn hide(&self) -> crate::Result<()> {
  1129. self.window.dispatcher.hide().map_err(Into::into)
  1130. }
  1131. /// Closes this window.
  1132. /// # Panics
  1133. ///
  1134. /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
  1135. /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
  1136. ///
  1137. /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
  1138. pub fn close(&self) -> crate::Result<()> {
  1139. self.window.dispatcher.close().map_err(Into::into)
  1140. }
  1141. /// Determines if this window should be [decorated].
  1142. ///
  1143. /// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration
  1144. pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
  1145. self
  1146. .window
  1147. .dispatcher
  1148. .set_decorations(decorations)
  1149. .map_err(Into::into)
  1150. }
  1151. /// Determines if this window should have shadow.
  1152. ///
  1153. /// ## Platform-specific
  1154. ///
  1155. /// - **Windows:**
  1156. /// - `false` has no effect on decorated window, shadow are always ON.
  1157. /// - `true` will make ndecorated window have a 1px white border,
  1158. /// and on Windows 11, it will have a rounded corners.
  1159. /// - **Linux:** Unsupported.
  1160. pub fn set_shadow(&self, enable: bool) -> crate::Result<()> {
  1161. self
  1162. .window
  1163. .dispatcher
  1164. .set_shadow(enable)
  1165. .map_err(Into::into)
  1166. }
  1167. /// Determines if this window should always be on top of other windows.
  1168. pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
  1169. self
  1170. .window
  1171. .dispatcher
  1172. .set_always_on_top(always_on_top)
  1173. .map_err(Into::into)
  1174. }
  1175. /// Prevents the window contents from being captured by other apps.
  1176. pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> {
  1177. self
  1178. .window
  1179. .dispatcher
  1180. .set_content_protected(protected)
  1181. .map_err(Into::into)
  1182. }
  1183. /// Resizes this window.
  1184. pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
  1185. self
  1186. .window
  1187. .dispatcher
  1188. .set_size(size.into())
  1189. .map_err(Into::into)
  1190. }
  1191. /// Sets this window's minimum size.
  1192. pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
  1193. self
  1194. .window
  1195. .dispatcher
  1196. .set_min_size(size.map(|s| s.into()))
  1197. .map_err(Into::into)
  1198. }
  1199. /// Sets this window's maximum size.
  1200. pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
  1201. self
  1202. .window
  1203. .dispatcher
  1204. .set_max_size(size.map(|s| s.into()))
  1205. .map_err(Into::into)
  1206. }
  1207. /// Sets this window's position.
  1208. pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
  1209. self
  1210. .window
  1211. .dispatcher
  1212. .set_position(position.into())
  1213. .map_err(Into::into)
  1214. }
  1215. /// Determines if this window should be fullscreen.
  1216. pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
  1217. self
  1218. .window
  1219. .dispatcher
  1220. .set_fullscreen(fullscreen)
  1221. .map_err(Into::into)
  1222. }
  1223. /// Bring the window to front and focus.
  1224. pub fn set_focus(&self) -> crate::Result<()> {
  1225. self.window.dispatcher.set_focus().map_err(Into::into)
  1226. }
  1227. /// Sets this window' icon.
  1228. pub fn set_icon(&self, icon: Icon) -> crate::Result<()> {
  1229. self
  1230. .window
  1231. .dispatcher
  1232. .set_icon(icon.try_into()?)
  1233. .map_err(Into::into)
  1234. }
  1235. /// Whether to hide the window icon from the taskbar or not.
  1236. ///
  1237. /// ## Platform-specific
  1238. ///
  1239. /// - **macOS:** Unsupported.
  1240. pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
  1241. self
  1242. .window
  1243. .dispatcher
  1244. .set_skip_taskbar(skip)
  1245. .map_err(Into::into)
  1246. }
  1247. /// Grabs the cursor, preventing it from leaving the window.
  1248. ///
  1249. /// There's no guarantee that the cursor will be hidden. You should
  1250. /// hide it by yourself if you want so.
  1251. ///
  1252. /// ## Platform-specific
  1253. ///
  1254. /// - **Linux:** Unsupported.
  1255. /// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
  1256. pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
  1257. self
  1258. .window
  1259. .dispatcher
  1260. .set_cursor_grab(grab)
  1261. .map_err(Into::into)
  1262. }
  1263. /// Modifies the cursor's visibility.
  1264. ///
  1265. /// If `false`, this will hide the cursor. If `true`, this will show the cursor.
  1266. ///
  1267. /// ## Platform-specific
  1268. ///
  1269. /// - **Windows:** The cursor is only hidden within the confines of the window.
  1270. /// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
  1271. /// outside of the window.
  1272. pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
  1273. self
  1274. .window
  1275. .dispatcher
  1276. .set_cursor_visible(visible)
  1277. .map_err(Into::into)
  1278. }
  1279. /// Modifies the cursor icon of the window.
  1280. pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
  1281. self
  1282. .window
  1283. .dispatcher
  1284. .set_cursor_icon(icon)
  1285. .map_err(Into::into)
  1286. }
  1287. /// Changes the position of the cursor in window coordinates.
  1288. pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
  1289. self
  1290. .window
  1291. .dispatcher
  1292. .set_cursor_position(position)
  1293. .map_err(Into::into)
  1294. }
  1295. /// Ignores the window cursor events.
  1296. pub fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
  1297. self
  1298. .window
  1299. .dispatcher
  1300. .set_ignore_cursor_events(ignore)
  1301. .map_err(Into::into)
  1302. }
  1303. /// Starts dragging the window.
  1304. pub fn start_dragging(&self) -> crate::Result<()> {
  1305. self.window.dispatcher.start_dragging().map_err(Into::into)
  1306. }
  1307. }
  1308. /// Webview APIs.
  1309. impl<R: Runtime> Window<R> {
  1310. /// Returns the current url of the webview.
  1311. pub fn url(&self) -> crate::Result<Url> {
  1312. self.window.dispatcher.url().map_err(Into::into)
  1313. }
  1314. /// Handles this window receiving an [`InvokeMessage`].
  1315. pub fn on_message(self, payload: InvokePayload) -> crate::Result<()> {
  1316. let manager = self.manager.clone();
  1317. match payload.cmd.as_str() {
  1318. "__initialized" => {
  1319. let payload: PageLoadPayload = serde_json::from_value(payload.inner)?;
  1320. manager.run_on_page_load(self, payload);
  1321. }
  1322. _ => {
  1323. let message = InvokeMessage::new(
  1324. self.clone(),
  1325. manager.state(),
  1326. payload.cmd.to_string(),
  1327. payload.inner,
  1328. );
  1329. #[allow(clippy::redundant_clone)]
  1330. let resolver = InvokeResolver::new(self.clone(), payload.callback, payload.error);
  1331. let mut invoke = Invoke { message, resolver };
  1332. if let Some(module) = &payload.tauri_module {
  1333. crate::endpoints::handle(
  1334. module.to_string(),
  1335. invoke,
  1336. manager.config(),
  1337. manager.package_info(),
  1338. );
  1339. } else if payload.cmd.starts_with("plugin:") {
  1340. let command = invoke.message.command.replace("plugin:", "");
  1341. let mut tokens = command.split('|');
  1342. // safe to unwrap: split always has a least one item
  1343. let plugin = tokens.next().unwrap();
  1344. invoke.message.command = tokens
  1345. .next()
  1346. .map(|c| c.to_string())
  1347. .unwrap_or_else(String::new);
  1348. let command = invoke.message.command.clone();
  1349. let resolver = invoke.resolver.clone();
  1350. #[cfg(mobile)]
  1351. let message = invoke.message.clone();
  1352. #[allow(unused_mut)]
  1353. let mut handled = manager.extend_api(plugin, invoke);
  1354. #[cfg(target_os = "ios")]
  1355. {
  1356. if !handled {
  1357. handled = true;
  1358. let plugin = plugin.to_string();
  1359. let (callback, error) = (resolver.callback, resolver.error);
  1360. self.with_webview(move |webview| {
  1361. unsafe {
  1362. crate::ios::post_ipc_message(
  1363. webview.inner(),
  1364. &plugin.as_str().into(),
  1365. &heck::ToLowerCamelCase::to_lower_camel_case(message.command.as_str())
  1366. .as_str()
  1367. .into(),
  1368. crate::ios::json_to_dictionary(message.payload),
  1369. callback.0,
  1370. error.0,
  1371. )
  1372. };
  1373. })?;
  1374. }
  1375. }
  1376. #[cfg(target_os = "android")]
  1377. {
  1378. if !handled {
  1379. handled = true;
  1380. let resolver_ = resolver.clone();
  1381. let runtime_handle = self.app_handle.runtime_handle.clone();
  1382. let plugin = plugin.to_string();
  1383. self.with_webview(move |webview| {
  1384. webview.jni_handle().exec(move |env, activity, webview| {
  1385. use jni::{
  1386. errors::Error as JniError,
  1387. objects::JObject,
  1388. JNIEnv,
  1389. };
  1390. use crate::api::ipc::CallbackFn;
  1391. fn handle_message<R: Runtime>(
  1392. plugin: &str,
  1393. runtime_handle: &R::Handle,
  1394. message: InvokeMessage<R>,
  1395. (callback, error): (CallbackFn, CallbackFn),
  1396. env: JNIEnv<'_>,
  1397. activity: JObject<'_>,
  1398. webview: JObject<'_>,
  1399. ) -> Result<(), JniError> {
  1400. let data = crate::jni_helpers::to_jsobject::<R>(env, activity, runtime_handle, message.payload)?;
  1401. let plugin_manager = env
  1402. .call_method(
  1403. activity,
  1404. "getPluginManager",
  1405. "()Lapp/tauri/plugin/PluginManager;",
  1406. &[],
  1407. )?
  1408. .l()?;
  1409. env.call_method(
  1410. plugin_manager,
  1411. "postIpcMessage",
  1412. "(Landroid/webkit/WebView;Ljava/lang/String;Ljava/lang/String;Lapp/tauri/plugin/JSObject;JJ)V",
  1413. &[
  1414. webview.into(),
  1415. env.new_string(plugin)?.into(),
  1416. env.new_string(&heck::ToLowerCamelCase::to_lower_camel_case(message.command.as_str()))?.into(),
  1417. data,
  1418. (callback.0 as i64).into(),
  1419. (error.0 as i64).into(),
  1420. ],
  1421. )?;
  1422. Ok(())
  1423. }
  1424. if let Err(e) = handle_message(
  1425. &plugin,
  1426. &runtime_handle,
  1427. message,
  1428. (resolver_.callback, resolver_.error),
  1429. env,
  1430. activity,
  1431. webview,
  1432. ) {
  1433. resolver_.reject(format!("failed to reach Android layer: {e}"));
  1434. }
  1435. });
  1436. })?;
  1437. }
  1438. }
  1439. if !handled {
  1440. resolver.reject(format!("Command {command} not found"));
  1441. }
  1442. } else {
  1443. let command = invoke.message.command.clone();
  1444. let resolver = invoke.resolver.clone();
  1445. let handled = manager.run_invoke_handler(invoke);
  1446. if !handled {
  1447. resolver.reject(format!("Command {command} not found"));
  1448. }
  1449. }
  1450. }
  1451. }
  1452. Ok(())
  1453. }
  1454. /// Evaluates JavaScript on this window.
  1455. pub fn eval(&self, js: &str) -> crate::Result<()> {
  1456. self.window.dispatcher.eval_script(js).map_err(Into::into)
  1457. }
  1458. pub(crate) fn register_js_listener(&self, window_label: Option<String>, event: String, id: u64) {
  1459. self
  1460. .window
  1461. .js_event_listeners
  1462. .lock()
  1463. .unwrap()
  1464. .entry(JsEventListenerKey {
  1465. window_label,
  1466. event,
  1467. })
  1468. .or_insert_with(Default::default)
  1469. .insert(id);
  1470. }
  1471. pub(crate) fn unregister_js_listener(&self, id: u64) {
  1472. let mut empty = None;
  1473. let mut js_listeners = self.window.js_event_listeners.lock().unwrap();
  1474. let iter = js_listeners.iter_mut();
  1475. for (key, ids) in iter {
  1476. if ids.contains(&id) {
  1477. ids.remove(&id);
  1478. if ids.is_empty() {
  1479. empty.replace(key.clone());
  1480. }
  1481. break;
  1482. }
  1483. }
  1484. if let Some(key) = empty {
  1485. js_listeners.remove(&key);
  1486. }
  1487. }
  1488. /// Whether this window registered a listener to an event from the given window and event name.
  1489. pub(crate) fn has_js_listener(&self, window_label: Option<String>, event: &str) -> bool {
  1490. self
  1491. .window
  1492. .js_event_listeners
  1493. .lock()
  1494. .unwrap()
  1495. .contains_key(&JsEventListenerKey {
  1496. window_label,
  1497. event: event.into(),
  1498. })
  1499. }
  1500. /// Opens the developer tools window (Web Inspector).
  1501. /// The devtools is only enabled on debug builds or with the `devtools` feature flag.
  1502. ///
  1503. /// ## Platform-specific
  1504. ///
  1505. /// - **macOS:** Only supported on macOS 10.15+.
  1506. /// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
  1507. ///
  1508. /// # Examples
  1509. ///
  1510. /// ```rust,no_run
  1511. /// use tauri::Manager;
  1512. /// tauri::Builder::default()
  1513. /// .setup(|app| {
  1514. /// #[cfg(debug_assertions)]
  1515. /// app.get_window("main").unwrap().open_devtools();
  1516. /// Ok(())
  1517. /// });
  1518. /// ```
  1519. #[cfg(any(debug_assertions, feature = "devtools"))]
  1520. #[cfg_attr(doc_cfg, doc(cfg(any(debug_assertions, feature = "devtools"))))]
  1521. pub fn open_devtools(&self) {
  1522. self.window.dispatcher.open_devtools();
  1523. }
  1524. /// Closes the developer tools window (Web Inspector).
  1525. /// The devtools is only enabled on debug builds or with the `devtools` feature flag.
  1526. ///
  1527. /// ## Platform-specific
  1528. ///
  1529. /// - **macOS:** Only supported on macOS 10.15+.
  1530. /// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
  1531. /// - **Windows:** Unsupported.
  1532. ///
  1533. /// # Examples
  1534. ///
  1535. /// ```rust,no_run
  1536. /// use tauri::Manager;
  1537. /// tauri::Builder::default()
  1538. /// .setup(|app| {
  1539. /// #[cfg(debug_assertions)]
  1540. /// {
  1541. /// let window = app.get_window("main").unwrap();
  1542. /// window.open_devtools();
  1543. /// std::thread::spawn(move || {
  1544. /// std::thread::sleep(std::time::Duration::from_secs(10));
  1545. /// window.close_devtools();
  1546. /// });
  1547. /// }
  1548. /// Ok(())
  1549. /// });
  1550. /// ```
  1551. #[cfg(any(debug_assertions, feature = "devtools"))]
  1552. #[cfg_attr(doc_cfg, doc(cfg(any(debug_assertions, feature = "devtools"))))]
  1553. pub fn close_devtools(&self) {
  1554. self.window.dispatcher.close_devtools();
  1555. }
  1556. /// Checks if the developer tools window (Web Inspector) is opened.
  1557. /// The devtools is only enabled on debug builds or with the `devtools` feature flag.
  1558. ///
  1559. /// ## Platform-specific
  1560. ///
  1561. /// - **macOS:** Only supported on macOS 10.15+.
  1562. /// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
  1563. /// - **Windows:** Unsupported.
  1564. ///
  1565. /// # Examples
  1566. ///
  1567. /// ```rust,no_run
  1568. /// use tauri::Manager;
  1569. /// tauri::Builder::default()
  1570. /// .setup(|app| {
  1571. /// #[cfg(debug_assertions)]
  1572. /// {
  1573. /// let window = app.get_window("main").unwrap();
  1574. /// if !window.is_devtools_open() {
  1575. /// window.open_devtools();
  1576. /// }
  1577. /// }
  1578. /// Ok(())
  1579. /// });
  1580. /// ```
  1581. #[cfg(any(debug_assertions, feature = "devtools"))]
  1582. #[cfg_attr(doc_cfg, doc(cfg(any(debug_assertions, feature = "devtools"))))]
  1583. pub fn is_devtools_open(&self) -> bool {
  1584. self
  1585. .window
  1586. .dispatcher
  1587. .is_devtools_open()
  1588. .unwrap_or_default()
  1589. }
  1590. }
  1591. /// Event system APIs.
  1592. impl<R: Runtime> Window<R> {
  1593. /// Emits an event to both the JavaScript and the Rust listeners.
  1594. pub fn emit_and_trigger<S: Serialize + Clone>(
  1595. &self,
  1596. event: &str,
  1597. payload: S,
  1598. ) -> crate::Result<()> {
  1599. self.trigger(event, Some(serde_json::to_string(&payload)?));
  1600. self.emit(event, payload)
  1601. }
  1602. pub(crate) fn emit_internal<S: Serialize>(
  1603. &self,
  1604. event: &str,
  1605. source_window_label: Option<&str>,
  1606. payload: S,
  1607. ) -> crate::Result<()> {
  1608. self.eval(&format!(
  1609. "(function () {{ const fn = window['{}']; fn && fn({{event: {}, windowLabel: {}, payload: {}}}) }})()",
  1610. self.manager.event_emit_function_name(),
  1611. serde_json::to_string(event)?,
  1612. serde_json::to_string(&source_window_label)?,
  1613. serde_json::to_value(payload)?,
  1614. ))?;
  1615. Ok(())
  1616. }
  1617. /// Emits an event to the JavaScript listeners on the current window.
  1618. ///
  1619. /// The event is only delivered to listeners that used the `WebviewWindow#listen` method on the @tauri-apps/api `window` module.
  1620. pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
  1621. self
  1622. .manager
  1623. .emit_filter(event, Some(self.label()), payload, |w| {
  1624. w.has_js_listener(None, event) || w.has_js_listener(Some(self.label().into()), event)
  1625. })?;
  1626. Ok(())
  1627. }
  1628. /// Listen to an event on this window.
  1629. ///
  1630. /// This listener only receives events that are triggered using the
  1631. /// [`trigger`](Window#method.trigger) and [`emit_and_trigger`](Window#method.emit_and_trigger) methods or
  1632. /// the `appWindow.emit` function from the @tauri-apps/api `window` module.
  1633. pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
  1634. where
  1635. F: Fn(Event) + Send + 'static,
  1636. {
  1637. let label = self.window.label.clone();
  1638. self.manager.listen(event.into(), Some(label), handler)
  1639. }
  1640. /// Unlisten to an event on this window.
  1641. pub fn unlisten(&self, handler_id: EventHandler) {
  1642. self.manager.unlisten(handler_id)
  1643. }
  1644. /// Listen to an event on this window a single time.
  1645. pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
  1646. where
  1647. F: FnOnce(Event) + Send + 'static,
  1648. {
  1649. let label = self.window.label.clone();
  1650. self.manager.once(event.into(), Some(label), handler)
  1651. }
  1652. /// Triggers an event to the Rust listeners on this window.
  1653. ///
  1654. /// The event is only delivered to listeners that used the [`listen`](Window#method.listen) method.
  1655. pub fn trigger(&self, event: &str, data: Option<String>) {
  1656. let label = self.window.label.clone();
  1657. self.manager.trigger(event, Some(label), data)
  1658. }
  1659. }
  1660. #[cfg(test)]
  1661. mod tests {
  1662. #[test]
  1663. fn window_is_send_sync() {
  1664. crate::test_utils::assert_send::<super::Window>();
  1665. crate::test_utils::assert_sync::<super::Window>();
  1666. }
  1667. }