Prechádzať zdrojové kódy

feat(core): add WindowBuilder type (#3598)

Lucas Fernandes Nogueira 3 rokov pred
rodič
commit
141133a414

+ 5 - 0
.changes/window-builder.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Added a `WindowBuilder` type.

+ 3 - 3
core/tauri-runtime/Cargo.toml

@@ -35,9 +35,9 @@ infer = "0.4"
 [target."cfg(windows)".dependencies]
 webview2-com = "0.13.0"
 
-  [target."cfg(windows)".dependencies.windows]
-  version = "0.30.0"
-  features = [ "Win32_Foundation" ]
+[target."cfg(windows)".dependencies.windows]
+version = "0.30.0"
+features = [ "Win32_Foundation" ]
 
 [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
 gtk = { version = "0.15", features = [ "v3_20" ] }

+ 1 - 1
core/tauri-runtime/src/lib.rs

@@ -361,7 +361,7 @@ pub trait Dispatch: Debug + Clone + Send + Sync + Sized + 'static {
   type Runtime: Runtime;
 
   /// The winoow builder type.
-  type WindowBuilder: WindowBuilder + Clone;
+  type WindowBuilder: WindowBuilder;
 
   /// Run a task on the main thread.
   fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()>;

+ 3 - 3
core/tauri-runtime/src/webview.rs

@@ -14,7 +14,7 @@ use windows::Win32::Foundation::HWND;
 use std::{fmt, path::PathBuf};
 
 /// The attributes used to create an webview.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct WebviewAttributes {
   pub url: WindowUrl,
   pub initialization_scripts: Vec<String>,
@@ -70,7 +70,7 @@ impl WebviewAttributes {
 /// Do **NOT** implement this trait except for use in a custom [`Runtime`](crate::Runtime).
 ///
 /// This trait is separate from [`WindowBuilder`] to prevent "accidental" implementation.
-pub trait WindowBuilderBase: fmt::Debug + Sized {}
+pub trait WindowBuilderBase: fmt::Debug + Clone + Sized {}
 
 /// A builder for all attributes related to a single webview.
 ///
@@ -97,7 +97,7 @@ pub trait WindowBuilder: WindowBuilderBase {
 
   /// Window size.
   #[must_use]
-  fn inner_size(self, min_width: f64, min_height: f64) -> Self;
+  fn inner_size(self, width: f64, height: f64) -> Self;
 
   /// Window min inner size.
   #[must_use]

+ 1 - 1
core/tauri/Cargo.toml

@@ -97,7 +97,7 @@ glib = "0.15"
 [target."cfg(target_os = \"macos\")".dependencies]
 embed_plist = "1.2"
 
-[target."cfg(windows)".dev-dependencies.windows]
+[target."cfg(windows)".dependencies.windows]
 version = "0.30.0"
 features = [ "Win32_Foundation" ]
 

+ 7 - 1
core/tauri/src/app.rs

@@ -15,7 +15,7 @@ use crate::{
   plugin::{Plugin, PluginStore},
   runtime::{
     http::{Request as HttpRequest, Response as HttpResponse},
-    webview::{WebviewAttributes, WindowBuilder},
+    webview::{WebviewAttributes, WindowBuilder as _},
     window::{PendingWindow, WindowEvent},
     Dispatch, ExitRequestedEventAction, RunEvent as RuntimeRunEvent, Runtime,
   },
@@ -359,6 +359,12 @@ macro_rules! shared_app_impl {
       /// Creates a new webview window.
       ///
       /// Data URLs are only supported with the `window-data-url` feature flag.
+      ///
+      /// See [`Self::window_builder`] for an API with extended functionality.
+      #[deprecated(
+        since = "1.0.0-rc.4",
+        note = "The `window_builder` function offers an easier API with extended functionality"
+      )]
       pub fn create_window<F>(
         &self,
         label: impl Into<String>,

+ 3 - 9
core/tauri/src/endpoints/window.rs

@@ -152,18 +152,12 @@ impl Cmd {
     context: InvokeContext<R>,
     options: Box<WindowConfig>,
   ) -> super::Result<()> {
-    let mut window = context.window;
     let label = options.label.clone();
     let url = options.url.clone();
 
-    window
-      .create_window(label, url, |_, webview_attributes| {
-        (
-          <<R::Dispatcher as Dispatch>::WindowBuilder>::with_config(*options),
-          webview_attributes,
-        )
-      })
-      .map_err(crate::error::into_anyhow)?;
+    let mut builder = context.window.builder(label, url);
+    builder.window_builder = <<R::Dispatcher as Dispatch>::WindowBuilder>::with_config(*options);
+    builder.build().map_err(crate::error::into_anyhow)?;
 
     Ok(())
   }

+ 284 - 10
core/tauri/src/window.rs

@@ -15,8 +15,9 @@ use crate::{
   hooks::{InvokePayload, InvokeResponder},
   manager::WindowManager,
   runtime::{
+    menu::Menu,
     monitor::Monitor as RuntimeMonitor,
-    webview::{WebviewAttributes, WindowBuilder},
+    webview::{WebviewAttributes, WindowBuilder as _},
     window::{
       dpi::{PhysicalPosition, PhysicalSize, Position, Size},
       DetachedWindow, JsEventListenerKey, PendingWindow, WindowEvent,
@@ -30,11 +31,14 @@ use crate::{
 };
 
 use serde::Serialize;
+#[cfg(windows)]
+use windows::Win32::Foundation::HWND;
 
 use tauri_macros::default_runtime;
 
 use std::{
   hash::{Hash, Hasher},
+  path::PathBuf,
   sync::Arc,
 };
 
@@ -82,6 +86,266 @@ impl Monitor {
   }
 }
 
+/// A runtime handle or dispatch used to create new windows.
+#[derive(Debug, Clone)]
+pub enum RuntimeHandleOrDispatch<R: Runtime> {
+  /// Handle to the running [`Runtime`].
+  RuntimeHandle(R::Handle),
+
+  /// A dispatcher to the running [`Runtime`].
+  Dispatch(R::Dispatcher),
+}
+
+/// A builder for a webview window managed by Tauri.
+#[default_runtime(crate::Wry, wry)]
+#[derive(Debug)]
+pub struct WindowBuilder<R: Runtime> {
+  manager: WindowManager<R>,
+  runtime: RuntimeHandleOrDispatch<R>,
+  app_handle: AppHandle<R>,
+  label: String,
+  pub(crate) window_builder: <R::Dispatcher as Dispatch>::WindowBuilder,
+  webview_attributes: WebviewAttributes,
+}
+
+impl<R: Runtime> ManagerBase<R> for WindowBuilder<R> {
+  fn manager(&self) -> &WindowManager<R> {
+    &self.manager
+  }
+
+  fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
+    match &self.runtime {
+      RuntimeHandleOrDispatch::RuntimeHandle(r) => RuntimeOrDispatch::RuntimeHandle(r.clone()),
+      RuntimeHandleOrDispatch::Dispatch(d) => RuntimeOrDispatch::Dispatch(d.clone()),
+    }
+  }
+
+  fn app_handle(&self) -> AppHandle<R> {
+    self.app_handle.clone()
+  }
+}
+
+impl<R: Runtime> WindowBuilder<R> {
+  /// Initializes a webview window builder with the given window label and URL to load on the webview.
+  pub fn new<M: Manager<R>, L: Into<String>>(manager: &M, label: L, url: WindowUrl) -> Self {
+    let runtime = match manager.runtime() {
+      RuntimeOrDispatch::Runtime(r) => RuntimeHandleOrDispatch::RuntimeHandle(r.handle()),
+      RuntimeOrDispatch::RuntimeHandle(h) => RuntimeHandleOrDispatch::RuntimeHandle(h),
+      RuntimeOrDispatch::Dispatch(d) => RuntimeHandleOrDispatch::Dispatch(d),
+    };
+    let app_handle = manager.app_handle();
+    Self {
+      manager: manager.manager().clone(),
+      runtime,
+      app_handle,
+      label: label.into(),
+      window_builder: <R::Dispatcher as Dispatch>::WindowBuilder::new(),
+      webview_attributes: WebviewAttributes::new(url),
+    }
+  }
+
+  /// Creates a new webview window.
+  pub fn build(self) -> crate::Result<Window<R>> {
+    self.create_new_window(PendingWindow::new(
+      self.window_builder.clone(),
+      self.webview_attributes.clone(),
+      self.label.clone(),
+    )?)
+  }
+
+  // --------------------------------------------- Window builder ---------------------------------------------
+
+  /// Sets the menu for the window.
+  #[must_use]
+  pub fn menu(mut self, menu: Menu) -> 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's.
+  #[must_use]
+  pub fn position(mut self, x: f64, y: f64) -> Self {
+    self.window_builder = self.window_builder.position(x, y);
+    self
+  }
+
+  /// Window size.
+  #[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.
+  #[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.
+  #[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
+  }
+
+  /// Whether the window is resizable or not.
+  #[must_use]
+  pub fn resizable(mut self, resizable: bool) -> Self {
+    self.window_builder = self.window_builder.resizable(resizable);
+    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
+  }
+
+  /// Whether the window will be initially hidden or focused.
+  #[must_use]
+  pub fn focus(mut self) -> Self {
+    self.window_builder = self.window_builder.focus();
+    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
+  }
+
+  /// Whether the 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(
+    doc_cfg,
+    doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
+  )]
+  #[must_use]
+  pub fn transparent(mut self, transparent: bool) -> Self {
+    self.window_builder = self.window_builder.transparent(transparent);
+    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 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
+  }
+
+  /// Sets the window icon.
+  pub fn icon(mut self, icon: Icon) -> crate::Result<Self> {
+    self.window_builder = self.window_builder.icon(icon)?;
+    Ok(self)
+  }
+
+  /// Sets whether or not the window icon should be added to the taskbar.
+  #[must_use]
+  pub fn skip_taskbar(mut self, skip: bool) -> Self {
+    self.window_builder = self.window_builder.skip_taskbar(skip);
+    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_window(mut self, parent: HWND) -> Self {
+    self.window_builder = self.window_builder.parent_window(parent);
+    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_window(mut self, owner: HWND) -> Self {
+    self.window_builder = self.window_builder.owner_window(owner);
+    self
+  }
+
+  // ------------------------------------------- Webview attributes -------------------------------------------
+
+  /// Sets the init script.
+  #[must_use]
+  pub fn initialization_script(mut self, script: &str) -> Self {
+    self
+      .webview_attributes
+      .initialization_scripts
+      .push(script.to_string());
+    self
+  }
+
+  /// Data directory for the webview.
+  #[must_use]
+  pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
+    self
+      .webview_attributes
+      .data_directory
+      .replace(data_directory);
+    self
+  }
+
+  /// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows.
+  #[must_use]
+  pub fn disable_file_drop_handler(mut self) -> Self {
+    self.webview_attributes.file_drop_handler_enabled = false;
+    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_attributes.clipboard = true;
+    self
+  }
+}
+
 // TODO: expand these docs since this is a pretty important type
 /// A webview window managed by Tauri.
 ///
@@ -178,9 +442,22 @@ impl<R: Runtime> Window<R> {
     }
   }
 
+  /// Initializes a webview window builder with the given window label and URL to load on the webview.
+  ///
+  /// Data URLs are only supported with the `window-data-url` feature flag.
+  pub fn builder<L: Into<String>>(&self, label: L, url: WindowUrl) -> WindowBuilder<R> {
+    WindowBuilder::<R>::new(self, label.into(), url)
+  }
+
   /// Creates a new webview window.
   ///
   /// Data URLs are only supported with the `window-data-url` feature flag.
+  ///
+  /// See [`Self::builder`] for an API with extended functionality.
+  #[deprecated(
+    since = "1.0.0-rc.4",
+    note = "The `builder` function offers an easier API with extended functionality"
+  )]
   pub fn create_window<F>(
     &mut self,
     label: String,
@@ -196,15 +473,12 @@ impl<R: Runtime> Window<R> {
       WebviewAttributes,
     ),
   {
-    let (window_builder, webview_attributes) = setup(
-      <R::Dispatcher as Dispatch>::WindowBuilder::new(),
-      WebviewAttributes::new(url),
-    );
-    self.create_new_window(PendingWindow::new(
-      window_builder,
-      webview_attributes,
-      label,
-    )?)
+    let mut builder = WindowBuilder::<R>::new(self, label, url);
+    let (window_builder, webview_attributes) =
+      setup(builder.window_builder, builder.webview_attributes);
+    builder.window_builder = window_builder;
+    builder.webview_attributes = webview_attributes;
+    builder.build()
   }
 
   pub(crate) fn invoke_responder(&self) -> Arc<InvokeResponder<R>> {

+ 1 - 0
core/tauri/tests/restart/Cargo.lock

@@ -2545,6 +2545,7 @@ dependencies = [
  "tokio",
  "url",
  "uuid",
+ "windows",
 ]
 
 [[package]]

+ 1 - 0
examples/api/src-tauri/Cargo.lock

@@ -3349,6 +3349,7 @@ dependencies = [
  "tokio",
  "url",
  "uuid",
+ "windows 0.30.0",
  "zip",
 ]
 

+ 5 - 10
examples/api/src-tauri/src/main.rs

@@ -16,8 +16,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
 
 use serde::{Deserialize, Serialize};
 use tauri::{
-  api::dialog::ask, http::ResponseBuilder, CustomMenuItem, GlobalShortcutManager, Manager,
-  RunEvent, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowBuilder, WindowUrl,
+  api::dialog::ask, http::ResponseBuilder, window::WindowBuilder, CustomMenuItem,
+  GlobalShortcutManager, Manager, RunEvent, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowUrl,
 };
 
 #[derive(Clone, Serialize)]
@@ -129,14 +129,9 @@ fn main() {
             item_handle.set_title(new_title).unwrap();
           }
           "new" => {
-            app
-              .create_window(
-                "new",
-                WindowUrl::App("index.html".into()),
-                |window_builder, webview_attributes| {
-                  (window_builder.title("Tauri"), webview_attributes)
-                },
-              )
+            WindowBuilder::new(app, "new", WindowUrl::App("index.html".into()))
+              .title("Tauri")
+              .build()
               .unwrap();
           }
           #[cfg(target_os = "macos")]