tauri/webview/webview_window.rs
1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! [`Window`] that hosts a single [`Webview`].
6
7use std::{
8 borrow::Cow,
9 path::{Path, PathBuf},
10 sync::{Arc, MutexGuard},
11};
12
13use crate::{
14 event::EventTarget,
15 ipc::ScopeObject,
16 runtime::dpi::{PhysicalPosition, PhysicalSize},
17 webview::NewWindowResponse,
18 window::Monitor,
19 Emitter, EventName, Listener, ResourceTable, Window,
20};
21#[cfg(desktop)]
22use crate::{
23 image::Image,
24 menu::{ContextMenu, Menu},
25 runtime::{
26 dpi::{Position, Size},
27 window::CursorIcon,
28 UserAttentionType,
29 },
30};
31use tauri_runtime::webview::NewWindowFeatures;
32use tauri_utils::config::{BackgroundThrottlingPolicy, Color, WebviewUrl, WindowConfig};
33use url::Url;
34
35use crate::{
36 ipc::{CommandArg, CommandItem, InvokeError, OwnedInvokeResponder},
37 manager::AppManager,
38 sealed::{ManagerBase, RuntimeOrDispatch},
39 webview::{Cookie, PageLoadPayload, WebviewBuilder, WebviewEvent},
40 window::WindowBuilder,
41 AppHandle, Event, EventId, Manager, Runtime, Webview, WindowEvent,
42};
43
44use tauri_macros::default_runtime;
45
46#[cfg(windows)]
47use windows::Win32::Foundation::HWND;
48
49use super::{DownloadEvent, ResolvedScope};
50
51/// A builder for [`WebviewWindow`], a window that hosts a single webview.
52pub struct WebviewWindowBuilder<'a, R: Runtime, M: Manager<R>> {
53 window_builder: WindowBuilder<'a, R, M>,
54 webview_builder: WebviewBuilder<R>,
55}
56
57impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
58 /// Initializes a webview window builder with the given window label.
59 ///
60 /// # Known issues
61 ///
62 /// On Windows, this function deadlocks when used in a synchronous command and event handlers, see [the Webview2 issue].
63 /// You should use `async` commands and separate threads when creating windows.
64 ///
65 /// # Examples
66 ///
67 /// - Create a window in the setup hook:
68 ///
69 /// ```
70 /// tauri::Builder::default()
71 /// .setup(|app| {
72 /// let webview_window = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
73 /// .build()?;
74 /// Ok(())
75 /// });
76 /// ```
77 ///
78 /// - Create a window in a separate thread:
79 ///
80 /// ```
81 /// tauri::Builder::default()
82 /// .setup(|app| {
83 /// let handle = app.handle().clone();
84 /// std::thread::spawn(move || {
85 /// let webview_window = tauri::WebviewWindowBuilder::new(&handle, "label", tauri::WebviewUrl::App("index.html".into()))
86 /// .build()
87 /// .unwrap();
88 /// });
89 /// Ok(())
90 /// });
91 /// ```
92 ///
93 /// - Create a window in a command:
94 ///
95 /// ```
96 /// #[tauri::command]
97 /// async fn create_window(app: tauri::AppHandle) {
98 /// let webview_window = tauri::WebviewWindowBuilder::new(&app, "label", tauri::WebviewUrl::App("index.html".into()))
99 /// .build()
100 /// .unwrap();
101 /// }
102 /// ```
103 ///
104 /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
105 pub fn new<L: Into<String>>(manager: &'a M, label: L, url: WebviewUrl) -> Self {
106 let label = label.into();
107 Self {
108 window_builder: WindowBuilder::new(manager, &label),
109 webview_builder: WebviewBuilder::new(&label, url),
110 }
111 }
112
113 /// Initializes a webview window builder from a [`WindowConfig`] from tauri.conf.json.
114 /// Keep in mind that you can't create 2 windows with the same `label` so make sure
115 /// that the initial window was closed or change the label of the cloned [`WindowConfig`].
116 ///
117 /// # Known issues
118 ///
119 /// On Windows, this function deadlocks when used in a synchronous command or event handlers, see [the Webview2 issue].
120 /// You should use `async` commands and separate threads when creating windows.
121 ///
122 /// # Examples
123 ///
124 /// - Create a window in a command:
125 ///
126 /// ```
127 /// #[tauri::command]
128 /// async fn reopen_window(app: tauri::AppHandle) {
129 /// let webview_window = tauri::WebviewWindowBuilder::from_config(&app, &app.config().app.windows.get(0).unwrap())
130 /// .unwrap()
131 /// .build()
132 /// .unwrap();
133 /// }
134 /// ```
135 ///
136 /// - Create a window in a command from a config with a specific label, and change its label so multiple instances can exist:
137 ///
138 /// ```
139 /// #[tauri::command]
140 /// async fn open_window_multiple(app: tauri::AppHandle) {
141 /// let mut conf = app.config().app.windows.iter().find(|c| c.label == "template-for-multiwindow").unwrap().clone();
142 /// // This should be a unique label for all windows. For example, we can use a random suffix:
143 /// let mut buf = [0u8; 1];
144 /// assert_eq!(getrandom::fill(&mut buf), Ok(()));
145 /// conf.label = format!("my-multiwindow-{}", buf[0]);
146 /// let webview_window = tauri::WebviewWindowBuilder::from_config(&app, &conf)
147 /// .unwrap()
148 /// .build()
149 /// .unwrap();
150 /// }
151 /// ```
152 ///
153 /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
154 pub fn from_config(manager: &'a M, config: &WindowConfig) -> crate::Result<Self> {
155 Ok(Self {
156 window_builder: WindowBuilder::from_config(manager, config)?,
157 webview_builder: WebviewBuilder::from_config(config),
158 })
159 }
160
161 /// Registers a global menu event listener.
162 ///
163 /// Note that this handler is called for any menu event,
164 /// whether it is coming from this window, another window or from the tray icon menu.
165 ///
166 /// Also note that this handler will not be called if
167 /// the window used to register it was closed.
168 ///
169 /// # Examples
170 /// ```
171 /// use tauri::menu::{Menu, Submenu, MenuItem};
172 /// tauri::Builder::default()
173 /// .setup(|app| {
174 /// let handle = app.handle();
175 /// let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
176 /// let menu = Menu::with_items(handle, &[
177 /// &Submenu::with_items(handle, "File", true, &[
178 /// &save_menu_item,
179 /// ])?,
180 /// ])?;
181 /// let webview_window = tauri::WebviewWindowBuilder::new(app, "editor", tauri::WebviewUrl::App("index.html".into()))
182 /// .menu(menu)
183 /// .on_menu_event(move |window, event| {
184 /// if event.id == save_menu_item.id() {
185 /// // save menu item
186 /// }
187 /// })
188 /// .build()
189 /// .unwrap();
190 ///
191 /// Ok(())
192 /// });
193 /// ```
194 #[cfg(desktop)]
195 pub fn on_menu_event<F: Fn(&crate::Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
196 mut self,
197 f: F,
198 ) -> Self {
199 self.window_builder = self.window_builder.on_menu_event(f);
200 self
201 }
202
203 /// Defines a closure to be executed when the webview makes an HTTP request for a web resource, allowing you to modify the response.
204 ///
205 /// Currently only implemented for the `tauri` URI protocol.
206 ///
207 /// **NOTE:** Currently this is **not** executed when using external URLs such as a development server,
208 /// but it might be implemented in the future. **Always** check the request URL.
209 ///
210 /// # Examples
211 /// ```rust,no_run
212 /// use tauri::{
213 /// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
214 /// webview::WebviewWindowBuilder,
215 /// };
216 /// use http::header::HeaderValue;
217 /// use std::collections::HashMap;
218 /// tauri::Builder::default()
219 /// .setup(|app| {
220 /// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
221 /// .on_web_resource_request(|request, response| {
222 /// if request.uri().scheme_str() == Some("tauri") {
223 /// // if we have a CSP header, Tauri is loading an HTML file
224 /// // for this example, let's dynamically change the CSP
225 /// if let Some(csp) = response.headers_mut().get_mut("Content-Security-Policy") {
226 /// // use the tauri helper to parse the CSP policy to a map
227 /// let mut csp_map: HashMap<String, CspDirectiveSources> = Csp::Policy(csp.to_str().unwrap().to_string()).into();
228 /// csp_map.entry("script-src".to_string()).or_insert_with(Default::default).push("'unsafe-inline'");
229 /// // use the tauri helper to get a CSP string from the map
230 /// let csp_string = Csp::from(csp_map).to_string();
231 /// *csp = HeaderValue::from_str(&csp_string).unwrap();
232 /// }
233 /// }
234 /// })
235 /// .build()?;
236 /// Ok(())
237 /// });
238 /// ```
239 pub fn on_web_resource_request<
240 F: Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync + 'static,
241 >(
242 mut self,
243 f: F,
244 ) -> Self {
245 self.webview_builder = self.webview_builder.on_web_resource_request(f);
246 self
247 }
248
249 /// Defines a closure to be executed when the webview navigates to a URL. Returning `false` cancels the navigation.
250 ///
251 /// # Examples
252 /// ```rust,no_run
253 /// use tauri::{
254 /// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
255 /// webview::WebviewWindowBuilder,
256 /// };
257 /// use http::header::HeaderValue;
258 /// use std::collections::HashMap;
259 /// tauri::Builder::default()
260 /// .setup(|app| {
261 /// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
262 /// .on_navigation(|url| {
263 /// // allow the production URL or localhost on dev
264 /// url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
265 /// })
266 /// .build()?;
267 /// Ok(())
268 /// });
269 /// ```
270 pub fn on_navigation<F: Fn(&Url) -> bool + Send + 'static>(mut self, f: F) -> Self {
271 self.webview_builder = self.webview_builder.on_navigation(f);
272 self
273 }
274
275 /// Set a new window request handler to decide if incoming url is allowed to be opened.
276 ///
277 /// A new window is requested to be opened by the [window.open] API.
278 ///
279 /// The closure take the URL to open and the window features object and returns [`NewWindowResponse`] to determine whether the window should open.
280 ///
281 /// # Examples
282 /// ```rust,no_run
283 /// use tauri::{
284 /// utils::config::WebviewUrl,
285 /// webview::WebviewWindowBuilder,
286 /// };
287 /// use http::header::HeaderValue;
288 /// use std::collections::HashMap;
289 /// tauri::Builder::default()
290 /// .setup(|app| {
291 /// let app_ = app.handle().clone();
292 /// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
293 /// .on_new_window(move |url, features| {
294 /// let builder = tauri::WebviewWindowBuilder::new(
295 /// &app_,
296 /// // note: add an ID counter or random label generator to support multiple opened windows at the same time
297 /// "opened-window",
298 /// tauri::WebviewUrl::External("about:blank".parse().unwrap()),
299 /// )
300 /// .window_features(features)
301 /// .on_document_title_changed(|window, title| {
302 /// window.set_title(&title).unwrap();
303 /// })
304 /// .title(url.as_str());
305 ///
306 /// let window = builder.build().unwrap();
307 /// tauri::webview::NewWindowResponse::Create { window }
308 /// })
309 /// .build()?;
310 /// Ok(())
311 /// });
312 /// ```
313 ///
314 /// # Platform-specific
315 ///
316 /// - **Android / iOS**: Not supported.
317 /// - **Windows**: The closure is executed on a separate thread to prevent a deadlock.
318 ///
319 /// [window.open]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
320 pub fn on_new_window<
321 F: Fn(Url, NewWindowFeatures) -> NewWindowResponse<R> + Send + Sync + 'static,
322 >(
323 mut self,
324 f: F,
325 ) -> Self {
326 self.webview_builder = self.webview_builder.on_new_window(f);
327 self
328 }
329
330 /// Defines a closure to be executed when the document title changes.
331 ///
332 /// Note that it may run before or after the navigation event.
333 pub fn on_document_title_changed<F: Fn(WebviewWindow<R>, String) + Send + 'static>(
334 mut self,
335 f: F,
336 ) -> Self {
337 self.webview_builder = self
338 .webview_builder
339 .on_document_title_changed(move |webview, url| {
340 f(
341 WebviewWindow {
342 window: webview.window(),
343 webview,
344 },
345 url,
346 )
347 });
348 self
349 }
350
351 /// Set a download event handler to be notified when a download is requested or finished.
352 ///
353 /// Returning `false` prevents the download from happening on a [`DownloadEvent::Requested`] event.
354 ///
355 /// # Examples
356 ///
357 #[cfg_attr(
358 feature = "unstable",
359 doc = r####"
360```rust,no_run
361use tauri::{
362 utils::config::{Csp, CspDirectiveSources, WebviewUrl},
363 webview::{DownloadEvent, WebviewWindowBuilder},
364};
365
366tauri::Builder::default()
367 .setup(|app| {
368 let handle = app.handle();
369 let webview_window = WebviewWindowBuilder::new(handle, "core", WebviewUrl::App("index.html".into()))
370 .on_download(|webview, event| {
371 match event {
372 DownloadEvent::Requested { url, destination } => {
373 println!("downloading {}", url);
374 *destination = "/home/tauri/target/path".into();
375 }
376 DownloadEvent::Finished { url, path, success } => {
377 println!("downloaded {} to {:?}, success: {}", url, path, success);
378 }
379 _ => (),
380 }
381 // let the download start
382 true
383 })
384 .build()?;
385
386 Ok(())
387 });
388```
389 "####
390 )]
391 pub fn on_download<F: Fn(Webview<R>, DownloadEvent<'_>) -> bool + Send + Sync + 'static>(
392 mut self,
393 f: F,
394 ) -> Self {
395 self.webview_builder.download_handler.replace(Arc::new(f));
396 self
397 }
398
399 /// Defines a closure to be executed when a page load event is triggered.
400 /// The event can be either [`tauri_runtime::webview::PageLoadEvent::Started`] if the page has started loading
401 /// or [`tauri_runtime::webview::PageLoadEvent::Finished`] when the page finishes loading.
402 ///
403 /// # Examples
404 /// ```rust,no_run
405 /// use tauri::{
406 /// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
407 /// webview::{PageLoadEvent, WebviewWindowBuilder},
408 /// };
409 /// use http::header::HeaderValue;
410 /// use std::collections::HashMap;
411 /// tauri::Builder::default()
412 /// .setup(|app| {
413 /// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
414 /// .on_page_load(|window, payload| {
415 /// match payload.event() {
416 /// PageLoadEvent::Started => {
417 /// println!("{} finished loading", payload.url());
418 /// }
419 /// PageLoadEvent::Finished => {
420 /// println!("{} finished loading", payload.url());
421 /// }
422 /// }
423 /// })
424 /// .build()?;
425 /// Ok(())
426 /// });
427 /// ```
428 pub fn on_page_load<F: Fn(WebviewWindow<R>, PageLoadPayload<'_>) + Send + Sync + 'static>(
429 mut self,
430 f: F,
431 ) -> Self {
432 self.webview_builder = self.webview_builder.on_page_load(move |webview, payload| {
433 f(
434 WebviewWindow {
435 window: webview.window(),
436 webview,
437 },
438 payload,
439 )
440 });
441 self
442 }
443
444 /// Creates a new window.
445 pub fn build(self) -> crate::Result<WebviewWindow<R>> {
446 let (window, webview) = self.window_builder.with_webview(self.webview_builder)?;
447 Ok(WebviewWindow { window, webview })
448 }
449}
450
451/// Desktop APIs.
452#[cfg(desktop)]
453impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
454 /// Sets the menu for the window.
455 #[must_use]
456 pub fn menu(mut self, menu: crate::menu::Menu<R>) -> Self {
457 self.window_builder = self.window_builder.menu(menu);
458 self
459 }
460
461 /// Show window in the center of the screen.
462 #[must_use]
463 pub fn center(mut self) -> Self {
464 self.window_builder = self.window_builder.center();
465 self
466 }
467
468 /// The initial position of the window's.
469 #[must_use]
470 pub fn position(mut self, x: f64, y: f64) -> Self {
471 self.window_builder = self.window_builder.position(x, y);
472 self
473 }
474
475 /// Window size.
476 #[must_use]
477 pub fn inner_size(mut self, width: f64, height: f64) -> Self {
478 self.window_builder = self.window_builder.inner_size(width, height);
479 self
480 }
481
482 /// Window min inner size.
483 #[must_use]
484 pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
485 self.window_builder = self.window_builder.min_inner_size(min_width, min_height);
486 self
487 }
488
489 /// Window max inner size.
490 #[must_use]
491 pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
492 self.window_builder = self.window_builder.max_inner_size(max_width, max_height);
493 self
494 }
495
496 /// Window inner size constraints.
497 #[must_use]
498 pub fn inner_size_constraints(
499 mut self,
500 constraints: tauri_runtime::window::WindowSizeConstraints,
501 ) -> Self {
502 self.window_builder = self.window_builder.inner_size_constraints(constraints);
503 self
504 }
505
506 /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size)
507 /// on creation, which means the window size will be limited to `monitor size - taskbar size`
508 ///
509 /// **NOTE**: The overflow check is only performed on window creation, resizes can still overflow
510 ///
511 /// ## Platform-specific
512 ///
513 /// - **iOS / Android:** Unsupported.
514 #[must_use]
515 pub fn prevent_overflow(mut self) -> Self {
516 self.window_builder = self.window_builder.prevent_overflow();
517 self
518 }
519
520 /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size)
521 /// on creation with a margin, which means the window size will be limited to `monitor size - taskbar size - margin size`
522 ///
523 /// **NOTE**: The overflow check is only performed on window creation, resizes can still overflow
524 ///
525 /// ## Platform-specific
526 ///
527 /// - **iOS / Android:** Unsupported.
528 #[must_use]
529 pub fn prevent_overflow_with_margin(mut self, margin: impl Into<Size>) -> Self {
530 self.window_builder = self.window_builder.prevent_overflow_with_margin(margin);
531 self
532 }
533
534 /// Whether the window is resizable or not.
535 /// When resizable is set to false, native window's maximize button is automatically disabled.
536 #[must_use]
537 pub fn resizable(mut self, resizable: bool) -> Self {
538 self.window_builder = self.window_builder.resizable(resizable);
539 self
540 }
541
542 /// Whether the window's native maximize button is enabled or not.
543 /// If resizable is set to false, this setting is ignored.
544 ///
545 /// ## Platform-specific
546 ///
547 /// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
548 /// - **Linux / iOS / Android:** Unsupported.
549 #[must_use]
550 pub fn maximizable(mut self, maximizable: bool) -> Self {
551 self.window_builder = self.window_builder.maximizable(maximizable);
552 self
553 }
554
555 /// Whether the window's native minimize button is enabled or not.
556 ///
557 /// ## Platform-specific
558 ///
559 /// - **Linux / iOS / Android:** Unsupported.
560 #[must_use]
561 pub fn minimizable(mut self, minimizable: bool) -> Self {
562 self.window_builder = self.window_builder.minimizable(minimizable);
563 self
564 }
565
566 /// Whether the window's native close button is enabled or not.
567 ///
568 /// ## Platform-specific
569 ///
570 /// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button.
571 /// Depending on the system, this function may not have any effect when called on a window that is already visible"
572 /// - **iOS / Android:** Unsupported.
573 #[must_use]
574 pub fn closable(mut self, closable: bool) -> Self {
575 self.window_builder = self.window_builder.closable(closable);
576 self
577 }
578
579 /// The title of the window in the title bar.
580 #[must_use]
581 pub fn title<S: Into<String>>(mut self, title: S) -> Self {
582 self.window_builder = self.window_builder.title(title);
583 self
584 }
585
586 /// Whether to start the window in fullscreen or not.
587 #[must_use]
588 pub fn fullscreen(mut self, fullscreen: bool) -> Self {
589 self.window_builder = self.window_builder.fullscreen(fullscreen);
590 self
591 }
592
593 /// Sets the window to be initially focused.
594 #[must_use]
595 #[deprecated(
596 since = "1.2.0",
597 note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead."
598 )]
599 pub fn focus(mut self) -> Self {
600 self.window_builder = self.window_builder.focused(true);
601 self.webview_builder = self.webview_builder.focused(true);
602 self
603 }
604
605 /// Whether the window will be focusable or not.
606 #[must_use]
607 pub fn focusable(mut self, focusable: bool) -> Self {
608 self.window_builder = self.window_builder.focusable(focusable);
609 self
610 }
611
612 /// Whether the window will be initially focused or not.
613 #[must_use]
614 pub fn focused(mut self, focused: bool) -> Self {
615 self.window_builder = self.window_builder.focused(focused);
616 self.webview_builder = self.webview_builder.focused(focused);
617 self
618 }
619
620 /// Whether the window should be maximized upon creation.
621 #[must_use]
622 pub fn maximized(mut self, maximized: bool) -> Self {
623 self.window_builder = self.window_builder.maximized(maximized);
624 self
625 }
626
627 /// Whether the window should be immediately visible upon creation.
628 #[must_use]
629 pub fn visible(mut self, visible: bool) -> Self {
630 self.window_builder = self.window_builder.visible(visible);
631 self
632 }
633
634 /// Forces a theme or uses the system settings if None was provided.
635 ///
636 /// ## Platform-specific
637 ///
638 /// - **macOS**: Only supported on macOS 10.14+.
639 #[must_use]
640 pub fn theme(mut self, theme: Option<crate::Theme>) -> Self {
641 self.window_builder = self.window_builder.theme(theme);
642 self
643 }
644
645 /// Whether the window should have borders and bars.
646 #[must_use]
647 pub fn decorations(mut self, decorations: bool) -> Self {
648 self.window_builder = self.window_builder.decorations(decorations);
649 self
650 }
651
652 /// Whether the window should always be below other windows.
653 #[must_use]
654 pub fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
655 self.window_builder = self.window_builder.always_on_bottom(always_on_bottom);
656 self
657 }
658
659 /// Whether the window should always be on top of other windows.
660 #[must_use]
661 pub fn always_on_top(mut self, always_on_top: bool) -> Self {
662 self.window_builder = self.window_builder.always_on_top(always_on_top);
663 self
664 }
665
666 /// Whether the window will be visible on all workspaces or virtual desktops.
667 #[must_use]
668 pub fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
669 self.window_builder = self
670 .window_builder
671 .visible_on_all_workspaces(visible_on_all_workspaces);
672 self
673 }
674
675 /// Prevents the window contents from being captured by other apps.
676 #[must_use]
677 pub fn content_protected(mut self, protected: bool) -> Self {
678 self.window_builder = self.window_builder.content_protected(protected);
679 self
680 }
681
682 /// Sets the window icon.
683 pub fn icon(mut self, icon: Image<'a>) -> crate::Result<Self> {
684 self.window_builder = self.window_builder.icon(icon)?;
685 Ok(self)
686 }
687
688 /// Sets whether or not the window icon should be hidden from the taskbar.
689 ///
690 /// ## Platform-specific
691 ///
692 /// - **macOS**: Unsupported.
693 #[must_use]
694 pub fn skip_taskbar(mut self, skip: bool) -> Self {
695 self.window_builder = self.window_builder.skip_taskbar(skip);
696 self
697 }
698
699 /// Sets custom name for Windows' window class. **Windows only**.
700 #[must_use]
701 pub fn window_classname<S: Into<String>>(mut self, classname: S) -> Self {
702 self.window_builder = self.window_builder.window_classname(classname);
703 self
704 }
705
706 /// Sets whether or not the window has shadow.
707 ///
708 /// ## Platform-specific
709 ///
710 /// - **Windows:**
711 /// - `false` has no effect on decorated window, shadows are always ON.
712 /// - `true` will make undecorated window have a 1px white border,
713 /// and on Windows 11, it will have a rounded corners.
714 /// - **Linux:** Unsupported.
715 #[must_use]
716 pub fn shadow(mut self, enable: bool) -> Self {
717 self.window_builder = self.window_builder.shadow(enable);
718 self
719 }
720
721 /// Sets a parent to the window to be created.
722 ///
723 /// ## Platform-specific
724 ///
725 /// - **Windows**: This sets the passed parent as an owner window to the window to be created.
726 /// From [MSDN owned windows docs](https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows):
727 /// - An owned window is always above its owner in the z-order.
728 /// - The system automatically destroys an owned window when its owner is destroyed.
729 /// - An owned window is hidden when its owner is minimized.
730 /// - **Linux**: This makes the new window transient for parent, see <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
731 /// - **macOS**: This adds the window as a child of parent, see <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
732 pub fn parent(mut self, parent: &WebviewWindow<R>) -> crate::Result<Self> {
733 self.window_builder = self.window_builder.parent(&parent.window)?;
734 Ok(self)
735 }
736
737 /// Set an owner to the window to be created.
738 ///
739 /// From MSDN:
740 /// - An owned window is always above its owner in the z-order.
741 /// - The system automatically destroys an owned window when its owner is destroyed.
742 /// - An owned window is hidden when its owner is minimized.
743 ///
744 /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
745 #[cfg(windows)]
746 pub fn owner(mut self, owner: &WebviewWindow<R>) -> crate::Result<Self> {
747 self.window_builder = self.window_builder.owner(&owner.window)?;
748 Ok(self)
749 }
750
751 /// Set an owner to the window to be created.
752 ///
753 /// From MSDN:
754 /// - An owned window is always above its owner in the z-order.
755 /// - The system automatically destroys an owned window when its owner is destroyed.
756 /// - An owned window is hidden when its owner is minimized.
757 ///
758 /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
759 #[cfg(windows)]
760 #[must_use]
761 pub fn owner_raw(mut self, owner: HWND) -> Self {
762 self.window_builder = self.window_builder.owner_raw(owner);
763 self
764 }
765
766 /// Sets a parent to the window to be created.
767 ///
768 /// A child window has the WS_CHILD style and is confined to the client area of its parent window.
769 ///
770 /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
771 #[cfg(windows)]
772 #[must_use]
773 pub fn parent_raw(mut self, parent: HWND) -> Self {
774 self.window_builder = self.window_builder.parent_raw(parent);
775 self
776 }
777
778 /// Sets a parent to the window to be created.
779 ///
780 /// See <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
781 #[cfg(target_os = "macos")]
782 #[must_use]
783 pub fn parent_raw(mut self, parent: *mut std::ffi::c_void) -> Self {
784 self.window_builder = self.window_builder.parent_raw(parent);
785 self
786 }
787
788 /// Sets the window to be created transient for parent.
789 ///
790 /// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
791 #[cfg(any(
792 target_os = "linux",
793 target_os = "dragonfly",
794 target_os = "freebsd",
795 target_os = "netbsd",
796 target_os = "openbsd"
797 ))]
798 pub fn transient_for(mut self, parent: &WebviewWindow<R>) -> crate::Result<Self> {
799 self.window_builder = self.window_builder.transient_for(&parent.window)?;
800 Ok(self)
801 }
802
803 /// Sets the window to be created transient for parent.
804 ///
805 /// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
806 #[cfg(any(
807 target_os = "linux",
808 target_os = "dragonfly",
809 target_os = "freebsd",
810 target_os = "netbsd",
811 target_os = "openbsd"
812 ))]
813 #[must_use]
814 pub fn transient_for_raw(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
815 self.window_builder = self.window_builder.transient_for_raw(parent);
816 self
817 }
818
819 /// Enables or disables drag and drop support.
820 #[cfg(windows)]
821 #[must_use]
822 pub fn drag_and_drop(mut self, enabled: bool) -> Self {
823 self.window_builder = self.window_builder.drag_and_drop(enabled);
824 self
825 }
826
827 /// Sets the [`crate::TitleBarStyle`].
828 #[cfg(target_os = "macos")]
829 #[must_use]
830 pub fn title_bar_style(mut self, style: crate::TitleBarStyle) -> Self {
831 self.window_builder = self.window_builder.title_bar_style(style);
832 self
833 }
834
835 /// Change the position of the window controls on macOS.
836 ///
837 /// Requires titleBarStyle: Overlay and decorations: true.
838 #[cfg(target_os = "macos")]
839 #[must_use]
840 pub fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
841 self.webview_builder.webview_attributes = self
842 .webview_builder
843 .webview_attributes
844 .traffic_light_position(position.into());
845 self
846 }
847
848 /// Whether to show a link preview when long pressing on links. Available on macOS and iOS only.
849 ///
850 /// Default is true.
851 ///
852 /// See https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview
853 ///
854 /// ## Platform-specific
855 ///
856 /// - **Linux / Windows / Android:** Unsupported.
857 #[cfg(target_os = "macos")]
858 #[must_use]
859 pub fn allow_link_preview(mut self, allow_link_preview: bool) -> Self {
860 self.webview_builder = self.webview_builder.allow_link_preview(allow_link_preview);
861 self
862 }
863
864 /// Hide the window title.
865 #[cfg(target_os = "macos")]
866 #[must_use]
867 pub fn hidden_title(mut self, hidden: bool) -> Self {
868 self.window_builder = self.window_builder.hidden_title(hidden);
869 self
870 }
871
872 /// Defines the window [tabbing identifier] for macOS.
873 ///
874 /// Windows with matching tabbing identifiers will be grouped together.
875 /// If the tabbing identifier is not set, automatic tabbing will be disabled.
876 ///
877 /// [tabbing identifier]: <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
878 #[cfg(target_os = "macos")]
879 #[must_use]
880 pub fn tabbing_identifier(mut self, identifier: &str) -> Self {
881 self.window_builder = self.window_builder.tabbing_identifier(identifier);
882 self
883 }
884
885 /// Sets window effects.
886 ///
887 /// Requires the window to be transparent.
888 ///
889 /// ## Platform-specific:
890 ///
891 /// - **Windows**: If using decorations or shadows, you may want to try this workaround <https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891>
892 /// - **Linux**: Unsupported
893 pub fn effects(mut self, effects: crate::utils::config::WindowEffectsConfig) -> Self {
894 self.window_builder = self.window_builder.effects(effects);
895 self
896 }
897}
898
899/// Webview attributes.
900impl<R: Runtime, M: Manager<R>> WebviewWindowBuilder<'_, R, M> {
901 /// Sets whether clicking an inactive window also clicks through to the webview.
902 #[must_use]
903 pub fn accept_first_mouse(mut self, accept: bool) -> Self {
904 self.webview_builder = self.webview_builder.accept_first_mouse(accept);
905 self
906 }
907
908 /// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
909 /// but before the HTML document has been parsed and before any other script included by the HTML document is run.
910 ///
911 /// Since it runs on all top-level document navigations,
912 /// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
913 ///
914 /// This is executed only on the main frame.
915 /// If you only want to run it in all frames, use [Self::initialization_script_for_all_frames] instead.
916 ///
917 /// ## Platform-specific
918 ///
919 /// - **Windows:** scripts are always added to subframes.
920 /// - **Android:** When [addDocumentStartJavaScript] is not supported,
921 /// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
922 /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
923 ///
924 /// # Examples
925 ///
926 /// ```rust
927 /// const INIT_SCRIPT: &str = r#"
928 /// if (window.location.origin === 'https://tauri.app') {
929 /// console.log("hello world from js init script");
930 ///
931 /// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
932 /// }
933 /// "#;
934 ///
935 /// fn main() {
936 /// tauri::Builder::default()
937 /// .setup(|app| {
938 /// let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
939 /// .initialization_script(INIT_SCRIPT)
940 /// .build()?;
941 /// Ok(())
942 /// });
943 /// }
944 /// ```
945 #[must_use]
946 pub fn initialization_script(mut self, script: impl Into<String>) -> Self {
947 self.webview_builder = self.webview_builder.initialization_script(script);
948 self
949 }
950
951 /// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
952 /// but before the HTML document has been parsed and before any other script included by the HTML document is run.
953 ///
954 /// Since it runs on all top-level document navigations and also child frame page navigations,
955 /// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
956 ///
957 /// This is executed on all frames (main frame and also sub frames).
958 /// If you only want to run the script in the main frame, use [Self::initialization_script] instead.
959 ///
960 /// ## Platform-specific
961 ///
962 /// - **Android:** When [addDocumentStartJavaScript] is not supported,
963 /// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
964 /// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
965 ///
966 /// # Examples
967 ///
968 /// ```rust
969 /// const INIT_SCRIPT: &str = r#"
970 /// if (window.location.origin === 'https://tauri.app') {
971 /// console.log("hello world from js init script");
972 ///
973 /// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
974 /// }
975 /// "#;
976 ///
977 /// fn main() {
978 /// tauri::Builder::default()
979 /// .setup(|app| {
980 /// let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
981 /// .initialization_script_for_all_frames(INIT_SCRIPT)
982 /// .build()?;
983 /// Ok(())
984 /// });
985 /// }
986 /// ```
987 #[must_use]
988 pub fn initialization_script_for_all_frames(mut self, script: impl Into<String>) -> Self {
989 self.webview_builder = self
990 .webview_builder
991 .initialization_script_for_all_frames(script);
992 self
993 }
994
995 /// Set the user agent for the webview
996 #[must_use]
997 pub fn user_agent(mut self, user_agent: &str) -> Self {
998 self.webview_builder = self.webview_builder.user_agent(user_agent);
999 self
1000 }
1001
1002 /// Set additional arguments for the webview.
1003 ///
1004 /// ## Platform-specific
1005 ///
1006 /// - **macOS / Linux / Android / iOS**: Unsupported.
1007 ///
1008 /// ## Warning
1009 ///
1010 /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
1011 /// so if you use this method, you also need to disable these components by yourself if you want.
1012 #[must_use]
1013 pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
1014 self.webview_builder = self
1015 .webview_builder
1016 .additional_browser_args(additional_args);
1017 self
1018 }
1019
1020 /// Data directory for the webview.
1021 #[must_use]
1022 pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
1023 self.webview_builder = self.webview_builder.data_directory(data_directory);
1024 self
1025 }
1026
1027 /// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
1028 #[must_use]
1029 pub fn disable_drag_drop_handler(mut self) -> Self {
1030 self.webview_builder = self.webview_builder.disable_drag_drop_handler();
1031 self
1032 }
1033
1034 /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
1035 ///
1036 /// **macOS** doesn't provide such method and is always enabled by default,
1037 /// but you still need to add menu item accelerators to use shortcuts.
1038 #[must_use]
1039 pub fn enable_clipboard_access(mut self) -> Self {
1040 self.webview_builder = self.webview_builder.enable_clipboard_access();
1041 self
1042 }
1043
1044 /// Enable or disable incognito mode for the WebView..
1045 ///
1046 /// ## Platform-specific:
1047 ///
1048 /// **Android**: Unsupported.
1049 #[must_use]
1050 pub fn incognito(mut self, incognito: bool) -> Self {
1051 self.webview_builder = self.webview_builder.incognito(incognito);
1052 self
1053 }
1054
1055 /// Sets the webview to automatically grow and shrink its size and position when the parent window resizes.
1056 #[must_use]
1057 pub fn auto_resize(mut self) -> Self {
1058 self.webview_builder = self.webview_builder.auto_resize();
1059 self
1060 }
1061
1062 /// Set a proxy URL for the WebView for all network requests.
1063 ///
1064 /// Must be either a `http://` or a `socks5://` URL.
1065 #[must_use]
1066 pub fn proxy_url(mut self, url: Url) -> Self {
1067 self.webview_builder = self.webview_builder.proxy_url(url);
1068 self
1069 }
1070
1071 /// Whether the window should be transparent. If this is true, writing colors
1072 /// with alpha values different than `1.0` will produce a transparent window.
1073 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
1074 #[cfg_attr(
1075 docsrs,
1076 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
1077 )]
1078 #[must_use]
1079 pub fn transparent(mut self, transparent: bool) -> Self {
1080 #[cfg(desktop)]
1081 {
1082 self.window_builder = self.window_builder.transparent(transparent);
1083 }
1084 self.webview_builder = self.webview_builder.transparent(transparent);
1085 self
1086 }
1087
1088 /// Whether page zooming by hotkeys and mousewheel should be enabled or not.
1089 ///
1090 /// ## Platform-specific:
1091 ///
1092 /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting.
1093 /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `Ctrl/Cmd + [- = +]` hotkeys or mousewheel events,
1094 /// 20% in each step, ranging from 20% to 1000%. Requires `core:webview:allow-set-webview-zoom` permission
1095 ///
1096 /// - **Android / iOS**: Unsupported.
1097 #[must_use]
1098 pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self {
1099 self.webview_builder = self.webview_builder.zoom_hotkeys_enabled(enabled);
1100 self
1101 }
1102
1103 /// Whether browser extensions can be installed for the webview process
1104 ///
1105 /// ## Platform-specific:
1106 ///
1107 /// - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)
1108 /// - **MacOS / Linux / iOS / Android** - Unsupported.
1109 #[must_use]
1110 pub fn browser_extensions_enabled(mut self, enabled: bool) -> Self {
1111 self.webview_builder = self.webview_builder.browser_extensions_enabled(enabled);
1112 self
1113 }
1114
1115 /// Set the path from which to load extensions from. Extensions stored in this path should be unpacked Chrome extensions on Windows, and compiled `.so` extensions on Linux.
1116 ///
1117 /// ## Platform-specific:
1118 ///
1119 /// - **Windows**: Browser extensions must first be enabled. See [`browser_extensions_enabled`](Self::browser_extensions_enabled)
1120 /// - **MacOS / iOS / Android** - Unsupported.
1121 #[must_use]
1122 pub fn extensions_path(mut self, path: impl AsRef<Path>) -> Self {
1123 self.webview_builder = self.webview_builder.extensions_path(path);
1124 self
1125 }
1126
1127 /// Initialize the WebView with a custom data store identifier.
1128 /// Can be used as a replacement for data_directory not being available in WKWebView.
1129 ///
1130 /// - **macOS / iOS**: Available on macOS >= 14 and iOS >= 17
1131 /// - **Windows / Linux / Android**: Unsupported.
1132 #[must_use]
1133 pub fn data_store_identifier(mut self, data_store_identifier: [u8; 16]) -> Self {
1134 self.webview_builder = self
1135 .webview_builder
1136 .data_store_identifier(data_store_identifier);
1137 self
1138 }
1139
1140 /// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
1141 ///
1142 /// ## Note
1143 ///
1144 /// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
1145 ///
1146 /// ## Warning
1147 ///
1148 /// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.
1149 #[must_use]
1150 pub fn use_https_scheme(mut self, enabled: bool) -> Self {
1151 self.webview_builder = self.webview_builder.use_https_scheme(enabled);
1152 self
1153 }
1154
1155 /// Whether web inspector, which is usually called browser devtools, is enabled or not. Enabled by default.
1156 ///
1157 /// This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds.
1158 ///
1159 /// ## Platform-specific
1160 ///
1161 /// - macOS: This will call private functions on **macOS**.
1162 /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
1163 /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
1164 #[must_use]
1165 pub fn devtools(mut self, enabled: bool) -> Self {
1166 self.webview_builder = self.webview_builder.devtools(enabled);
1167 self
1168 }
1169
1170 /// Set the window and webview background color.
1171 ///
1172 /// ## Platform-specific:
1173 ///
1174 /// - **Android / iOS:** Unsupported for the window layer.
1175 /// - **macOS / iOS**: Not implemented for the webview layer.
1176 /// - **Windows**:
1177 /// - alpha channel is ignored for the window layer.
1178 /// - On Windows 7, alpha channel is ignored for the webview layer.
1179 /// - On Windows 8 and newer, if alpha channel is not `0`, it will be ignored.
1180 #[must_use]
1181 pub fn background_color(mut self, color: Color) -> Self {
1182 self.window_builder = self.window_builder.background_color(color);
1183 self.webview_builder = self.webview_builder.background_color(color);
1184 self
1185 }
1186
1187 /// Change the default background throttling behaviour.
1188 ///
1189 /// By default, browsers use a suspend policy that will throttle timers and even unload
1190 /// the whole tab (view) to free resources after roughly 5 minutes when a view became
1191 /// minimized or hidden. This will pause all tasks until the documents visibility state
1192 /// changes back from hidden to visible by bringing the view back to the foreground.
1193 ///
1194 /// ## Platform-specific
1195 ///
1196 /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
1197 /// - **iOS**: Supported since version 17.0+.
1198 /// - **macOS**: Supported since version 14.0+.
1199 ///
1200 /// see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578
1201 #[must_use]
1202 pub fn background_throttling(mut self, policy: BackgroundThrottlingPolicy) -> Self {
1203 self.webview_builder = self.webview_builder.background_throttling(policy);
1204 self
1205 }
1206
1207 /// Whether JavaScript should be disabled.
1208 #[must_use]
1209 pub fn disable_javascript(mut self) -> Self {
1210 self.webview_builder = self.webview_builder.disable_javascript();
1211 self
1212 }
1213
1214 /// Allows overriding the the keyboard accessory view on iOS.
1215 /// Returning `None` effectively removes the view.
1216 ///
1217 /// The closure parameter is the webview instance.
1218 ///
1219 /// The accessory view is the view that appears above the keyboard when a text input element is focused.
1220 /// It usually displays a view with "Done", "Next" buttons.
1221 ///
1222 /// # Examples
1223 ///
1224 /// ```
1225 /// fn main() {
1226 /// tauri::Builder::default()
1227 /// .setup(|app| {
1228 /// let mut builder = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()));
1229 /// #[cfg(target_os = "ios")]
1230 /// {
1231 /// window_builder = window_builder.with_input_accessory_view_builder(|_webview| unsafe {
1232 /// let mtm = objc2_foundation::MainThreadMarker::new_unchecked();
1233 /// let button = objc2_ui_kit::UIButton::buttonWithType(objc2_ui_kit::UIButtonType(1), mtm);
1234 /// button.setTitle_forState(
1235 /// Some(&objc2_foundation::NSString::from_str("Tauri")),
1236 /// objc2_ui_kit::UIControlState(0),
1237 /// );
1238 /// Some(button.downcast().unwrap())
1239 /// });
1240 /// }
1241 /// let webview = builder.build()?;
1242 /// Ok(())
1243 /// });
1244 /// }
1245 /// ```
1246 ///
1247 /// # Stability
1248 ///
1249 /// This relies on [`objc2_ui_kit`] which does not provide a stable API yet, so it can receive breaking changes in minor releases.
1250 #[cfg(target_os = "ios")]
1251 pub fn with_input_accessory_view_builder<
1252 F: Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>
1253 + Send
1254 + Sync
1255 + 'static,
1256 >(
1257 mut self,
1258 builder: F,
1259 ) -> Self {
1260 self.webview_builder = self
1261 .webview_builder
1262 .with_input_accessory_view_builder(builder);
1263 self
1264 }
1265
1266 /// Set the environment for the webview.
1267 /// Useful if you need to share the same environment, for instance when using the [`Self::on_new_window`].
1268 #[cfg(all(feature = "wry", windows))]
1269 pub fn with_environment(
1270 mut self,
1271 environment: webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Environment,
1272 ) -> Self {
1273 self.webview_builder = self.webview_builder.with_environment(environment);
1274 self
1275 }
1276
1277 /// Creates a new webview sharing the same web process with the provided webview.
1278 /// Useful if you need to link a webview to another, for instance when using the [`Self::on_new_window`].
1279 #[cfg(all(
1280 feature = "wry",
1281 any(
1282 target_os = "linux",
1283 target_os = "dragonfly",
1284 target_os = "freebsd",
1285 target_os = "netbsd",
1286 target_os = "openbsd",
1287 )
1288 ))]
1289 pub fn with_related_view(mut self, related_view: webkit2gtk::WebView) -> Self {
1290 self.webview_builder = self.webview_builder.with_related_view(related_view);
1291 self
1292 }
1293
1294 /// Set the webview configuration.
1295 /// Useful if you need to share the same webview configuration, for instance when using the [`Self::on_new_window`].
1296 #[cfg(target_os = "macos")]
1297 pub fn with_webview_configuration(
1298 mut self,
1299 webview_configuration: objc2::rc::Retained<objc2_web_kit::WKWebViewConfiguration>,
1300 ) -> Self {
1301 self.webview_builder = self
1302 .webview_builder
1303 .with_webview_configuration(webview_configuration);
1304 self
1305 }
1306
1307 /// Set the window features.
1308 /// Useful if you need to share the same window features, for instance when using the [`Self::on_new_window`].
1309 #[cfg(any(
1310 target_os = "macos",
1311 windows,
1312 target_os = "linux",
1313 target_os = "dragonfly",
1314 target_os = "freebsd",
1315 target_os = "netbsd",
1316 target_os = "openbsd"
1317 ))]
1318 pub fn window_features(mut self, features: NewWindowFeatures) -> Self {
1319 if let Some(position) = features.position() {
1320 self.window_builder = self.window_builder.position(position.x, position.y);
1321 }
1322
1323 if let Some(size) = features.size() {
1324 self.window_builder = self.window_builder.inner_size(size.width, size.height);
1325 }
1326
1327 #[cfg(target_os = "macos")]
1328 {
1329 self.webview_builder = self
1330 .webview_builder
1331 .with_webview_configuration(features.opener().target_configuration.clone());
1332 }
1333
1334 #[cfg(all(feature = "wry", windows))]
1335 {
1336 self.webview_builder = self
1337 .webview_builder
1338 .with_environment(features.opener().environment.clone());
1339 }
1340
1341 #[cfg(all(
1342 feature = "wry",
1343 any(
1344 target_os = "linux",
1345 target_os = "dragonfly",
1346 target_os = "freebsd",
1347 target_os = "netbsd",
1348 target_os = "openbsd",
1349 )
1350 ))]
1351 {
1352 self.webview_builder = self
1353 .webview_builder
1354 .with_related_view(features.opener().webview.clone());
1355 }
1356 self
1357 }
1358}
1359
1360/// A type that wraps a [`Window`] together with a [`Webview`].
1361#[default_runtime(crate::Wry, wry)]
1362#[derive(Debug)]
1363pub struct WebviewWindow<R: Runtime> {
1364 pub(crate) window: Window<R>,
1365 pub(crate) webview: Webview<R>,
1366}
1367
1368impl<R: Runtime> AsRef<Webview<R>> for WebviewWindow<R> {
1369 fn as_ref(&self) -> &Webview<R> {
1370 &self.webview
1371 }
1372}
1373
1374impl<R: Runtime> Clone for WebviewWindow<R> {
1375 fn clone(&self) -> Self {
1376 Self {
1377 window: self.window.clone(),
1378 webview: self.webview.clone(),
1379 }
1380 }
1381}
1382
1383impl<R: Runtime> Eq for WebviewWindow<R> {}
1384impl<R: Runtime> PartialEq for WebviewWindow<R> {
1385 /// Only use the [`Webview`]'s label to compare equality.
1386 fn eq(&self, other: &Self) -> bool {
1387 self.webview.eq(&other.webview)
1388 }
1389}
1390
1391impl<R: Runtime> raw_window_handle::HasWindowHandle for WebviewWindow<R> {
1392 fn window_handle(
1393 &self,
1394 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
1395 Ok(unsafe {
1396 raw_window_handle::WindowHandle::borrow_raw(self.window.window_handle()?.as_raw())
1397 })
1398 }
1399}
1400
1401impl<R: Runtime> raw_window_handle::HasDisplayHandle for WebviewWindow<R> {
1402 fn display_handle(
1403 &self,
1404 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
1405 self.webview.app_handle.display_handle()
1406 }
1407}
1408
1409impl<'de, R: Runtime> CommandArg<'de, R> for WebviewWindow<R> {
1410 /// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
1411 fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
1412 let webview = command.message.webview();
1413 let window = webview.window();
1414 if window.is_webview_window() {
1415 return Ok(Self { window, webview });
1416 }
1417
1418 Err(InvokeError::from("current webview is not a WebviewWindow"))
1419 }
1420}
1421
1422/// Base webview window functions.
1423impl<R: Runtime> WebviewWindow<R> {
1424 /// Initializes a [`WebviewWindowBuilder`] with the given window label and webview URL.
1425 ///
1426 /// Data URLs are only supported with the `webview-data-url` feature flag.
1427 pub fn builder<M: Manager<R>, L: Into<String>>(
1428 manager: &M,
1429 label: L,
1430 url: WebviewUrl,
1431 ) -> WebviewWindowBuilder<'_, R, M> {
1432 WebviewWindowBuilder::new(manager, label, url)
1433 }
1434
1435 /// Runs the given closure on the main thread.
1436 pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
1437 self.webview.run_on_main_thread(f)
1438 }
1439
1440 /// The webview label.
1441 pub fn label(&self) -> &str {
1442 self.webview.label()
1443 }
1444
1445 /// Registers a window event listener.
1446 pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
1447 self.window.on_window_event(f);
1448 }
1449
1450 /// Registers a webview event listener.
1451 pub fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) {
1452 self.webview.on_webview_event(f);
1453 }
1454
1455 /// Resolves the given command scope for this webview on the currently loaded URL.
1456 ///
1457 /// If the command is not allowed, returns None.
1458 ///
1459 /// If the scope cannot be deserialized to the given type, an error is returned.
1460 ///
1461 /// In a command context this can be directly resolved from the command arguments via [crate::ipc::CommandScope]:
1462 ///
1463 /// ```
1464 /// use tauri::ipc::CommandScope;
1465 ///
1466 /// #[derive(Debug, serde::Deserialize)]
1467 /// struct ScopeType {
1468 /// some_value: String,
1469 /// }
1470 /// #[tauri::command]
1471 /// fn my_command(scope: CommandScope<ScopeType>) {
1472 /// // check scope
1473 /// }
1474 /// ```
1475 ///
1476 /// # Examples
1477 ///
1478 /// ```
1479 /// use tauri::Manager;
1480 ///
1481 /// #[derive(Debug, serde::Deserialize)]
1482 /// struct ScopeType {
1483 /// some_value: String,
1484 /// }
1485 ///
1486 /// tauri::Builder::default()
1487 /// .setup(|app| {
1488 /// let webview = app.get_webview_window("main").unwrap();
1489 /// let scope = webview.resolve_command_scope::<ScopeType>("my-plugin", "read");
1490 /// Ok(())
1491 /// });
1492 /// ```
1493 pub fn resolve_command_scope<T: ScopeObject>(
1494 &self,
1495 plugin: &str,
1496 command: &str,
1497 ) -> crate::Result<Option<ResolvedScope<T>>> {
1498 self.webview.resolve_command_scope(plugin, command)
1499 }
1500}
1501
1502/// Menu APIs
1503#[cfg(desktop)]
1504impl<R: Runtime> WebviewWindow<R> {
1505 /// Registers a global menu event listener.
1506 ///
1507 /// Note that this handler is called for any menu event,
1508 /// whether it is coming from this window, another window or from the tray icon menu.
1509 ///
1510 /// Also note that this handler will not be called if
1511 /// the window used to register it was closed.
1512 ///
1513 /// # Examples
1514 ///
1515 /// ```
1516 /// use tauri::menu::{Menu, Submenu, MenuItem};
1517 /// use tauri::{WebviewWindowBuilder, WebviewUrl};
1518 ///
1519 /// tauri::Builder::default()
1520 /// .setup(|app| {
1521 /// let handle = app.handle();
1522 /// let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
1523 /// let menu = Menu::with_items(handle, &[
1524 /// &Submenu::with_items(handle, "File", true, &[
1525 /// &save_menu_item,
1526 /// ])?,
1527 /// ])?;
1528 /// let webview_window = WebviewWindowBuilder::new(app, "editor", WebviewUrl::default())
1529 /// .menu(menu)
1530 /// .build()
1531 /// .unwrap();
1532 ///
1533 /// webview_window.on_menu_event(move |window, event| {
1534 /// if event.id == save_menu_item.id() {
1535 /// // save menu item
1536 /// }
1537 /// });
1538 ///
1539 /// Ok(())
1540 /// });
1541 /// ```
1542 pub fn on_menu_event<F: Fn(&crate::Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
1543 &self,
1544 f: F,
1545 ) {
1546 self.window.on_menu_event(f)
1547 }
1548
1549 /// Returns this window menu.
1550 pub fn menu(&self) -> Option<Menu<R>> {
1551 self.window.menu()
1552 }
1553
1554 /// Sets the window menu and returns the previous one.
1555 ///
1556 /// ## Platform-specific:
1557 ///
1558 /// - **macOS:** Unsupported. The menu on macOS is app-wide and not specific to one
1559 /// window, if you need to set it, use [`AppHandle::set_menu`] instead.
1560 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1561 pub fn set_menu(&self, menu: Menu<R>) -> crate::Result<Option<Menu<R>>> {
1562 self.window.set_menu(menu)
1563 }
1564
1565 /// Removes the window menu and returns it.
1566 ///
1567 /// ## Platform-specific:
1568 ///
1569 /// - **macOS:** Unsupported. The menu on macOS is app-wide and not specific to one
1570 /// window, if you need to remove it, use [`AppHandle::remove_menu`] instead.
1571 pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
1572 self.window.remove_menu()
1573 }
1574
1575 /// Hides the window menu.
1576 pub fn hide_menu(&self) -> crate::Result<()> {
1577 self.window.hide_menu()
1578 }
1579
1580 /// Shows the window menu.
1581 pub fn show_menu(&self) -> crate::Result<()> {
1582 self.window.show_menu()
1583 }
1584
1585 /// Shows the window menu.
1586 pub fn is_menu_visible(&self) -> crate::Result<bool> {
1587 self.window.is_menu_visible()
1588 }
1589
1590 /// Shows the specified menu as a context menu at the cursor position.
1591 pub fn popup_menu<M: ContextMenu>(&self, menu: &M) -> crate::Result<()> {
1592 self.window.popup_menu(menu)
1593 }
1594
1595 /// Shows the specified menu as a context menu at the specified position.
1596 ///
1597 /// The position is relative to the window's top-left corner.
1598 pub fn popup_menu_at<M: ContextMenu, P: Into<Position>>(
1599 &self,
1600 menu: &M,
1601 position: P,
1602 ) -> crate::Result<()> {
1603 self.window.popup_menu_at(menu, position)
1604 }
1605}
1606
1607/// Window getters.
1608impl<R: Runtime> WebviewWindow<R> {
1609 /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
1610 pub fn scale_factor(&self) -> crate::Result<f64> {
1611 self.window.scale_factor()
1612 }
1613
1614 /// 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.
1615 pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1616 self.window.inner_position()
1617 }
1618
1619 /// Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
1620 pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1621 self.window.outer_position()
1622 }
1623
1624 /// Returns the physical size of the window's client area.
1625 ///
1626 /// The client area is the content of the window, excluding the title bar and borders.
1627 pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
1628 self.window.inner_size()
1629 }
1630
1631 /// Returns the physical size of the entire window.
1632 ///
1633 /// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
1634 pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
1635 self.window.outer_size()
1636 }
1637
1638 /// Gets the window's current fullscreen state.
1639 pub fn is_fullscreen(&self) -> crate::Result<bool> {
1640 self.window.is_fullscreen()
1641 }
1642
1643 /// Gets the window's current minimized state.
1644 pub fn is_minimized(&self) -> crate::Result<bool> {
1645 self.window.is_minimized()
1646 }
1647
1648 /// Gets the window's current maximized state.
1649 pub fn is_maximized(&self) -> crate::Result<bool> {
1650 self.window.is_maximized()
1651 }
1652
1653 /// Gets the window's current focus state.
1654 pub fn is_focused(&self) -> crate::Result<bool> {
1655 self.window.is_focused()
1656 }
1657
1658 /// Gets the window's current decoration state.
1659 pub fn is_decorated(&self) -> crate::Result<bool> {
1660 self.window.is_decorated()
1661 }
1662
1663 /// Gets the window's current resizable state.
1664 pub fn is_resizable(&self) -> crate::Result<bool> {
1665 self.window.is_resizable()
1666 }
1667
1668 /// Whether the window is enabled or disabled.
1669 pub fn is_enabled(&self) -> crate::Result<bool> {
1670 self.webview.window().is_enabled()
1671 }
1672
1673 /// Determines if this window should always be on top of other windows.
1674 ///
1675 /// ## Platform-specific
1676 ///
1677 /// - **iOS / Android:** Unsupported.
1678 pub fn is_always_on_top(&self) -> crate::Result<bool> {
1679 self.webview.window().is_always_on_top()
1680 }
1681
1682 /// Gets the window's native maximize button state
1683 ///
1684 /// ## Platform-specific
1685 ///
1686 /// - **Linux / iOS / Android:** Unsupported.
1687 pub fn is_maximizable(&self) -> crate::Result<bool> {
1688 self.window.is_maximizable()
1689 }
1690
1691 /// Gets the window's native minimize button state
1692 ///
1693 /// ## Platform-specific
1694 ///
1695 /// - **Linux / iOS / Android:** Unsupported.
1696 pub fn is_minimizable(&self) -> crate::Result<bool> {
1697 self.window.is_minimizable()
1698 }
1699
1700 /// Gets the window's native close button state
1701 ///
1702 /// ## Platform-specific
1703 ///
1704 /// - **Linux / iOS / Android:** Unsupported.
1705 pub fn is_closable(&self) -> crate::Result<bool> {
1706 self.window.is_closable()
1707 }
1708
1709 /// Gets the window's current visibility state.
1710 pub fn is_visible(&self) -> crate::Result<bool> {
1711 self.window.is_visible()
1712 }
1713
1714 /// Gets the window's current title.
1715 pub fn title(&self) -> crate::Result<String> {
1716 self.window.title()
1717 }
1718
1719 /// Returns the monitor on which the window currently resides.
1720 ///
1721 /// Returns None if current monitor can't be detected.
1722 pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
1723 self.window.current_monitor()
1724 }
1725
1726 /// Returns the primary monitor of the system.
1727 ///
1728 /// Returns None if it can't identify any monitor as a primary one.
1729 pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
1730 self.window.primary_monitor()
1731 }
1732
1733 /// Returns the monitor that contains the given point.
1734 pub fn monitor_from_point(&self, x: f64, y: f64) -> crate::Result<Option<Monitor>> {
1735 self.window.monitor_from_point(x, y)
1736 }
1737
1738 /// Returns the list of all the monitors available on the system.
1739 pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
1740 self.window.available_monitors()
1741 }
1742
1743 /// Returns the native handle that is used by this window.
1744 #[cfg(target_os = "macos")]
1745 pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
1746 self.window.ns_window()
1747 }
1748
1749 /// Returns the pointer to the content view of this window.
1750 #[cfg(target_os = "macos")]
1751 pub fn ns_view(&self) -> crate::Result<*mut std::ffi::c_void> {
1752 self.window.ns_view()
1753 }
1754
1755 /// Returns the native handle that is used by this window.
1756 #[cfg(windows)]
1757 pub fn hwnd(&self) -> crate::Result<HWND> {
1758 self.window.hwnd()
1759 }
1760
1761 /// Returns the `ApplicationWindow` from gtk crate that is used by this window.
1762 ///
1763 /// Note that this type can only be used on the main thread.
1764 #[cfg(any(
1765 target_os = "linux",
1766 target_os = "dragonfly",
1767 target_os = "freebsd",
1768 target_os = "netbsd",
1769 target_os = "openbsd"
1770 ))]
1771 pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
1772 self.window.gtk_window()
1773 }
1774
1775 /// Returns the vertical [`gtk::Box`] that is added by default as the sole child of this window.
1776 ///
1777 /// Note that this type can only be used on the main thread.
1778 #[cfg(any(
1779 target_os = "linux",
1780 target_os = "dragonfly",
1781 target_os = "freebsd",
1782 target_os = "netbsd",
1783 target_os = "openbsd"
1784 ))]
1785 pub fn default_vbox(&self) -> crate::Result<gtk::Box> {
1786 self.window.default_vbox()
1787 }
1788
1789 /// Returns the current window theme.
1790 ///
1791 /// ## Platform-specific
1792 ///
1793 /// - **macOS**: Only supported on macOS 10.14+.
1794 pub fn theme(&self) -> crate::Result<crate::Theme> {
1795 self.window.theme()
1796 }
1797}
1798
1799/// Desktop window getters.
1800#[cfg(desktop)]
1801impl<R: Runtime> WebviewWindow<R> {
1802 /// Get the cursor position relative to the top-left hand corner of the desktop.
1803 ///
1804 /// Note that the top-left hand corner of the desktop is not necessarily the same as the screen.
1805 /// If the user uses a desktop with multiple monitors,
1806 /// the top-left hand corner of the desktop is the top-left hand corner of the main monitor on Windows and macOS
1807 /// or the top-left of the leftmost monitor on X11.
1808 ///
1809 /// The coordinates can be negative if the top-left hand corner of the window is outside of the visible screen region.
1810 pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
1811 self.webview.cursor_position()
1812 }
1813}
1814
1815/// Desktop window setters and actions.
1816#[cfg(desktop)]
1817impl<R: Runtime> WebviewWindow<R> {
1818 /// Centers the window.
1819 pub fn center(&self) -> crate::Result<()> {
1820 self.window.center()
1821 }
1822
1823 /// Requests user attention to the window, this has no effect if the application
1824 /// is already focused. How requesting for user attention manifests is platform dependent,
1825 /// see `UserAttentionType` for details.
1826 ///
1827 /// Providing `None` will unset the request for user attention. Unsetting the request for
1828 /// user attention might not be done automatically by the WM when the window receives input.
1829 ///
1830 /// ## Platform-specific
1831 ///
1832 /// - **macOS:** `None` has no effect.
1833 /// - **Linux:** Urgency levels have the same effect.
1834 pub fn request_user_attention(
1835 &self,
1836 request_type: Option<UserAttentionType>,
1837 ) -> crate::Result<()> {
1838 self.window.request_user_attention(request_type)
1839 }
1840
1841 /// Determines if this window should be resizable.
1842 /// When resizable is set to false, native window's maximize button is automatically disabled.
1843 pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
1844 self.window.set_resizable(resizable)
1845 }
1846
1847 /// Enable or disable the window.
1848 pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
1849 self.webview.window().set_enabled(enabled)
1850 }
1851
1852 /// Determines if this window's native maximize button should be enabled.
1853 /// If resizable is set to false, this setting is ignored.
1854 ///
1855 /// ## Platform-specific
1856 ///
1857 /// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
1858 /// - **Linux / iOS / Android:** Unsupported.
1859 pub fn set_maximizable(&self, maximizable: bool) -> crate::Result<()> {
1860 self.window.set_maximizable(maximizable)
1861 }
1862
1863 /// Determines if this window's native minimize button should be enabled.
1864 ///
1865 /// ## Platform-specific
1866 ///
1867 /// - **Linux / iOS / Android:** Unsupported.
1868 pub fn set_minimizable(&self, minimizable: bool) -> crate::Result<()> {
1869 self.window.set_minimizable(minimizable)
1870 }
1871
1872 /// Determines if this window's native close button should be enabled.
1873 ///
1874 /// ## Platform-specific
1875 ///
1876 /// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button.
1877 /// Depending on the system, this function may not have any effect when called on a window that is already visible"
1878 /// - **iOS / Android:** Unsupported.
1879 pub fn set_closable(&self, closable: bool) -> crate::Result<()> {
1880 self.window.set_closable(closable)
1881 }
1882
1883 /// Set this window's title.
1884 pub fn set_title(&self, title: &str) -> crate::Result<()> {
1885 self.window.set_title(title)
1886 }
1887
1888 /// Maximizes this window.
1889 pub fn maximize(&self) -> crate::Result<()> {
1890 self.window.maximize()
1891 }
1892
1893 /// Un-maximizes this window.
1894 pub fn unmaximize(&self) -> crate::Result<()> {
1895 self.window.unmaximize()
1896 }
1897
1898 /// Minimizes this window.
1899 pub fn minimize(&self) -> crate::Result<()> {
1900 self.window.minimize()
1901 }
1902
1903 /// Un-minimizes this window.
1904 pub fn unminimize(&self) -> crate::Result<()> {
1905 self.window.unminimize()
1906 }
1907
1908 /// Show this window.
1909 pub fn show(&self) -> crate::Result<()> {
1910 self.window.show()
1911 }
1912
1913 /// Hide this window.
1914 pub fn hide(&self) -> crate::Result<()> {
1915 self.window.hide()
1916 }
1917
1918 /// Closes this window. It emits [`crate::RunEvent::CloseRequested`] first like a user-initiated close request so you can intercept it.
1919 pub fn close(&self) -> crate::Result<()> {
1920 self.window.close()
1921 }
1922
1923 /// Destroys this window. Similar to [`Self::close`] but does not emit any events and force close the window instead.
1924 pub fn destroy(&self) -> crate::Result<()> {
1925 self.window.destroy()
1926 }
1927
1928 /// Determines if this window should be [decorated].
1929 ///
1930 /// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration
1931 pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
1932 self.window.set_decorations(decorations)
1933 }
1934
1935 /// Determines if this window should have shadow.
1936 ///
1937 /// ## Platform-specific
1938 ///
1939 /// - **Windows:**
1940 /// - `false` has no effect on decorated window, shadow are always ON.
1941 /// - `true` will make undecorated window have a 1px white border,
1942 /// and on Windows 11, it will have a rounded corners.
1943 /// - **Linux:** Unsupported.
1944 pub fn set_shadow(&self, enable: bool) -> crate::Result<()> {
1945 self.window.set_shadow(enable)
1946 }
1947
1948 /// Sets window effects, pass [`None`] to clear any effects applied if possible.
1949 ///
1950 /// Requires the window to be transparent.
1951 ///
1952 /// See [`crate::window::EffectsBuilder`] for a convenient builder for [`crate::utils::config::WindowEffectsConfig`].
1953 ///
1954 ///
1955 /// ```rust,no_run
1956 /// use tauri::{Manager, window::{Color, Effect, EffectState, EffectsBuilder}};
1957 /// tauri::Builder::default()
1958 /// .setup(|app| {
1959 /// let webview_window = app.get_webview_window("main").unwrap();
1960 /// webview_window.set_effects(
1961 /// EffectsBuilder::new()
1962 /// .effect(Effect::Popover)
1963 /// .state(EffectState::Active)
1964 /// .radius(5.)
1965 /// .color(Color(0, 0, 0, 255))
1966 /// .build(),
1967 /// )?;
1968 /// Ok(())
1969 /// });
1970 /// ```
1971 ///
1972 /// ## Platform-specific:
1973 ///
1974 /// - **Windows**: If using decorations or shadows, you may want to try this workaround <https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891>
1975 /// - **Linux**: Unsupported
1976 pub fn set_effects<E: Into<Option<crate::utils::config::WindowEffectsConfig>>>(
1977 &self,
1978 effects: E,
1979 ) -> crate::Result<()> {
1980 self.window.set_effects(effects)
1981 }
1982
1983 /// Determines if this window should always be below other windows.
1984 pub fn set_always_on_bottom(&self, always_on_bottom: bool) -> crate::Result<()> {
1985 self.window.set_always_on_bottom(always_on_bottom)
1986 }
1987
1988 /// Determines if this window should always be on top of other windows.
1989 pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
1990 self.window.set_always_on_top(always_on_top)
1991 }
1992
1993 /// Sets whether the window should be visible on all workspaces or virtual desktops.
1994 pub fn set_visible_on_all_workspaces(
1995 &self,
1996 visible_on_all_workspaces: bool,
1997 ) -> crate::Result<()> {
1998 self
1999 .window
2000 .set_visible_on_all_workspaces(visible_on_all_workspaces)
2001 }
2002
2003 /// Prevents the window contents from being captured by other apps.
2004 pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> {
2005 self.window.set_content_protected(protected)
2006 }
2007
2008 /// Resizes this window.
2009 pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
2010 self.window.set_size(size.into())
2011 }
2012
2013 /// Sets this window's minimum inner size.
2014 pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
2015 self.window.set_min_size(size.map(|s| s.into()))
2016 }
2017
2018 /// Sets this window's maximum inner size.
2019 pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
2020 self.window.set_max_size(size.map(|s| s.into()))
2021 }
2022
2023 /// Sets this window's minimum inner width.
2024 pub fn set_size_constraints(
2025 &self,
2026 constriants: tauri_runtime::window::WindowSizeConstraints,
2027 ) -> crate::Result<()> {
2028 self.window.set_size_constraints(constriants)
2029 }
2030
2031 /// Sets this window's position.
2032 pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2033 self.window.set_position(position)
2034 }
2035
2036 /// Determines if this window should be fullscreen.
2037 pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
2038 self.window.set_fullscreen(fullscreen)
2039 }
2040
2041 /// Bring the window to front and focus.
2042 pub fn set_focus(&self) -> crate::Result<()> {
2043 self.window.set_focus()
2044 }
2045
2046 /// Sets whether the window can be focused.
2047 ///
2048 /// ## Platform-specific
2049 ///
2050 /// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`.
2051 /// In this case, you might consider calling [`Window::set_focus`] but it will move the window to the back i.e. at the bottom in terms of z-order.
2052 pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> {
2053 self.window.set_focusable(focusable)
2054 }
2055
2056 /// Sets this window' icon.
2057 pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> {
2058 self.window.set_icon(icon)
2059 }
2060
2061 /// Sets the window background color.
2062 ///
2063 /// ## Platform-specific:
2064 ///
2065 /// - **iOS / Android:** Unsupported.
2066 /// - **macOS**: Not implemented for the webview layer..
2067 /// - **Windows**:
2068 /// - alpha channel is ignored for the window layer.
2069 /// - On Windows 7, transparency is not supported and the alpha value will be ignored for the webview layer..
2070 /// - On Windows 8 and newer: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` for the webview layer.
2071 pub fn set_background_color(&self, color: Option<Color>) -> crate::Result<()> {
2072 self.window.set_background_color(color)?;
2073 self.webview.set_background_color(color)
2074 }
2075
2076 /// Whether to hide the window icon from the taskbar or not.
2077 ///
2078 /// ## Platform-specific
2079 ///
2080 /// - **macOS:** Unsupported.
2081 pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
2082 self.window.set_skip_taskbar(skip)
2083 }
2084
2085 /// Grabs the cursor, preventing it from leaving the window.
2086 ///
2087 /// There's no guarantee that the cursor will be hidden. You should
2088 /// hide it by yourself if you want so.
2089 ///
2090 /// ## Platform-specific
2091 ///
2092 /// - **Linux:** Unsupported.
2093 /// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
2094 pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2095 self.window.set_cursor_grab(grab)
2096 }
2097
2098 /// Modifies the cursor's visibility.
2099 ///
2100 /// If `false`, this will hide the cursor. If `true`, this will show the cursor.
2101 ///
2102 /// ## Platform-specific
2103 ///
2104 /// - **Windows:** The cursor is only hidden within the confines of the window.
2105 /// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
2106 /// outside of the window.
2107 pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2108 self.window.set_cursor_visible(visible)
2109 }
2110
2111 /// Modifies the cursor icon of the window.
2112 pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2113 self.window.set_cursor_icon(icon)
2114 }
2115
2116 /// Changes the position of the cursor in window coordinates.
2117 pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2118 self.window.set_cursor_position(position)
2119 }
2120
2121 /// Ignores the window cursor events.
2122 pub fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2123 self.window.set_ignore_cursor_events(ignore)
2124 }
2125
2126 /// Starts dragging the window.
2127 pub fn start_dragging(&self) -> crate::Result<()> {
2128 self.window.start_dragging()
2129 }
2130
2131 /// Sets the overlay icon on the taskbar **Windows only**. Using `None` will remove the icon
2132 ///
2133 /// The overlay icon can be unique for each window.
2134 #[cfg(target_os = "windows")]
2135 #[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
2136 pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
2137 self.window.set_overlay_icon(icon)
2138 }
2139
2140 /// Sets the taskbar badge count. Using `0` or `None` will remove the badge
2141 ///
2142 /// ## Platform-specific
2143 /// - **Windows:** Unsupported, use [`WebviewWindow::set_overlay_icon`] instead.
2144 /// - **iOS:** iOS expects i32, the value will be clamped to i32::MIN, i32::MAX.
2145 /// - **Android:** Unsupported.
2146 pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
2147 self.window.set_badge_count(count)
2148 }
2149
2150 /// Sets the taskbar badge label **macOS only**. Using `None` will remove the badge
2151 #[cfg(target_os = "macos")]
2152 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
2153 pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
2154 self.window.set_badge_label(label)
2155 }
2156
2157 /// Sets the taskbar progress state.
2158 ///
2159 /// ## Platform-specific
2160 ///
2161 /// - **Linux / macOS**: Progress bar is app-wide and not specific to this window.
2162 /// - **Linux**: Only supported desktop environments with `libunity` (e.g. GNOME).
2163 /// - **iOS / Android:** Unsupported.
2164 pub fn set_progress_bar(
2165 &self,
2166 progress_state: crate::window::ProgressBarState,
2167 ) -> crate::Result<()> {
2168 self.window.set_progress_bar(progress_state)
2169 }
2170
2171 /// Sets the title bar style. **macOS only**.
2172 pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
2173 self.window.set_title_bar_style(style)
2174 }
2175
2176 /// Sets the theme for this window.
2177 ///
2178 /// ## Platform-specific
2179 ///
2180 /// - **Linux / macOS**: Theme is app-wide and not specific to this window.
2181 /// - **iOS / Android:** Unsupported.
2182 pub fn set_theme(&self, theme: Option<tauri_utils::Theme>) -> crate::Result<()> {
2183 self.window.set_theme(theme)
2184 }
2185}
2186
2187/// Desktop webview setters and actions.
2188#[cfg(desktop)]
2189impl<R: Runtime> WebviewWindow<R> {
2190 /// Opens the dialog to prints the contents of the webview.
2191 /// Currently only supported on macOS on `wry`.
2192 /// `window.print()` works on all platforms.
2193 pub fn print(&self) -> crate::Result<()> {
2194 self.webview.print()
2195 }
2196}
2197
2198/// Webview APIs.
2199impl<R: Runtime> WebviewWindow<R> {
2200 /// Executes a closure, providing it with the webview handle that is specific to the current platform.
2201 ///
2202 /// The closure is executed on the main thread.
2203 ///
2204 /// Note that `webview2-com`, `webkit2gtk`, `objc2_web_kit` and similar crates may be updated in minor releases of Tauri.
2205 /// Therefore it's recommended to pin Tauri to at least a minor version when you're using `with_webview`.
2206 ///
2207 /// # Examples
2208 ///
2209 /// ```rust,no_run
2210 /// use tauri::Manager;
2211 ///
2212 /// fn main() {
2213 /// tauri::Builder::default()
2214 /// .setup(|app| {
2215 /// let main_webview = app.get_webview_window("main").unwrap();
2216 /// main_webview.with_webview(|webview| {
2217 /// #[cfg(target_os = "linux")]
2218 /// {
2219 /// // see <https://docs.rs/webkit2gtk/2.0.0/webkit2gtk/struct.WebView.html>
2220 /// // and <https://docs.rs/webkit2gtk/2.0.0/webkit2gtk/trait.WebViewExt.html>
2221 /// use webkit2gtk::WebViewExt;
2222 /// webview.inner().set_zoom_level(4.);
2223 /// }
2224 ///
2225 /// #[cfg(windows)]
2226 /// unsafe {
2227 /// // see <https://docs.rs/webview2-com/0.19.1/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html>
2228 /// webview.controller().SetZoomFactor(4.).unwrap();
2229 /// }
2230 ///
2231 /// #[cfg(target_os = "macos")]
2232 /// unsafe {
2233 /// let view: &objc2_web_kit::WKWebView = &*webview.inner().cast();
2234 /// let controller: &objc2_web_kit::WKUserContentController = &*webview.controller().cast();
2235 /// let window: &objc2_app_kit::NSWindow = &*webview.ns_window().cast();
2236 ///
2237 /// view.setPageZoom(4.);
2238 /// controller.removeAllUserScripts();
2239 /// let bg_color = objc2_app_kit::NSColor::colorWithDeviceRed_green_blue_alpha(0.5, 0.2, 0.4, 1.);
2240 /// window.setBackgroundColor(Some(&bg_color));
2241 /// }
2242 ///
2243 /// #[cfg(target_os = "android")]
2244 /// {
2245 /// use jni::objects::JValue;
2246 /// webview.jni_handle().exec(|env, _, webview| {
2247 /// env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap();
2248 /// })
2249 /// }
2250 /// });
2251 /// Ok(())
2252 /// });
2253 /// }
2254 /// ```
2255 #[allow(clippy::needless_doctest_main)] // To avoid a large diff
2256 #[cfg(feature = "wry")]
2257 #[cfg_attr(docsrs, doc(feature = "wry"))]
2258 pub fn with_webview<F: FnOnce(crate::webview::PlatformWebview) + Send + 'static>(
2259 &self,
2260 f: F,
2261 ) -> crate::Result<()> {
2262 self.webview.with_webview(f)
2263 }
2264
2265 /// Returns the current url of the webview.
2266 pub fn url(&self) -> crate::Result<Url> {
2267 self.webview.url()
2268 }
2269
2270 /// Navigates the webview to the defined url.
2271 pub fn navigate(&self, url: Url) -> crate::Result<()> {
2272 self.webview.navigate(url)
2273 }
2274
2275 /// Reloads the current page.
2276 pub fn reload(&self) -> crate::Result<()> {
2277 self.webview.reload()
2278 }
2279
2280 /// Handles this window receiving an [`crate::webview::InvokeRequest`].
2281 pub fn on_message(
2282 self,
2283 request: crate::webview::InvokeRequest,
2284 responder: Box<OwnedInvokeResponder<R>>,
2285 ) {
2286 self.webview.on_message(request, responder)
2287 }
2288
2289 /// Evaluates JavaScript on this window.
2290 pub fn eval(&self, js: impl Into<String>) -> crate::Result<()> {
2291 self.webview.eval(js)
2292 }
2293
2294 /// Opens the developer tools window (Web Inspector).
2295 /// The devtools is only enabled on debug builds or with the `devtools` feature flag.
2296 ///
2297 /// ## Platform-specific
2298 ///
2299 /// - **macOS:** Only supported on macOS 10.15+.
2300 /// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
2301 ///
2302 /// # Examples
2303 ///
2304 /// ```rust,no_run
2305 /// use tauri::Manager;
2306 /// tauri::Builder::default()
2307 /// .setup(|app| {
2308 /// #[cfg(debug_assertions)]
2309 /// app.get_webview_window("main").unwrap().open_devtools();
2310 /// Ok(())
2311 /// });
2312 /// ```
2313 #[cfg(any(debug_assertions, feature = "devtools"))]
2314 #[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
2315 pub fn open_devtools(&self) {
2316 self.webview.open_devtools();
2317 }
2318
2319 /// Closes the developer tools window (Web Inspector).
2320 /// The devtools is only enabled on debug builds or with the `devtools` feature flag.
2321 ///
2322 /// ## Platform-specific
2323 ///
2324 /// - **macOS:** Only supported on macOS 10.15+.
2325 /// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
2326 /// - **Windows:** Unsupported.
2327 ///
2328 /// # Examples
2329 ///
2330 /// ```rust,no_run
2331 /// use tauri::Manager;
2332 /// tauri::Builder::default()
2333 /// .setup(|app| {
2334 /// #[cfg(debug_assertions)]
2335 /// {
2336 /// let webview = app.get_webview_window("main").unwrap();
2337 /// webview.open_devtools();
2338 /// std::thread::spawn(move || {
2339 /// std::thread::sleep(std::time::Duration::from_secs(10));
2340 /// webview.close_devtools();
2341 /// });
2342 /// }
2343 /// Ok(())
2344 /// });
2345 /// ```
2346 #[cfg(any(debug_assertions, feature = "devtools"))]
2347 #[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
2348 pub fn close_devtools(&self) {
2349 self.webview.close_devtools();
2350 }
2351
2352 /// Checks if the developer tools window (Web Inspector) is opened.
2353 /// The devtools is only enabled on debug builds or with the `devtools` feature flag.
2354 ///
2355 /// ## Platform-specific
2356 ///
2357 /// - **macOS:** Only supported on macOS 10.15+.
2358 /// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
2359 /// - **Windows:** Unsupported.
2360 ///
2361 /// # Examples
2362 ///
2363 /// ```rust,no_run
2364 /// use tauri::Manager;
2365 /// tauri::Builder::default()
2366 /// .setup(|app| {
2367 /// #[cfg(debug_assertions)]
2368 /// {
2369 /// let webview = app.get_webview_window("main").unwrap();
2370 /// if !webview.is_devtools_open() {
2371 /// webview.open_devtools();
2372 /// }
2373 /// }
2374 /// Ok(())
2375 /// });
2376 /// ```
2377 #[cfg(any(debug_assertions, feature = "devtools"))]
2378 #[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
2379 pub fn is_devtools_open(&self) -> bool {
2380 self.webview.is_devtools_open()
2381 }
2382
2383 /// Set the webview zoom level
2384 ///
2385 /// ## Platform-specific:
2386 ///
2387 /// - **Android**: Not supported.
2388 /// - **macOS**: available on macOS 11+ only.
2389 /// - **iOS**: available on iOS 14+ only.
2390 pub fn set_zoom(&self, scale_factor: f64) -> crate::Result<()> {
2391 self.webview.set_zoom(scale_factor)
2392 }
2393
2394 /// Clear all browsing data for this webview window.
2395 pub fn clear_all_browsing_data(&self) -> crate::Result<()> {
2396 self.webview.clear_all_browsing_data()
2397 }
2398
2399 /// Returns all cookies in the runtime's cookie store including HTTP-only and secure cookies.
2400 ///
2401 /// Note that cookies will only be returned for URLs with an http or https scheme.
2402 /// Cookies set through javascript for local files
2403 /// (such as those served from the tauri://) protocol are not currently supported.
2404 ///
2405 /// # Stability
2406 ///
2407 /// See [Self::cookies].
2408 ///
2409 /// # Known issues
2410 ///
2411 /// See [Self::cookies].
2412 pub fn cookies_for_url(&self, url: Url) -> crate::Result<Vec<Cookie<'static>>> {
2413 self.webview.cookies_for_url(url)
2414 }
2415
2416 /// Returns all cookies in the runtime's cookie store for all URLs including HTTP-only and secure cookies.
2417 ///
2418 /// Note that cookies will only be returned for URLs with an http or https scheme.
2419 /// Cookies set through javascript for local files
2420 /// (such as those served from the tauri://) protocol are not currently supported.
2421 ///
2422 /// # Stability
2423 ///
2424 /// The return value of this function leverages [`tauri_runtime::Cookie`] which re-exports the cookie crate.
2425 /// This dependency might receive updates in minor Tauri releases.
2426 ///
2427 /// # Known issues
2428 ///
2429 /// On Windows, this function deadlocks when used in a synchronous command or event handlers, see [the Webview2 issue].
2430 /// You should use `async` commands and separate threads when reading cookies.
2431 ///
2432 /// ## Platform-specific
2433 ///
2434 /// - **Android**: Unsupported, always returns an empty [`Vec`].
2435 ///
2436 /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
2437 pub fn cookies(&self) -> crate::Result<Vec<Cookie<'static>>> {
2438 self.webview.cookies()
2439 }
2440
2441 /// Set a cookie for the webview.
2442 ///
2443 /// # Stability
2444 ///
2445 /// See [Self::cookies].
2446 pub fn set_cookie(&self, cookie: Cookie<'_>) -> crate::Result<()> {
2447 self.webview.set_cookie(cookie)
2448 }
2449
2450 /// Delete a cookie for the webview.
2451 ///
2452 /// # Stability
2453 ///
2454 /// See [Self::cookies].
2455 pub fn delete_cookie(&self, cookie: Cookie<'_>) -> crate::Result<()> {
2456 self.webview.delete_cookie(cookie)
2457 }
2458}
2459
2460impl<R: Runtime> Listener<R> for WebviewWindow<R> {
2461 /// Listen to an event on this webview window.
2462 ///
2463 /// # Examples
2464 ///
2465 /// ```
2466 /// use tauri::{Manager, Listener};
2467 ///
2468 /// tauri::Builder::default()
2469 /// .setup(|app| {
2470 /// let webview_window = app.get_webview_window("main").unwrap();
2471 /// webview_window.listen("component-loaded", move |event| {
2472 /// println!("window just loaded a component");
2473 /// });
2474 ///
2475 /// Ok(())
2476 /// });
2477 /// ```
2478 fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
2479 where
2480 F: Fn(Event) + Send + 'static,
2481 {
2482 let event = EventName::new(event.into()).unwrap();
2483 self.manager().listen(
2484 event,
2485 EventTarget::WebviewWindow {
2486 label: self.label().to_string(),
2487 },
2488 handler,
2489 )
2490 }
2491
2492 /// Listen to an event on this window webview only once.
2493 ///
2494 /// See [`Self::listen`] for more information.
2495 fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
2496 where
2497 F: FnOnce(Event) + Send + 'static,
2498 {
2499 let event = EventName::new(event.into()).unwrap();
2500 self.manager().once(
2501 event,
2502 EventTarget::WebviewWindow {
2503 label: self.label().to_string(),
2504 },
2505 handler,
2506 )
2507 }
2508
2509 /// Unlisten to an event on this webview window.
2510 ///
2511 /// # Examples
2512 /// ```
2513 /// use tauri::{Manager, Listener};
2514 ///
2515 /// tauri::Builder::default()
2516 /// .setup(|app| {
2517 /// let webview_window = app.get_webview_window("main").unwrap();
2518 /// let webview_window_ = webview_window.clone();
2519 /// let handler = webview_window.listen("component-loaded", move |event| {
2520 /// println!("webview_window just loaded a component");
2521 ///
2522 /// // we no longer need to listen to the event
2523 /// // we also could have used `webview_window.once` instead
2524 /// webview_window_.unlisten(event.id());
2525 /// });
2526 ///
2527 /// // stop listening to the event when you do not need it anymore
2528 /// webview_window.unlisten(handler);
2529 ///
2530 /// Ok(())
2531 /// });
2532 /// ```
2533 fn unlisten(&self, id: EventId) {
2534 self.manager().unlisten(id)
2535 }
2536}
2537
2538impl<R: Runtime> Emitter<R> for WebviewWindow<R> {}
2539
2540impl<R: Runtime> Manager<R> for WebviewWindow<R> {
2541 fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
2542 self
2543 .webview
2544 .resources_table
2545 .lock()
2546 .expect("poisoned window resources table")
2547 }
2548}
2549
2550impl<R: Runtime> ManagerBase<R> for WebviewWindow<R> {
2551 fn manager(&self) -> &AppManager<R> {
2552 self.webview.manager()
2553 }
2554
2555 fn manager_owned(&self) -> Arc<AppManager<R>> {
2556 self.webview.manager_owned()
2557 }
2558
2559 fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
2560 self.webview.runtime()
2561 }
2562
2563 fn managed_app_handle(&self) -> &AppHandle<R> {
2564 self.webview.managed_app_handle()
2565 }
2566}