Files
archived-tauri/crates/tauri/src/webview/webview_window.rs
Luke e919a760ed feat(webview-window): add set_simple_fullscreen to WebviewWindow (#14619)
* feat(webview): add set_simple_fullscreen to WebviewWindow

* add changes

* Combine per platform fn to one

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2026-01-19 11:38:37 +08:00

2600 lines
87 KiB
Rust

// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! [`Window`] that hosts a single [`Webview`].
use std::{
borrow::Cow,
path::{Path, PathBuf},
sync::{Arc, MutexGuard},
};
use crate::{
event::EventTarget,
ipc::ScopeObject,
runtime::dpi::{PhysicalPosition, PhysicalSize},
webview::{NewWindowResponse, ScrollBarStyle},
window::Monitor,
Emitter, EventName, Listener, ResourceTable, Window,
};
#[cfg(desktop)]
use crate::{
image::Image,
menu::{ContextMenu, Menu},
runtime::{
dpi::{Position, Size},
window::CursorIcon,
UserAttentionType,
},
};
use tauri_runtime::webview::NewWindowFeatures;
use tauri_utils::config::{BackgroundThrottlingPolicy, Color, WebviewUrl, WindowConfig};
use url::Url;
use crate::{
ipc::{CommandArg, CommandItem, InvokeError, OwnedInvokeResponder},
manager::AppManager,
sealed::{ManagerBase, RuntimeOrDispatch},
webview::{Cookie, PageLoadPayload, WebviewBuilder, WebviewEvent},
window::WindowBuilder,
AppHandle, Event, EventId, Manager, Runtime, Webview, WindowEvent,
};
use tauri_macros::default_runtime;
#[cfg(windows)]
use windows::Win32::Foundation::HWND;
use super::{DownloadEvent, ResolvedScope};
/// A builder for [`WebviewWindow`], a window that hosts a single webview.
pub struct WebviewWindowBuilder<'a, R: Runtime, M: Manager<R>> {
window_builder: WindowBuilder<'a, R, M>,
webview_builder: WebviewBuilder<R>,
}
impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
/// Initializes a webview window builder with the given window label.
///
/// # Known issues
///
/// On Windows, this function deadlocks when used in a synchronous command and event handlers, see [the Webview2 issue].
/// You should use `async` commands and separate threads when creating windows.
///
/// # Examples
///
/// - Create a window in the setup hook:
///
/// ```
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
/// .build()?;
/// Ok(())
/// });
/// ```
///
/// - Create a window in a separate thread:
///
/// ```
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle().clone();
/// std::thread::spawn(move || {
/// let webview_window = tauri::WebviewWindowBuilder::new(&handle, "label", tauri::WebviewUrl::App("index.html".into()))
/// .build()
/// .unwrap();
/// });
/// Ok(())
/// });
/// ```
///
/// - Create a window in a command:
///
/// ```
/// #[tauri::command]
/// async fn create_window(app: tauri::AppHandle) {
/// let webview_window = tauri::WebviewWindowBuilder::new(&app, "label", tauri::WebviewUrl::App("index.html".into()))
/// .build()
/// .unwrap();
/// }
/// ```
///
/// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
pub fn new<L: Into<String>>(manager: &'a M, label: L, url: WebviewUrl) -> Self {
let label = label.into();
Self {
window_builder: WindowBuilder::new(manager, &label),
webview_builder: WebviewBuilder::new(&label, url),
}
}
/// Initializes a webview window builder from a [`WindowConfig`] from tauri.conf.json.
/// Keep in mind that you can't create 2 windows with the same `label` so make sure
/// that the initial window was closed or change the label of the cloned [`WindowConfig`].
///
/// # Known issues
///
/// On Windows, this function deadlocks when used in a synchronous command or event handlers, see [the Webview2 issue].
/// You should use `async` commands and separate threads when creating windows.
///
/// # Examples
///
/// - Create a window in a command:
///
/// ```
/// #[tauri::command]
/// async fn reopen_window(app: tauri::AppHandle) {
/// let webview_window = tauri::WebviewWindowBuilder::from_config(&app, &app.config().app.windows.get(0).unwrap())
/// .unwrap()
/// .build()
/// .unwrap();
/// }
/// ```
///
/// - Create a window in a command from a config with a specific label, and change its label so multiple instances can exist:
///
/// ```
/// #[tauri::command]
/// async fn open_window_multiple(app: tauri::AppHandle) {
/// let mut conf = app.config().app.windows.iter().find(|c| c.label == "template-for-multiwindow").unwrap().clone();
/// // This should be a unique label for all windows. For example, we can use a random suffix:
/// let mut buf = [0u8; 1];
/// assert_eq!(getrandom::fill(&mut buf), Ok(()));
/// conf.label = format!("my-multiwindow-{}", buf[0]);
/// let webview_window = tauri::WebviewWindowBuilder::from_config(&app, &conf)
/// .unwrap()
/// .build()
/// .unwrap();
/// }
/// ```
///
/// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
pub fn from_config(manager: &'a M, config: &WindowConfig) -> crate::Result<Self> {
Ok(Self {
window_builder: WindowBuilder::from_config(manager, config)?,
webview_builder: WebviewBuilder::from_config(config),
})
}
/// Registers a global menu event listener.
///
/// Note that this handler is called for any menu event,
/// whether it is coming from this window, another window or from the tray icon menu.
///
/// Also note that this handler will not be called if
/// the window used to register it was closed.
///
/// # Examples
/// ```
/// use tauri::menu::{Menu, Submenu, MenuItem};
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle();
/// let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
/// let menu = Menu::with_items(handle, &[
/// &Submenu::with_items(handle, "File", true, &[
/// &save_menu_item,
/// ])?,
/// ])?;
/// let webview_window = tauri::WebviewWindowBuilder::new(app, "editor", tauri::WebviewUrl::App("index.html".into()))
/// .menu(menu)
/// .on_menu_event(move |window, event| {
/// if event.id == save_menu_item.id() {
/// // save menu item
/// }
/// })
/// .build()
/// .unwrap();
///
/// Ok(())
/// });
/// ```
#[cfg(desktop)]
pub fn on_menu_event<F: Fn(&crate::Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
mut self,
f: F,
) -> Self {
self.window_builder = self.window_builder.on_menu_event(f);
self
}
/// Defines a closure to be executed when the webview makes an HTTP request for a web resource, allowing you to modify the response.
///
/// Currently only implemented for the `tauri` URI protocol.
///
/// **NOTE:** Currently this is **not** executed when using external URLs such as a development server,
/// but it might be implemented in the future. **Always** check the request URL.
///
/// # Examples
/// ```rust,no_run
/// use tauri::{
/// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
/// webview::WebviewWindowBuilder,
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_web_resource_request(|request, response| {
/// if request.uri().scheme_str() == Some("tauri") {
/// // if we have a CSP header, Tauri is loading an HTML file
/// // for this example, let's dynamically change the CSP
/// if let Some(csp) = response.headers_mut().get_mut("Content-Security-Policy") {
/// // use the tauri helper to parse the CSP policy to a map
/// let mut csp_map: HashMap<String, CspDirectiveSources> = Csp::Policy(csp.to_str().unwrap().to_string()).into();
/// csp_map.entry("script-src".to_string()).or_insert_with(Default::default).push("'unsafe-inline'");
/// // use the tauri helper to get a CSP string from the map
/// let csp_string = Csp::from(csp_map).to_string();
/// *csp = HeaderValue::from_str(&csp_string).unwrap();
/// }
/// }
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
pub fn on_web_resource_request<
F: Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync + 'static,
>(
mut self,
f: F,
) -> Self {
self.webview_builder = self.webview_builder.on_web_resource_request(f);
self
}
/// Defines a closure to be executed when the webview navigates to a URL. Returning `false` cancels the navigation.
///
/// # Examples
/// ```rust,no_run
/// use tauri::{
/// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
/// webview::WebviewWindowBuilder,
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_navigation(|url| {
/// // allow the production URL or localhost on dev
/// url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
pub fn on_navigation<F: Fn(&Url) -> bool + Send + 'static>(mut self, f: F) -> Self {
self.webview_builder = self.webview_builder.on_navigation(f);
self
}
/// Set a new window request handler to decide if incoming url is allowed to be opened.
///
/// A new window is requested to be opened by the [window.open] API.
///
/// The closure take the URL to open and the window features object and returns [`NewWindowResponse`] to determine whether the window should open.
///
/// # Examples
/// ```rust,no_run
/// use tauri::{
/// utils::config::WebviewUrl,
/// webview::WebviewWindowBuilder,
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let app_ = app.handle().clone();
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_new_window(move |url, features| {
/// let builder = tauri::WebviewWindowBuilder::new(
/// &app_,
/// // note: add an ID counter or random label generator to support multiple opened windows at the same time
/// "opened-window",
/// tauri::WebviewUrl::External("about:blank".parse().unwrap()),
/// )
/// .window_features(features)
/// .on_document_title_changed(|window, title| {
/// window.set_title(&title).unwrap();
/// })
/// .title(url.as_str());
///
/// let window = builder.build().unwrap();
/// tauri::webview::NewWindowResponse::Create { window }
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
///
/// # Platform-specific
///
/// - **Android / iOS**: Not supported.
/// - **Windows**: The closure is executed on a separate thread to prevent a deadlock.
///
/// [window.open]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
pub fn on_new_window<
F: Fn(Url, NewWindowFeatures) -> NewWindowResponse<R> + Send + Sync + 'static,
>(
mut self,
f: F,
) -> Self {
self.webview_builder = self.webview_builder.on_new_window(f);
self
}
/// Defines a closure to be executed when the document title changes.
///
/// Note that it may run before or after the navigation event.
pub fn on_document_title_changed<F: Fn(WebviewWindow<R>, String) + Send + 'static>(
mut self,
f: F,
) -> Self {
self.webview_builder = self
.webview_builder
.on_document_title_changed(move |webview, url| {
f(
WebviewWindow {
window: webview.window(),
webview,
},
url,
)
});
self
}
/// Set a download event handler to be notified when a download is requested or finished.
///
/// Returning `false` prevents the download from happening on a [`DownloadEvent::Requested`] event.
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::{
utils::config::{Csp, CspDirectiveSources, WebviewUrl},
webview::{DownloadEvent, WebviewWindowBuilder},
};
tauri::Builder::default()
.setup(|app| {
let handle = app.handle();
let webview_window = WebviewWindowBuilder::new(handle, "core", WebviewUrl::App("index.html".into()))
.on_download(|webview, event| {
match event {
DownloadEvent::Requested { url, destination } => {
println!("downloading {}", url);
*destination = "/home/tauri/target/path".into();
}
DownloadEvent::Finished { url, path, success } => {
println!("downloaded {} to {:?}, success: {}", url, path, success);
}
_ => (),
}
// let the download start
true
})
.build()?;
Ok(())
});
```
"####
)]
pub fn on_download<F: Fn(Webview<R>, DownloadEvent<'_>) -> bool + Send + Sync + 'static>(
mut self,
f: F,
) -> Self {
self.webview_builder.download_handler.replace(Arc::new(f));
self
}
/// Defines a closure to be executed when a page load event is triggered.
/// The event can be either [`tauri_runtime::webview::PageLoadEvent::Started`] if the page has started loading
/// or [`tauri_runtime::webview::PageLoadEvent::Finished`] when the page finishes loading.
///
/// # Examples
/// ```rust,no_run
/// use tauri::{
/// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
/// webview::{PageLoadEvent, WebviewWindowBuilder},
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_page_load(|window, payload| {
/// match payload.event() {
/// PageLoadEvent::Started => {
/// println!("{} finished loading", payload.url());
/// }
/// PageLoadEvent::Finished => {
/// println!("{} finished loading", payload.url());
/// }
/// }
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
pub fn on_page_load<F: Fn(WebviewWindow<R>, PageLoadPayload<'_>) + Send + Sync + 'static>(
mut self,
f: F,
) -> Self {
self.webview_builder = self.webview_builder.on_page_load(move |webview, payload| {
f(
WebviewWindow {
window: webview.window(),
webview,
},
payload,
)
});
self
}
/// Creates a new window.
pub fn build(self) -> crate::Result<WebviewWindow<R>> {
let (window, webview) = self.window_builder.with_webview(self.webview_builder)?;
Ok(WebviewWindow { window, webview })
}
}
/// Desktop APIs.
#[cfg(desktop)]
impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
/// Sets the menu for the window.
#[must_use]
pub fn menu(mut self, menu: crate::menu::Menu<R>) -> Self {
self.window_builder = self.window_builder.menu(menu);
self
}
/// Show window in the center of the screen.
#[must_use]
pub fn center(mut self) -> Self {
self.window_builder = self.window_builder.center();
self
}
/// The initial position of the window in logical pixels.
#[must_use]
pub fn position(mut self, x: f64, y: f64) -> Self {
self.window_builder = self.window_builder.position(x, y);
self
}
/// Window size in logical pixels.
#[must_use]
pub fn inner_size(mut self, width: f64, height: f64) -> Self {
self.window_builder = self.window_builder.inner_size(width, height);
self
}
/// Window min inner size in logical pixels.
#[must_use]
pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
self.window_builder = self.window_builder.min_inner_size(min_width, min_height);
self
}
/// Window max inner size in logical pixels.
#[must_use]
pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
self.window_builder = self.window_builder.max_inner_size(max_width, max_height);
self
}
/// Window inner size constraints.
#[must_use]
pub fn inner_size_constraints(
mut self,
constraints: tauri_runtime::window::WindowSizeConstraints,
) -> Self {
self.window_builder = self.window_builder.inner_size_constraints(constraints);
self
}
/// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size)
/// on creation, which means the window size will be limited to `monitor size - taskbar size`
///
/// **NOTE**: The overflow check is only performed on window creation, resizes can still overflow
///
/// ## Platform-specific
///
/// - **iOS / Android:** Unsupported.
#[must_use]
pub fn prevent_overflow(mut self) -> Self {
self.window_builder = self.window_builder.prevent_overflow();
self
}
/// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size)
/// on creation with a margin, which means the window size will be limited to `monitor size - taskbar size - margin size`
///
/// **NOTE**: The overflow check is only performed on window creation, resizes can still overflow
///
/// ## Platform-specific
///
/// - **iOS / Android:** Unsupported.
#[must_use]
pub fn prevent_overflow_with_margin(mut self, margin: impl Into<Size>) -> Self {
self.window_builder = self.window_builder.prevent_overflow_with_margin(margin);
self
}
/// Whether the window is resizable or not.
/// When resizable is set to false, native window's maximize button is automatically disabled.
#[must_use]
pub fn resizable(mut self, resizable: bool) -> Self {
self.window_builder = self.window_builder.resizable(resizable);
self
}
/// Whether the window's native maximize button is enabled or not.
/// If resizable is set to false, this setting is ignored.
///
/// ## Platform-specific
///
/// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
/// - **Linux / iOS / Android:** Unsupported.
#[must_use]
pub fn maximizable(mut self, maximizable: bool) -> Self {
self.window_builder = self.window_builder.maximizable(maximizable);
self
}
/// Whether the window's native minimize button is enabled or not.
///
/// ## Platform-specific
///
/// - **Linux / iOS / Android:** Unsupported.
#[must_use]
pub fn minimizable(mut self, minimizable: bool) -> Self {
self.window_builder = self.window_builder.minimizable(minimizable);
self
}
/// Whether the window's native close button is enabled or not.
///
/// ## Platform-specific
///
/// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button.
/// Depending on the system, this function may not have any effect when called on a window that is already visible"
/// - **iOS / Android:** Unsupported.
#[must_use]
pub fn closable(mut self, closable: bool) -> Self {
self.window_builder = self.window_builder.closable(closable);
self
}
/// The title of the window in the title bar.
#[must_use]
pub fn title<S: Into<String>>(mut self, title: S) -> Self {
self.window_builder = self.window_builder.title(title);
self
}
/// Whether to start the window in fullscreen or not.
#[must_use]
pub fn fullscreen(mut self, fullscreen: bool) -> Self {
self.window_builder = self.window_builder.fullscreen(fullscreen);
self
}
/// Sets the window to be initially focused.
#[must_use]
#[deprecated(
since = "1.2.0",
note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead."
)]
pub fn focus(mut self) -> Self {
self.window_builder = self.window_builder.focused(true);
self.webview_builder = self.webview_builder.focused(true);
self
}
/// Whether the window will be focusable or not.
#[must_use]
pub fn focusable(mut self, focusable: bool) -> Self {
self.window_builder = self.window_builder.focusable(focusable);
self
}
/// Whether the window will be initially focused or not.
#[must_use]
pub fn focused(mut self, focused: bool) -> Self {
self.window_builder = self.window_builder.focused(focused);
self.webview_builder = self.webview_builder.focused(focused);
self
}
/// Whether the window should be maximized upon creation.
#[must_use]
pub fn maximized(mut self, maximized: bool) -> Self {
self.window_builder = self.window_builder.maximized(maximized);
self
}
/// Whether the window should be immediately visible upon creation.
#[must_use]
pub fn visible(mut self, visible: bool) -> Self {
self.window_builder = self.window_builder.visible(visible);
self
}
/// Forces a theme or uses the system settings if None was provided.
///
/// ## Platform-specific
///
/// - **macOS**: Only supported on macOS 10.14+.
#[must_use]
pub fn theme(mut self, theme: Option<crate::Theme>) -> Self {
self.window_builder = self.window_builder.theme(theme);
self
}
/// Whether the window should have borders and bars.
#[must_use]
pub fn decorations(mut self, decorations: bool) -> Self {
self.window_builder = self.window_builder.decorations(decorations);
self
}
/// Whether the window should always be below other windows.
#[must_use]
pub fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
self.window_builder = self.window_builder.always_on_bottom(always_on_bottom);
self
}
/// Whether the window should always be on top of other windows.
#[must_use]
pub fn always_on_top(mut self, always_on_top: bool) -> Self {
self.window_builder = self.window_builder.always_on_top(always_on_top);
self
}
/// Whether the window will be visible on all workspaces or virtual desktops.
#[must_use]
pub fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
self.window_builder = self
.window_builder
.visible_on_all_workspaces(visible_on_all_workspaces);
self
}
/// Prevents the window contents from being captured by other apps.
#[must_use]
pub fn content_protected(mut self, protected: bool) -> Self {
self.window_builder = self.window_builder.content_protected(protected);
self
}
/// Sets the window icon.
pub fn icon(mut self, icon: Image<'a>) -> crate::Result<Self> {
self.window_builder = self.window_builder.icon(icon)?;
Ok(self)
}
/// Sets whether or not the window icon should be hidden from the taskbar.
///
/// ## Platform-specific
///
/// - **macOS**: Unsupported.
#[must_use]
pub fn skip_taskbar(mut self, skip: bool) -> Self {
self.window_builder = self.window_builder.skip_taskbar(skip);
self
}
/// Sets custom name for Windows' window class. **Windows only**.
#[must_use]
pub fn window_classname<S: Into<String>>(mut self, classname: S) -> Self {
self.window_builder = self.window_builder.window_classname(classname);
self
}
/// Sets whether or not the window has shadow.
///
/// ## Platform-specific
///
/// - **Windows:**
/// - `false` has no effect on decorated window, shadows are always ON.
/// - `true` will make undecorated window have a 1px white border,
/// and on Windows 11, it will have a rounded corners.
/// - **Linux:** Unsupported.
#[must_use]
pub fn shadow(mut self, enable: bool) -> Self {
self.window_builder = self.window_builder.shadow(enable);
self
}
/// Sets a parent to the window to be created.
///
/// ## Platform-specific
///
/// - **Windows**: This sets the passed parent as an owner window to the window to be created.
/// From [MSDN owned windows docs](https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows):
/// - An owned window is always above its owner in the z-order.
/// - The system automatically destroys an owned window when its owner is destroyed.
/// - An owned window is hidden when its owner is minimized.
/// - **Linux**: This makes the new window transient for parent, see <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
/// - **macOS**: This adds the window as a child of parent, see <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
pub fn parent(mut self, parent: &WebviewWindow<R>) -> crate::Result<Self> {
self.window_builder = self.window_builder.parent(&parent.window)?;
Ok(self)
}
/// Set an owner to the window to be created.
///
/// From MSDN:
/// - An owned window is always above its owner in the z-order.
/// - The system automatically destroys an owned window when its owner is destroyed.
/// - An owned window is hidden when its owner is minimized.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
#[cfg(windows)]
pub fn owner(mut self, owner: &WebviewWindow<R>) -> crate::Result<Self> {
self.window_builder = self.window_builder.owner(&owner.window)?;
Ok(self)
}
/// Set an owner to the window to be created.
///
/// From MSDN:
/// - An owned window is always above its owner in the z-order.
/// - The system automatically destroys an owned window when its owner is destroyed.
/// - An owned window is hidden when its owner is minimized.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
#[cfg(windows)]
#[must_use]
pub fn owner_raw(mut self, owner: HWND) -> Self {
self.window_builder = self.window_builder.owner_raw(owner);
self
}
/// Sets a parent to the window to be created.
///
/// A child window has the WS_CHILD style and is confined to the client area of its parent window.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
#[cfg(windows)]
#[must_use]
pub fn parent_raw(mut self, parent: HWND) -> Self {
self.window_builder = self.window_builder.parent_raw(parent);
self
}
/// Sets a parent to the window to be created.
///
/// See <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
#[cfg(target_os = "macos")]
#[must_use]
pub fn parent_raw(mut self, parent: *mut std::ffi::c_void) -> Self {
self.window_builder = self.window_builder.parent_raw(parent);
self
}
/// Sets the window to be created transient for parent.
///
/// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn transient_for(mut self, parent: &WebviewWindow<R>) -> crate::Result<Self> {
self.window_builder = self.window_builder.transient_for(&parent.window)?;
Ok(self)
}
/// Sets the window to be created transient for parent.
///
/// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
#[must_use]
pub fn transient_for_raw(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
self.window_builder = self.window_builder.transient_for_raw(parent);
self
}
/// Enables or disables drag and drop support.
#[cfg(windows)]
#[must_use]
pub fn drag_and_drop(mut self, enabled: bool) -> Self {
self.window_builder = self.window_builder.drag_and_drop(enabled);
self
}
/// Sets the [`crate::TitleBarStyle`].
#[cfg(target_os = "macos")]
#[must_use]
pub fn title_bar_style(mut self, style: crate::TitleBarStyle) -> Self {
self.window_builder = self.window_builder.title_bar_style(style);
self
}
/// Change the position of the window controls on macOS.
///
/// Requires titleBarStyle: Overlay and decorations: true.
#[cfg(target_os = "macos")]
#[must_use]
pub fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
self.webview_builder.webview_attributes = self
.webview_builder
.webview_attributes
.traffic_light_position(position.into());
self
}
/// Whether to show a link preview when long pressing on links. Available on macOS and iOS only.
///
/// Default is true.
///
/// See https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview
///
/// ## Platform-specific
///
/// - **Linux / Windows / Android:** Unsupported.
#[cfg(target_os = "macos")]
#[must_use]
pub fn allow_link_preview(mut self, allow_link_preview: bool) -> Self {
self.webview_builder = self.webview_builder.allow_link_preview(allow_link_preview);
self
}
/// Hide the window title.
#[cfg(target_os = "macos")]
#[must_use]
pub fn hidden_title(mut self, hidden: bool) -> Self {
self.window_builder = self.window_builder.hidden_title(hidden);
self
}
/// Defines the window [tabbing identifier] for macOS.
///
/// Windows with matching tabbing identifiers will be grouped together.
/// If the tabbing identifier is not set, automatic tabbing will be disabled.
///
/// [tabbing identifier]: <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
#[cfg(target_os = "macos")]
#[must_use]
pub fn tabbing_identifier(mut self, identifier: &str) -> Self {
self.window_builder = self.window_builder.tabbing_identifier(identifier);
self
}
/// Sets window effects.
///
/// Requires the window to be transparent.
///
/// ## Platform-specific:
///
/// - **Windows**: If using decorations or shadows, you may want to try this workaround <https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891>
/// - **Linux**: Unsupported
pub fn effects(mut self, effects: crate::utils::config::WindowEffectsConfig) -> Self {
self.window_builder = self.window_builder.effects(effects);
self
}
}
/// Webview attributes.
impl<R: Runtime, M: Manager<R>> WebviewWindowBuilder<'_, R, M> {
/// Sets whether clicking an inactive window also clicks through to the webview.
#[must_use]
pub fn accept_first_mouse(mut self, accept: bool) -> Self {
self.webview_builder = self.webview_builder.accept_first_mouse(accept);
self
}
/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
///
/// Since it runs on all top-level document navigations,
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
///
/// This is executed only on the main frame.
/// If you only want to run it in all frames, use [Self::initialization_script_for_all_frames] instead.
///
/// ## Platform-specific
///
/// - **Windows:** scripts are always added to subframes.
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
///
/// # Examples
///
/// ```rust
/// const INIT_SCRIPT: &str = r#"
/// if (window.location.origin === 'https://tauri.app') {
/// console.log("hello world from js init script");
///
/// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
/// }
/// "#;
///
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
/// .initialization_script(INIT_SCRIPT)
/// .build()?;
/// Ok(())
/// });
/// }
/// ```
#[must_use]
pub fn initialization_script(mut self, script: impl Into<String>) -> Self {
self.webview_builder = self.webview_builder.initialization_script(script);
self
}
/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,
/// but before the HTML document has been parsed and before any other script included by the HTML document is run.
///
/// Since it runs on all top-level document navigations and also child frame page navigations,
/// it's recommended to check the `window.location` to guard your script from running on unexpected origins.
///
/// This is executed on all frames (main frame and also sub frames).
/// If you only want to run the script in the main frame, use [Self::initialization_script] instead.
///
/// ## Platform-specific
///
/// - **Android:** When [addDocumentStartJavaScript] is not supported,
/// we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
/// For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
///
/// # Examples
///
/// ```rust
/// const INIT_SCRIPT: &str = r#"
/// if (window.location.origin === 'https://tauri.app') {
/// console.log("hello world from js init script");
///
/// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
/// }
/// "#;
///
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
/// .initialization_script_for_all_frames(INIT_SCRIPT)
/// .build()?;
/// Ok(())
/// });
/// }
/// ```
#[must_use]
pub fn initialization_script_for_all_frames(mut self, script: impl Into<String>) -> Self {
self.webview_builder = self
.webview_builder
.initialization_script_for_all_frames(script);
self
}
/// Set the user agent for the webview
#[must_use]
pub fn user_agent(mut self, user_agent: &str) -> Self {
self.webview_builder = self.webview_builder.user_agent(user_agent);
self
}
/// Set additional arguments for the webview.
///
/// ## Platform-specific
///
/// - **macOS / Linux / Android / iOS**: Unsupported.
///
/// ## Warning
///
/// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
/// so if you use this method, you also need to disable these components by yourself if you want.
#[must_use]
pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
self.webview_builder = self
.webview_builder
.additional_browser_args(additional_args);
self
}
/// Data directory for the webview.
#[must_use]
pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
self.webview_builder = self.webview_builder.data_directory(data_directory);
self
}
/// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
#[must_use]
pub fn disable_drag_drop_handler(mut self) -> Self {
self.webview_builder = self.webview_builder.disable_drag_drop_handler();
self
}
/// Enables clipboard access for the page rendered on **Linux** and **Windows**.
///
/// **macOS** doesn't provide such method and is always enabled by default,
/// but you still need to add menu item accelerators to use shortcuts.
#[must_use]
pub fn enable_clipboard_access(mut self) -> Self {
self.webview_builder = self.webview_builder.enable_clipboard_access();
self
}
/// Enable or disable incognito mode for the WebView..
///
/// ## Platform-specific:
///
/// **Android**: Unsupported.
#[must_use]
pub fn incognito(mut self, incognito: bool) -> Self {
self.webview_builder = self.webview_builder.incognito(incognito);
self
}
/// Sets the webview to automatically grow and shrink its size and position when the parent window resizes.
#[must_use]
pub fn auto_resize(mut self) -> Self {
self.webview_builder = self.webview_builder.auto_resize();
self
}
/// Set a proxy URL for the WebView for all network requests.
///
/// Must be either a `http://` or a `socks5://` URL.
#[must_use]
pub fn proxy_url(mut self, url: Url) -> Self {
self.webview_builder = self.webview_builder.proxy_url(url);
self
}
/// Whether the window should be transparent. If this is true, writing colors
/// with alpha values different than `1.0` will produce a transparent window.
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
#[cfg_attr(
docsrs,
doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
)]
#[must_use]
pub fn transparent(mut self, transparent: bool) -> Self {
#[cfg(desktop)]
{
self.window_builder = self.window_builder.transparent(transparent);
}
self.webview_builder = self.webview_builder.transparent(transparent);
self
}
/// Whether page zooming by hotkeys and mousewheel should be enabled or not.
///
/// ## Platform-specific:
///
/// - **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.
/// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `Ctrl/Cmd + [- = +]` hotkeys or mousewheel events,
/// 20% in each step, ranging from 20% to 1000%. Requires `core:webview:allow-set-webview-zoom` permission
///
/// - **Android / iOS**: Unsupported.
#[must_use]
pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self {
self.webview_builder = self.webview_builder.zoom_hotkeys_enabled(enabled);
self
}
/// Whether browser extensions can be installed for the webview process
///
/// ## Platform-specific:
///
/// - **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)
/// - **MacOS / Linux / iOS / Android** - Unsupported.
#[must_use]
pub fn browser_extensions_enabled(mut self, enabled: bool) -> Self {
self.webview_builder = self.webview_builder.browser_extensions_enabled(enabled);
self
}
/// 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.
///
/// ## Platform-specific:
///
/// - **Windows**: Browser extensions must first be enabled. See [`browser_extensions_enabled`](Self::browser_extensions_enabled)
/// - **MacOS / iOS / Android** - Unsupported.
#[must_use]
pub fn extensions_path(mut self, path: impl AsRef<Path>) -> Self {
self.webview_builder = self.webview_builder.extensions_path(path);
self
}
/// Initialize the WebView with a custom data store identifier.
/// Can be used as a replacement for data_directory not being available in WKWebView.
///
/// - **macOS / iOS**: Available on macOS >= 14 and iOS >= 17
/// - **Windows / Linux / Android**: Unsupported.
#[must_use]
pub fn data_store_identifier(mut self, data_store_identifier: [u8; 16]) -> Self {
self.webview_builder = self
.webview_builder
.data_store_identifier(data_store_identifier);
self
}
/// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
///
/// ## Note
///
/// 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.
///
/// ## Warning
///
/// 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.
#[must_use]
pub fn use_https_scheme(mut self, enabled: bool) -> Self {
self.webview_builder = self.webview_builder.use_https_scheme(enabled);
self
}
/// Whether web inspector, which is usually called browser devtools, is enabled or not. Enabled by default.
///
/// This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds.
///
/// ## Platform-specific
///
/// - macOS: This will call private functions on **macOS**.
/// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
/// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
#[must_use]
pub fn devtools(mut self, enabled: bool) -> Self {
self.webview_builder = self.webview_builder.devtools(enabled);
self
}
/// Set the window and webview background color.
///
/// ## Platform-specific:
///
/// - **Android / iOS:** Unsupported for the window layer.
/// - **macOS / iOS**: Not implemented for the webview layer.
/// - **Windows**:
/// - alpha channel is ignored for the window layer.
/// - On Windows 7, alpha channel is ignored for the webview layer.
/// - On Windows 8 and newer, if alpha channel is not `0`, it will be ignored.
#[must_use]
pub fn background_color(mut self, color: Color) -> Self {
self.window_builder = self.window_builder.background_color(color);
self.webview_builder = self.webview_builder.background_color(color);
self
}
/// Change the default background throttling behaviour.
///
/// By default, browsers use a suspend policy that will throttle timers and even unload
/// the whole tab (view) to free resources after roughly 5 minutes when a view became
/// minimized or hidden. This will pause all tasks until the documents visibility state
/// changes back from hidden to visible by bringing the view back to the foreground.
///
/// ## Platform-specific
///
/// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
/// - **iOS**: Supported since version 17.0+.
/// - **macOS**: Supported since version 14.0+.
///
/// see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578
#[must_use]
pub fn background_throttling(mut self, policy: BackgroundThrottlingPolicy) -> Self {
self.webview_builder = self.webview_builder.background_throttling(policy);
self
}
/// Whether JavaScript should be disabled.
#[must_use]
pub fn disable_javascript(mut self) -> Self {
self.webview_builder = self.webview_builder.disable_javascript();
self
}
/// Specifies the native scrollbar style to use with the webview.
/// CSS styles that modifier the scrollbar are applied on top of the native appearance configured here.
///
/// Defaults to [`ScrollBarStyle::Default`], which is the browser default.
///
/// ## Platform-specific
///
/// - **Windows**:
/// - [`ScrollBarStyle::FluentOverlay`] requires WebView2 Runtime version 125.0.2535.41 or higher,
/// and does nothing on older versions.
/// - This option must be given the same value for all webviews that target the same data directory. Use
/// [`WebviewWindowBuilder::data_directory`] to change data directories if needed.
/// - **Linux / Android / iOS / macOS**: Unsupported. Only supports `Default` and performs no operation.
#[must_use]
pub fn scroll_bar_style(mut self, style: ScrollBarStyle) -> Self {
self.webview_builder = self.webview_builder.scroll_bar_style(style);
self
}
/// Allows overriding the keyboard accessory view on iOS.
/// Returning `None` effectively removes the view.
///
/// The closure parameter is the webview instance.
///
/// The accessory view is the view that appears above the keyboard when a text input element is focused.
/// It usually displays a view with "Done", "Next" buttons.
///
/// # Examples
///
/// ```
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let mut builder = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()));
/// #[cfg(target_os = "ios")]
/// {
/// window_builder = window_builder.with_input_accessory_view_builder(|_webview| unsafe {
/// let mtm = objc2_foundation::MainThreadMarker::new_unchecked();
/// let button = objc2_ui_kit::UIButton::buttonWithType(objc2_ui_kit::UIButtonType(1), mtm);
/// button.setTitle_forState(
/// Some(&objc2_foundation::NSString::from_str("Tauri")),
/// objc2_ui_kit::UIControlState(0),
/// );
/// Some(button.downcast().unwrap())
/// });
/// }
/// let webview = builder.build()?;
/// Ok(())
/// });
/// }
/// ```
///
/// # Stability
///
/// This relies on [`objc2_ui_kit`] which does not provide a stable API yet, so it can receive breaking changes in minor releases.
#[cfg(target_os = "ios")]
pub fn with_input_accessory_view_builder<
F: Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>
+ Send
+ Sync
+ 'static,
>(
mut self,
builder: F,
) -> Self {
self.webview_builder = self
.webview_builder
.with_input_accessory_view_builder(builder);
self
}
/// Set the environment for the webview.
/// Useful if you need to share the same environment, for instance when using the [`Self::on_new_window`].
#[cfg(all(feature = "wry", windows))]
pub fn with_environment(
mut self,
environment: webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Environment,
) -> Self {
self.webview_builder = self.webview_builder.with_environment(environment);
self
}
/// Creates a new webview sharing the same web process with the provided webview.
/// Useful if you need to link a webview to another, for instance when using the [`Self::on_new_window`].
#[cfg(all(
feature = "wry",
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
)
))]
pub fn with_related_view(mut self, related_view: webkit2gtk::WebView) -> Self {
self.webview_builder = self.webview_builder.with_related_view(related_view);
self
}
/// Set the webview configuration.
/// Useful if you need to share the same webview configuration, for instance when using the [`Self::on_new_window`].
#[cfg(target_os = "macos")]
pub fn with_webview_configuration(
mut self,
webview_configuration: objc2::rc::Retained<objc2_web_kit::WKWebViewConfiguration>,
) -> Self {
self.webview_builder = self
.webview_builder
.with_webview_configuration(webview_configuration);
self
}
/// Set the window features.
/// Useful if you need to share the same window features, for instance when using the [`Self::on_new_window`].
#[cfg(any(
target_os = "macos",
windows,
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn window_features(mut self, features: NewWindowFeatures) -> Self {
if let Some(position) = features.position() {
self.window_builder = self.window_builder.position(position.x, position.y);
}
if let Some(size) = features.size() {
self.window_builder = self.window_builder.inner_size(size.width, size.height);
}
#[cfg(target_os = "macos")]
{
self.webview_builder = self
.webview_builder
.with_webview_configuration(features.opener().target_configuration.clone());
}
#[cfg(all(feature = "wry", windows))]
{
self.webview_builder = self
.webview_builder
.with_environment(features.opener().environment.clone());
}
#[cfg(all(
feature = "wry",
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
)
))]
{
self.webview_builder = self
.webview_builder
.with_related_view(features.opener().webview.clone());
}
self
}
}
/// A type that wraps a [`Window`] together with a [`Webview`].
#[default_runtime(crate::Wry, wry)]
#[derive(Debug)]
pub struct WebviewWindow<R: Runtime> {
pub(crate) window: Window<R>,
pub(crate) webview: Webview<R>,
}
impl<R: Runtime> AsRef<Webview<R>> for WebviewWindow<R> {
fn as_ref(&self) -> &Webview<R> {
&self.webview
}
}
impl<R: Runtime> Clone for WebviewWindow<R> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
webview: self.webview.clone(),
}
}
}
impl<R: Runtime> Eq for WebviewWindow<R> {}
impl<R: Runtime> PartialEq for WebviewWindow<R> {
/// Only use the [`Webview`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.webview.eq(&other.webview)
}
}
impl<R: Runtime> raw_window_handle::HasWindowHandle for WebviewWindow<R> {
fn window_handle(
&self,
) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
Ok(unsafe {
raw_window_handle::WindowHandle::borrow_raw(self.window.window_handle()?.as_raw())
})
}
}
impl<R: Runtime> raw_window_handle::HasDisplayHandle for WebviewWindow<R> {
fn display_handle(
&self,
) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
self.webview.app_handle.display_handle()
}
}
impl<'de, R: Runtime> CommandArg<'de, R> for WebviewWindow<R> {
/// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
let webview = command.message.webview();
let window = webview.window();
if window.is_webview_window() {
return Ok(Self { window, webview });
}
Err(InvokeError::from("current webview is not a WebviewWindow"))
}
}
/// Base webview window functions.
impl<R: Runtime> WebviewWindow<R> {
/// Initializes a [`WebviewWindowBuilder`] with the given window label and webview URL.
///
/// Data URLs are only supported with the `webview-data-url` feature flag.
pub fn builder<M: Manager<R>, L: Into<String>>(
manager: &M,
label: L,
url: WebviewUrl,
) -> WebviewWindowBuilder<'_, R, M> {
WebviewWindowBuilder::new(manager, label, url)
}
/// Runs the given closure on the main thread.
pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
self.webview.run_on_main_thread(f)
}
/// The webview label.
pub fn label(&self) -> &str {
self.webview.label()
}
/// Registers a window event listener.
pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
self.window.on_window_event(f);
}
/// Registers a webview event listener.
pub fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) {
self.webview.on_webview_event(f);
}
/// Resolves the given command scope for this webview on the currently loaded URL.
///
/// If the command is not allowed, returns None.
///
/// If the scope cannot be deserialized to the given type, an error is returned.
///
/// In a command context this can be directly resolved from the command arguments via [crate::ipc::CommandScope]:
///
/// ```
/// use tauri::ipc::CommandScope;
///
/// #[derive(Debug, serde::Deserialize)]
/// struct ScopeType {
/// some_value: String,
/// }
/// #[tauri::command]
/// fn my_command(scope: CommandScope<ScopeType>) {
/// // check scope
/// }
/// ```
///
/// # Examples
///
/// ```
/// use tauri::Manager;
///
/// #[derive(Debug, serde::Deserialize)]
/// struct ScopeType {
/// some_value: String,
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview = app.get_webview_window("main").unwrap();
/// let scope = webview.resolve_command_scope::<ScopeType>("my-plugin", "read");
/// Ok(())
/// });
/// ```
pub fn resolve_command_scope<T: ScopeObject>(
&self,
plugin: &str,
command: &str,
) -> crate::Result<Option<ResolvedScope<T>>> {
self.webview.resolve_command_scope(plugin, command)
}
}
/// Menu APIs
#[cfg(desktop)]
impl<R: Runtime> WebviewWindow<R> {
/// Registers a global menu event listener.
///
/// Note that this handler is called for any menu event,
/// whether it is coming from this window, another window or from the tray icon menu.
///
/// Also note that this handler will not be called if
/// the window used to register it was closed.
///
/// # Examples
///
/// ```
/// use tauri::menu::{Menu, Submenu, MenuItem};
/// use tauri::{WebviewWindowBuilder, WebviewUrl};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle();
/// let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
/// let menu = Menu::with_items(handle, &[
/// &Submenu::with_items(handle, "File", true, &[
/// &save_menu_item,
/// ])?,
/// ])?;
/// let webview_window = WebviewWindowBuilder::new(app, "editor", WebviewUrl::default())
/// .menu(menu)
/// .build()
/// .unwrap();
///
/// webview_window.on_menu_event(move |window, event| {
/// if event.id == save_menu_item.id() {
/// // save menu item
/// }
/// });
///
/// Ok(())
/// });
/// ```
pub fn on_menu_event<F: Fn(&crate::Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
&self,
f: F,
) {
self.window.on_menu_event(f)
}
/// Returns this window menu.
pub fn menu(&self) -> Option<Menu<R>> {
self.window.menu()
}
/// Sets the window menu and returns the previous one.
///
/// ## Platform-specific:
///
/// - **macOS:** Unsupported. The menu on macOS is app-wide and not specific to one
/// window, if you need to set it, use [`AppHandle::set_menu`] instead.
#[cfg_attr(target_os = "macos", allow(unused_variables))]
pub fn set_menu(&self, menu: Menu<R>) -> crate::Result<Option<Menu<R>>> {
self.window.set_menu(menu)
}
/// Removes the window menu and returns it.
///
/// ## Platform-specific:
///
/// - **macOS:** Unsupported. The menu on macOS is app-wide and not specific to one
/// window, if you need to remove it, use [`AppHandle::remove_menu`] instead.
pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
self.window.remove_menu()
}
/// Hides the window menu.
pub fn hide_menu(&self) -> crate::Result<()> {
self.window.hide_menu()
}
/// Shows the window menu.
pub fn show_menu(&self) -> crate::Result<()> {
self.window.show_menu()
}
/// Shows the window menu.
pub fn is_menu_visible(&self) -> crate::Result<bool> {
self.window.is_menu_visible()
}
/// Shows the specified menu as a context menu at the cursor position.
pub fn popup_menu<M: ContextMenu>(&self, menu: &M) -> crate::Result<()> {
self.window.popup_menu(menu)
}
/// Shows the specified menu as a context menu at the specified position.
///
/// The position is relative to the window's top-left corner.
pub fn popup_menu_at<M: ContextMenu, P: Into<Position>>(
&self,
menu: &M,
position: P,
) -> crate::Result<()> {
self.window.popup_menu_at(menu, position)
}
}
/// Window getters.
impl<R: Runtime> WebviewWindow<R> {
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
pub fn scale_factor(&self) -> crate::Result<f64> {
self.window.scale_factor()
}
/// 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.
pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.inner_position()
}
/// Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.outer_position()
}
/// Returns the physical size of the window's client area.
///
/// The client area is the content of the window, excluding the title bar and borders.
pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.inner_size()
}
/// Returns the physical size of the entire window.
///
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.outer_size()
}
/// Gets the window's current fullscreen state.
pub fn is_fullscreen(&self) -> crate::Result<bool> {
self.window.is_fullscreen()
}
/// Gets the window's current minimized state.
pub fn is_minimized(&self) -> crate::Result<bool> {
self.window.is_minimized()
}
/// Gets the window's current maximized state.
pub fn is_maximized(&self) -> crate::Result<bool> {
self.window.is_maximized()
}
/// Gets the window's current focus state.
pub fn is_focused(&self) -> crate::Result<bool> {
self.window.is_focused()
}
/// Gets the window's current decoration state.
pub fn is_decorated(&self) -> crate::Result<bool> {
self.window.is_decorated()
}
/// Gets the window's current resizable state.
pub fn is_resizable(&self) -> crate::Result<bool> {
self.window.is_resizable()
}
/// Whether the window is enabled or disabled.
pub fn is_enabled(&self) -> crate::Result<bool> {
self.webview.window().is_enabled()
}
/// Determines if this window should always be on top of other windows.
///
/// ## Platform-specific
///
/// - **iOS / Android:** Unsupported.
pub fn is_always_on_top(&self) -> crate::Result<bool> {
self.webview.window().is_always_on_top()
}
/// Gets the window's native maximize button state
///
/// ## Platform-specific
///
/// - **Linux / iOS / Android:** Unsupported.
pub fn is_maximizable(&self) -> crate::Result<bool> {
self.window.is_maximizable()
}
/// Gets the window's native minimize button state
///
/// ## Platform-specific
///
/// - **Linux / iOS / Android:** Unsupported.
pub fn is_minimizable(&self) -> crate::Result<bool> {
self.window.is_minimizable()
}
/// Gets the window's native close button state
///
/// ## Platform-specific
///
/// - **Linux / iOS / Android:** Unsupported.
pub fn is_closable(&self) -> crate::Result<bool> {
self.window.is_closable()
}
/// Gets the window's current visibility state.
pub fn is_visible(&self) -> crate::Result<bool> {
self.window.is_visible()
}
/// Gets the window's current title.
pub fn title(&self) -> crate::Result<String> {
self.window.title()
}
/// Returns the monitor on which the window currently resides.
///
/// Returns None if current monitor can't be detected.
pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.current_monitor()
}
/// Returns the primary monitor of the system.
///
/// Returns None if it can't identify any monitor as a primary one.
pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.primary_monitor()
}
/// Returns the monitor that contains the given point.
pub fn monitor_from_point(&self, x: f64, y: f64) -> crate::Result<Option<Monitor>> {
self.window.monitor_from_point(x, y)
}
/// Returns the list of all the monitors available on the system.
pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
self.window.available_monitors()
}
/// Returns the native handle that is used by this window.
#[cfg(target_os = "macos")]
pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
self.window.ns_window()
}
/// Returns the pointer to the content view of this window.
#[cfg(target_os = "macos")]
pub fn ns_view(&self) -> crate::Result<*mut std::ffi::c_void> {
self.window.ns_view()
}
/// Returns the native handle that is used by this window.
#[cfg(windows)]
pub fn hwnd(&self) -> crate::Result<HWND> {
self.window.hwnd()
}
/// Returns the `ApplicationWindow` from gtk crate that is used by this window.
///
/// Note that this type can only be used on the main thread.
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
self.window.gtk_window()
}
/// Returns the vertical [`gtk::Box`] that is added by default as the sole child of this window.
///
/// Note that this type can only be used on the main thread.
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn default_vbox(&self) -> crate::Result<gtk::Box> {
self.window.default_vbox()
}
/// Returns the current window theme.
///
/// ## Platform-specific
///
/// - **macOS**: Only supported on macOS 10.14+.
pub fn theme(&self) -> crate::Result<crate::Theme> {
self.window.theme()
}
}
/// Desktop window getters.
#[cfg(desktop)]
impl<R: Runtime> WebviewWindow<R> {
/// Get the cursor position relative to the top-left hand corner of the desktop.
///
/// Note that the top-left hand corner of the desktop is not necessarily the same as the screen.
/// If the user uses a desktop with multiple monitors,
/// the top-left hand corner of the desktop is the top-left hand corner of the main monitor on Windows and macOS
/// or the top-left of the leftmost monitor on X11.
///
/// The coordinates can be negative if the top-left hand corner of the window is outside of the visible screen region.
pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
self.webview.cursor_position()
}
}
/// Desktop window setters and actions.
#[cfg(desktop)]
impl<R: Runtime> WebviewWindow<R> {
/// Centers the window.
pub fn center(&self) -> crate::Result<()> {
self.window.center()
}
/// Requests user attention to the window, this has no effect if the application
/// is already focused. How requesting for user attention manifests is platform dependent,
/// see `UserAttentionType` for details.
///
/// Providing `None` will unset the request for user attention. Unsetting the request for
/// user attention might not be done automatically by the WM when the window receives input.
///
/// ## Platform-specific
///
/// - **macOS:** `None` has no effect.
/// - **Linux:** Urgency levels have the same effect.
pub fn request_user_attention(
&self,
request_type: Option<UserAttentionType>,
) -> crate::Result<()> {
self.window.request_user_attention(request_type)
}
/// Determines if this window should be resizable.
/// When resizable is set to false, native window's maximize button is automatically disabled.
pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
self.window.set_resizable(resizable)
}
/// Enable or disable the window.
pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
self.webview.window().set_enabled(enabled)
}
/// Determines if this window's native maximize button should be enabled.
/// If resizable is set to false, this setting is ignored.
///
/// ## Platform-specific
///
/// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
/// - **Linux / iOS / Android:** Unsupported.
pub fn set_maximizable(&self, maximizable: bool) -> crate::Result<()> {
self.window.set_maximizable(maximizable)
}
/// Determines if this window's native minimize button should be enabled.
///
/// ## Platform-specific
///
/// - **Linux / iOS / Android:** Unsupported.
pub fn set_minimizable(&self, minimizable: bool) -> crate::Result<()> {
self.window.set_minimizable(minimizable)
}
/// Determines if this window's native close button should be enabled.
///
/// ## Platform-specific
///
/// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button.
/// Depending on the system, this function may not have any effect when called on a window that is already visible"
/// - **iOS / Android:** Unsupported.
pub fn set_closable(&self, closable: bool) -> crate::Result<()> {
self.window.set_closable(closable)
}
/// Set this window's title.
pub fn set_title(&self, title: &str) -> crate::Result<()> {
self.window.set_title(title)
}
/// Maximizes this window.
pub fn maximize(&self) -> crate::Result<()> {
self.window.maximize()
}
/// Un-maximizes this window.
pub fn unmaximize(&self) -> crate::Result<()> {
self.window.unmaximize()
}
/// Minimizes this window.
pub fn minimize(&self) -> crate::Result<()> {
self.window.minimize()
}
/// Un-minimizes this window.
pub fn unminimize(&self) -> crate::Result<()> {
self.window.unminimize()
}
/// Show this window.
pub fn show(&self) -> crate::Result<()> {
self.window.show()
}
/// Hide this window.
pub fn hide(&self) -> crate::Result<()> {
self.window.hide()
}
/// Closes this window. It emits [`crate::RunEvent::CloseRequested`] first like a user-initiated close request so you can intercept it.
pub fn close(&self) -> crate::Result<()> {
self.window.close()
}
/// Destroys this window. Similar to [`Self::close`] but does not emit any events and force close the window instead.
pub fn destroy(&self) -> crate::Result<()> {
self.window.destroy()
}
/// Determines if this window should be [decorated].
///
/// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration
pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
self.window.set_decorations(decorations)
}
/// Determines if this window should have shadow.
///
/// ## Platform-specific
///
/// - **Windows:**
/// - `false` has no effect on decorated window, shadow are always ON.
/// - `true` will make undecorated window have a 1px white border,
/// and on Windows 11, it will have a rounded corners.
/// - **Linux:** Unsupported.
pub fn set_shadow(&self, enable: bool) -> crate::Result<()> {
self.window.set_shadow(enable)
}
/// Sets window effects, pass [`None`] to clear any effects applied if possible.
///
/// Requires the window to be transparent.
///
/// See [`crate::window::EffectsBuilder`] for a convenient builder for [`crate::utils::config::WindowEffectsConfig`].
///
///
/// ```rust,no_run
/// use tauri::{Manager, window::{Color, Effect, EffectState, EffectsBuilder}};
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = app.get_webview_window("main").unwrap();
/// webview_window.set_effects(
/// EffectsBuilder::new()
/// .effect(Effect::Popover)
/// .state(EffectState::Active)
/// .radius(5.)
/// .color(Color(0, 0, 0, 255))
/// .build(),
/// )?;
/// Ok(())
/// });
/// ```
///
/// ## Platform-specific:
///
/// - **Windows**: If using decorations or shadows, you may want to try this workaround <https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891>
/// - **Linux**: Unsupported
pub fn set_effects<E: Into<Option<crate::utils::config::WindowEffectsConfig>>>(
&self,
effects: E,
) -> crate::Result<()> {
self.window.set_effects(effects)
}
/// Determines if this window should always be below other windows.
pub fn set_always_on_bottom(&self, always_on_bottom: bool) -> crate::Result<()> {
self.window.set_always_on_bottom(always_on_bottom)
}
/// Determines if this window should always be on top of other windows.
pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
self.window.set_always_on_top(always_on_top)
}
/// Sets whether the window should be visible on all workspaces or virtual desktops.
pub fn set_visible_on_all_workspaces(
&self,
visible_on_all_workspaces: bool,
) -> crate::Result<()> {
self
.window
.set_visible_on_all_workspaces(visible_on_all_workspaces)
}
/// Prevents the window contents from being captured by other apps.
pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> {
self.window.set_content_protected(protected)
}
/// Resizes this window.
pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
self.window.set_size(size.into())
}
/// Sets this window's minimum inner size.
pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self.window.set_min_size(size.map(|s| s.into()))
}
/// Sets this window's maximum inner size.
pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self.window.set_max_size(size.map(|s| s.into()))
}
/// Sets this window's minimum inner width.
pub fn set_size_constraints(
&self,
constraints: tauri_runtime::window::WindowSizeConstraints,
) -> crate::Result<()> {
self.window.set_size_constraints(constraints)
}
/// Sets this window's position.
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self.window.set_position(position)
}
/// Determines if this window should be fullscreen.
pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
self.window.set_fullscreen(fullscreen)
}
/// Toggles a fullscreen mode that doesn't require a new macOS space.
/// Returns a boolean indicating whether the transition was successful (this won't work if the window was already in the native fullscreen).
///
/// This is how fullscreen used to work on macOS in versions before Lion.
/// And allows the user to have a fullscreen window without using another space or taking control over the entire monitor.
///
/// ## Platform-specific
///
/// - **macOS:** Uses native simple fullscreen mode.
/// - **Other platforms:** Falls back to [`Self::set_fullscreen`].
pub fn set_simple_fullscreen(&self, enable: bool) -> crate::Result<()> {
self.window.set_simple_fullscreen(enable)
}
/// Bring the window to front and focus.
pub fn set_focus(&self) -> crate::Result<()> {
self.window.set_focus()
}
/// Sets whether the window can be focused.
///
/// ## Platform-specific
///
/// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`.
/// 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.
pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> {
self.window.set_focusable(focusable)
}
/// Sets this window' icon.
pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> {
self.window.set_icon(icon)
}
/// Sets the window background color.
///
/// ## Platform-specific:
///
/// - **iOS / Android:** Unsupported.
/// - **macOS**: Not implemented for the webview layer..
/// - **Windows**:
/// - alpha channel is ignored for the window layer.
/// - On Windows 7, transparency is not supported and the alpha value will be ignored for the webview layer..
/// - 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.
pub fn set_background_color(&self, color: Option<Color>) -> crate::Result<()> {
self.window.set_background_color(color)?;
self.webview.set_background_color(color)
}
/// Whether to hide the window icon from the taskbar or not.
///
/// ## Platform-specific
///
/// - **macOS:** Unsupported.
pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
self.window.set_skip_taskbar(skip)
}
/// Grabs the cursor, preventing it from leaving the window.
///
/// There's no guarantee that the cursor will be hidden. You should
/// hide it by yourself if you want so.
///
/// ## Platform-specific
///
/// - **Linux:** Unsupported.
/// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
self.window.set_cursor_grab(grab)
}
/// Modifies the cursor's visibility.
///
/// If `false`, this will hide the cursor. If `true`, this will show the cursor.
///
/// ## Platform-specific
///
/// - **Windows:** The cursor is only hidden within the confines of the window.
/// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
/// outside of the window.
pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
self.window.set_cursor_visible(visible)
}
/// Modifies the cursor icon of the window.
pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
self.window.set_cursor_icon(icon)
}
/// Changes the position of the cursor in window coordinates.
pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self.window.set_cursor_position(position)
}
/// Ignores the window cursor events.
pub fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
self.window.set_ignore_cursor_events(ignore)
}
/// Starts dragging the window.
pub fn start_dragging(&self) -> crate::Result<()> {
self.window.start_dragging()
}
/// Sets the overlay icon on the taskbar **Windows only**. Using `None` will remove the icon
///
/// The overlay icon can be unique for each window.
#[cfg(target_os = "windows")]
#[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
self.window.set_overlay_icon(icon)
}
/// Sets the taskbar badge count. Using `0` or `None` will remove the badge
///
/// ## Platform-specific
/// - **Windows:** Unsupported, use [`WebviewWindow::set_overlay_icon`] instead.
/// - **iOS:** iOS expects i32, the value will be clamped to i32::MIN, i32::MAX.
/// - **Android:** Unsupported.
pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
self.window.set_badge_count(count)
}
/// Sets the taskbar badge label **macOS only**. Using `None` will remove the badge
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
self.window.set_badge_label(label)
}
/// Sets the taskbar progress state.
///
/// ## Platform-specific
///
/// - **Linux / macOS**: Progress bar is app-wide and not specific to this window.
/// - **Linux**: Only supported desktop environments with `libunity` (e.g. GNOME).
/// - **iOS / Android:** Unsupported.
pub fn set_progress_bar(
&self,
progress_state: crate::window::ProgressBarState,
) -> crate::Result<()> {
self.window.set_progress_bar(progress_state)
}
/// Sets the title bar style. **macOS only**.
pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
self.window.set_title_bar_style(style)
}
/// Sets the theme for this window.
///
/// ## Platform-specific
///
/// - **Linux / macOS**: Theme is app-wide and not specific to this window.
/// - **iOS / Android:** Unsupported.
pub fn set_theme(&self, theme: Option<tauri_utils::Theme>) -> crate::Result<()> {
self.window.set_theme(theme)
}
}
/// Desktop webview setters and actions.
#[cfg(desktop)]
impl<R: Runtime> WebviewWindow<R> {
/// Opens the dialog to prints the contents of the webview.
/// Currently only supported on macOS on `wry`.
/// `window.print()` works on all platforms.
pub fn print(&self) -> crate::Result<()> {
self.webview.print()
}
}
/// Webview APIs.
impl<R: Runtime> WebviewWindow<R> {
/// Executes a closure, providing it with the webview handle that is specific to the current platform.
///
/// The closure is executed on the main thread.
///
/// Note that `webview2-com`, `webkit2gtk`, `objc2_web_kit` and similar crates may be updated in minor releases of Tauri.
/// Therefore it's recommended to pin Tauri to at least a minor version when you're using `with_webview`.
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::Manager;
///
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let main_webview = app.get_webview_window("main").unwrap();
/// main_webview.with_webview(|webview| {
/// #[cfg(target_os = "linux")]
/// {
/// // see <https://docs.rs/webkit2gtk/2.0.0/webkit2gtk/struct.WebView.html>
/// // and <https://docs.rs/webkit2gtk/2.0.0/webkit2gtk/trait.WebViewExt.html>
/// use webkit2gtk::WebViewExt;
/// webview.inner().set_zoom_level(4.);
/// }
///
/// #[cfg(windows)]
/// unsafe {
/// // see <https://docs.rs/webview2-com/0.19.1/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html>
/// webview.controller().SetZoomFactor(4.).unwrap();
/// }
///
/// #[cfg(target_os = "macos")]
/// unsafe {
/// let view: &objc2_web_kit::WKWebView = &*webview.inner().cast();
/// let controller: &objc2_web_kit::WKUserContentController = &*webview.controller().cast();
/// let window: &objc2_app_kit::NSWindow = &*webview.ns_window().cast();
///
/// view.setPageZoom(4.);
/// controller.removeAllUserScripts();
/// let bg_color = objc2_app_kit::NSColor::colorWithDeviceRed_green_blue_alpha(0.5, 0.2, 0.4, 1.);
/// window.setBackgroundColor(Some(&bg_color));
/// }
///
/// #[cfg(target_os = "android")]
/// {
/// use jni::objects::JValue;
/// webview.jni_handle().exec(|env, _, webview| {
/// env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap();
/// })
/// }
/// });
/// Ok(())
/// });
/// }
/// ```
#[allow(clippy::needless_doctest_main)] // To avoid a large diff
#[cfg(feature = "wry")]
#[cfg_attr(docsrs, doc(feature = "wry"))]
pub fn with_webview<F: FnOnce(crate::webview::PlatformWebview) + Send + 'static>(
&self,
f: F,
) -> crate::Result<()> {
self.webview.with_webview(f)
}
/// Returns the current url of the webview.
pub fn url(&self) -> crate::Result<Url> {
self.webview.url()
}
/// Navigates the webview to the defined url.
pub fn navigate(&self, url: Url) -> crate::Result<()> {
self.webview.navigate(url)
}
/// Reloads the current page.
pub fn reload(&self) -> crate::Result<()> {
self.webview.reload()
}
/// Handles this window receiving an [`crate::webview::InvokeRequest`].
pub fn on_message(
self,
request: crate::webview::InvokeRequest,
responder: Box<OwnedInvokeResponder<R>>,
) {
self.webview.on_message(request, responder)
}
/// Evaluates JavaScript on this window.
pub fn eval(&self, js: impl Into<String>) -> crate::Result<()> {
self.webview.eval(js)
}
/// Opens the developer tools window (Web Inspector).
/// The devtools is only enabled on debug builds or with the `devtools` feature flag.
///
/// ## Platform-specific
///
/// - **macOS:** Only supported on macOS 10.15+.
/// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(debug_assertions)]
/// app.get_webview_window("main").unwrap().open_devtools();
/// Ok(())
/// });
/// ```
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn open_devtools(&self) {
self.webview.open_devtools();
}
/// Closes the developer tools window (Web Inspector).
/// The devtools is only enabled on debug builds or with the `devtools` feature flag.
///
/// ## Platform-specific
///
/// - **macOS:** Only supported on macOS 10.15+.
/// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
/// - **Windows:** Unsupported.
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(debug_assertions)]
/// {
/// let webview = app.get_webview_window("main").unwrap();
/// webview.open_devtools();
/// std::thread::spawn(move || {
/// std::thread::sleep(std::time::Duration::from_secs(10));
/// webview.close_devtools();
/// });
/// }
/// Ok(())
/// });
/// ```
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn close_devtools(&self) {
self.webview.close_devtools();
}
/// Checks if the developer tools window (Web Inspector) is opened.
/// The devtools is only enabled on debug builds or with the `devtools` feature flag.
///
/// ## Platform-specific
///
/// - **macOS:** Only supported on macOS 10.15+.
/// This is a private API on macOS, so you cannot use this if your application will be published on the App Store.
/// - **Windows:** Unsupported.
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(debug_assertions)]
/// {
/// let webview = app.get_webview_window("main").unwrap();
/// if !webview.is_devtools_open() {
/// webview.open_devtools();
/// }
/// }
/// Ok(())
/// });
/// ```
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn is_devtools_open(&self) -> bool {
self.webview.is_devtools_open()
}
/// Set the webview zoom level
///
/// ## Platform-specific:
///
/// - **Android**: Not supported.
/// - **macOS**: available on macOS 11+ only.
/// - **iOS**: available on iOS 14+ only.
pub fn set_zoom(&self, scale_factor: f64) -> crate::Result<()> {
self.webview.set_zoom(scale_factor)
}
/// Clear all browsing data for this webview window.
pub fn clear_all_browsing_data(&self) -> crate::Result<()> {
self.webview.clear_all_browsing_data()
}
/// Returns all cookies in the runtime's cookie store including HTTP-only and secure cookies.
///
/// Note that cookies will only be returned for URLs with an http or https scheme.
/// Cookies set through javascript for local files
/// (such as those served from the tauri://) protocol are not currently supported.
///
/// # Stability
///
/// See [Self::cookies].
///
/// # Known issues
///
/// See [Self::cookies].
pub fn cookies_for_url(&self, url: Url) -> crate::Result<Vec<Cookie<'static>>> {
self.webview.cookies_for_url(url)
}
/// Returns all cookies in the runtime's cookie store for all URLs including HTTP-only and secure cookies.
///
/// Note that cookies will only be returned for URLs with an http or https scheme.
/// Cookies set through javascript for local files
/// (such as those served from the tauri://) protocol are not currently supported.
///
/// # Stability
///
/// The return value of this function leverages [`tauri_runtime::Cookie`] which re-exports the cookie crate.
/// This dependency might receive updates in minor Tauri releases.
///
/// # Known issues
///
/// On Windows, this function deadlocks when used in a synchronous command or event handlers, see [the Webview2 issue].
/// You should use `async` commands and separate threads when reading cookies.
///
/// ## Platform-specific
///
/// - **Android**: Unsupported, always returns an empty [`Vec`].
///
/// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
pub fn cookies(&self) -> crate::Result<Vec<Cookie<'static>>> {
self.webview.cookies()
}
/// Set a cookie for the webview.
///
/// # Stability
///
/// See [Self::cookies].
pub fn set_cookie(&self, cookie: Cookie<'_>) -> crate::Result<()> {
self.webview.set_cookie(cookie)
}
/// Delete a cookie for the webview.
///
/// # Stability
///
/// See [Self::cookies].
pub fn delete_cookie(&self, cookie: Cookie<'_>) -> crate::Result<()> {
self.webview.delete_cookie(cookie)
}
}
impl<R: Runtime> Listener<R> for WebviewWindow<R> {
/// Listen to an event on this webview window.
///
/// # Examples
///
/// ```
/// use tauri::{Manager, Listener};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = app.get_webview_window("main").unwrap();
/// webview_window.listen("component-loaded", move |event| {
/// println!("window just loaded a component");
/// });
///
/// Ok(())
/// });
/// ```
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager().listen(
event,
EventTarget::WebviewWindow {
label: self.label().to_string(),
},
handler,
)
}
/// Listen to an event on this window webview only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager().once(
event,
EventTarget::WebviewWindow {
label: self.label().to_string(),
},
handler,
)
}
/// Unlisten to an event on this webview window.
///
/// # Examples
/// ```
/// use tauri::{Manager, Listener};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = app.get_webview_window("main").unwrap();
/// let webview_window_ = webview_window.clone();
/// let handler = webview_window.listen("component-loaded", move |event| {
/// println!("webview_window just loaded a component");
///
/// // we no longer need to listen to the event
/// // we also could have used `webview_window.once` instead
/// webview_window_.unlisten(event.id());
/// });
///
/// // stop listening to the event when you do not need it anymore
/// webview_window.unlisten(handler);
///
/// Ok(())
/// });
/// ```
fn unlisten(&self, id: EventId) {
self.manager().unlisten(id)
}
}
impl<R: Runtime> Emitter<R> for WebviewWindow<R> {}
impl<R: Runtime> Manager<R> for WebviewWindow<R> {
fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
self
.webview
.resources_table
.lock()
.expect("poisoned window resources table")
}
}
impl<R: Runtime> ManagerBase<R> for WebviewWindow<R> {
fn manager(&self) -> &AppManager<R> {
self.webview.manager()
}
fn manager_owned(&self) -> Arc<AppManager<R>> {
self.webview.manager_owned()
}
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
self.webview.runtime()
}
fn managed_app_handle(&self) -> &AppHandle<R> {
self.webview.managed_app_handle()
}
}