Pārlūkot izejas kodu

refactor: refactor parent APIs on `WindowBuilder` (#8622)

* refactor: refactor parent APIs on `WindowBuilder`

closes #8587 #1643

* fix build

* clippy

* support parent in JS and config

* change files

* fix build

* clippy

* fix doctests

* fix linux build

* fix doctests

* update docs

* fix api, update example to use JS API

* fix merge

* lint

* fix tests on windows

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Amr Bashir 1 gadu atpakaļ
vecāks
revīzija
9eaeb5a8cd

+ 5 - 0
.changes/api-window-parent.md

@@ -0,0 +1,5 @@
+---
+'@tauri-apps/api': 'patch:feat'
+---
+
+Add `parent` option when creating a window.

+ 5 - 0
.changes/tauri-parent-owner.md

@@ -0,0 +1,5 @@
+---
+'tauri': 'patch:feat'
+---
+
+Add `WindowBuilder::parent` which is a convenient wrapper around parent functionality for Windows, Linux and macOS. Also added `WindowBuilder::owner` on Windows only. Also added `WindowBuilder::transient_for` and `WindowBuilder::transient_for_raw` on Linux only.

+ 5 - 0
.changes/tauri-remove-parent-window.md

@@ -0,0 +1,5 @@
+---
+'tauri': 'patch:breaking'
+---
+
+Renamed `WindowBuilder::owner_window` to `WindowBuilder::owner_raw` and `WindowBuilder::parent_window` to `WindowBuilder::parent_raw`.

+ 6 - 0
.changes/tauri-runtime-remove-parent-window.md

@@ -0,0 +1,6 @@
+---
+'tauri-runtime': 'patch'
+'tauri-runtime-wry': 'patch'
+---
+
+Added `WindowBuilder::transient_for` and Renamed `WindowBuilder::owner_window` to `WindowBuilder::owner` and `WindowBuilder::parent_window` to `WindowBuilder::parent`.

+ 5 - 0
.changes/tauri-utils-config-parent.md

@@ -0,0 +1,5 @@
+---
+'tauri-utils': 'patch:feat'
+---
+
+Add `parent` option for window config.

+ 5 - 0
.changes/tauri-window-builder-from-config.md

@@ -0,0 +1,5 @@
+---
+'tauri': 'patch:breaking'
+---
+
+Changed `WindowBuilder::from_config` to return a `Result<Self>`.

+ 6 - 0
.changes/tauri-window-builder-with-config-ref.md

@@ -0,0 +1,6 @@
+---
+'tauri-runtime': 'patch:breaking'
+'tauri-runtime-wry': 'patch:breaking'
+---
+
+Changed `WindowBuilder::with_config` to take a reference to a `WindowConfig` instead of an owned value.

+ 7 - 0
core/tauri-config-schema/schema.json

@@ -565,6 +565,13 @@
           "description": "Whether or not the webview should be launched in incognito  mode.\n\n## Platform-specific:\n\n- **Android**: Unsupported.",
           "default": false,
           "type": "boolean"
+        },
+        "parent": {
+          "description": "Sets the window associated with this label to be the parent of the window to be created.\n\n## Platform-specific\n\n- **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>",
+          "type": [
+            "string",
+            "null"
+          ]
         }
       },
       "additionalProperties": false

+ 1 - 1
core/tauri-runtime-wry/Cargo.toml

@@ -14,7 +14,7 @@ rust-version = { workspace = true }
 
 [dependencies]
 wry = { version = "0.35.2", default-features = false, features = [ "file-drop", "protocol", "os-webview" ] }
-tao = { version = "0.24", default-features = false, features = [ "rwh_05", "rwh_06" ] }
+tao = { git = "https://github.com/tauri-apps/tao", branch = "dev", default-features = false, features = [ "rwh_05", "rwh_06" ] }
 tauri-runtime = { version = "1.0.0-alpha.8", path = "../tauri-runtime" }
 tauri-utils = { version = "2.0.0-alpha.13", path = "../tauri-utils" }
 raw-window-handle = "0.5"

+ 18 - 6
core/tauri-runtime-wry/src/lib.rs

@@ -679,7 +679,7 @@ impl WindowBuilder for WindowBuilderWrapper {
     Self::default().focused(true)
   }
 
-  fn with_config(config: WindowConfig) -> Self {
+  fn with_config(config: &WindowConfig) -> Self {
     let mut window = WindowBuilderWrapper::new();
 
     #[cfg(target_os = "macos")]
@@ -878,20 +878,32 @@ impl WindowBuilder for WindowBuilderWrapper {
   }
 
   #[cfg(windows)]
-  fn parent_window(mut self, parent: HWND) -> Self {
+  fn owner(mut self, owner: HWND) -> Self {
+    self.inner = self.inner.with_owner_window(owner.0);
+    self
+  }
+
+  #[cfg(windows)]
+  fn parent(mut self, parent: HWND) -> Self {
     self.inner = self.inner.with_parent_window(parent.0);
     self
   }
 
   #[cfg(target_os = "macos")]
-  fn parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
+  fn parent(mut self, parent: *mut std::ffi::c_void) -> Self {
     self.inner = self.inner.with_parent_window(parent);
     self
   }
 
-  #[cfg(windows)]
-  fn owner_window(mut self, owner: HWND) -> Self {
-    self.inner = self.inner.with_owner_window(owner.0);
+  #[cfg(any(
+    target_os = "linux",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "netbsd",
+    target_os = "openbsd"
+  ))]
+  fn transient_for(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
+    self.inner = self.inner.with_transient_for(parent);
     self
   }
 

+ 26 - 16
core/tauri-runtime/src/window.rs

@@ -206,7 +206,7 @@ pub trait WindowBuilder: WindowBuilderBase {
   fn new() -> Self;
 
   /// Initializes a new window builder from a [`WindowConfig`]
-  fn with_config(config: WindowConfig) -> Self;
+  fn with_config(config: &WindowConfig) -> Self;
 
   /// Show window in the center of the screen.
   #[must_use]
@@ -330,35 +330,45 @@ pub trait WindowBuilder: WindowBuilderBase {
   #[must_use]
   fn shadow(self, enable: bool) -> Self;
 
-  /// Sets a parent to the window to be created.
+  /// Set an owner 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.
+  /// 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#child-windows>
+  /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
   #[cfg(windows)]
   #[must_use]
-  fn parent_window(self, parent: HWND) -> Self;
+  fn owner(self, owner: HWND) -> 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(target_os = "macos")]
+  #[cfg(windows)]
   #[must_use]
-  fn parent_window(self, parent: *mut std::ffi::c_void) -> Self;
+  fn parent(self, parent: HWND) -> 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.
+  /// Sets a parent to the window to be created.
   ///
-  /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
-  #[cfg(windows)]
+  /// See <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
+  #[cfg(target_os = "macos")]
   #[must_use]
-  fn owner_window(self, owner: HWND) -> Self;
+  fn parent(self, parent: *mut std::ffi::c_void) -> 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"
+  ))]
+  fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self;
 
   /// Enables or disables drag and drop support.
   #[cfg(windows)]

+ 16 - 1
core/tauri-utils/src/config.rs

@@ -1252,6 +1252,18 @@ pub struct WindowConfig {
   ///  - **Android**: Unsupported.
   #[serde(default)]
   pub incognito: bool,
+  /// Sets the window associated with this label to be the parent of 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 parent: Option<String>,
 }
 
 impl Default for WindowConfig {
@@ -1295,6 +1307,7 @@ impl Default for WindowConfig {
       shadow: true,
       window_effects: None,
       incognito: false,
+      parent: None,
     }
   }
 }
@@ -2310,6 +2323,7 @@ mod build {
       let shadow = self.shadow;
       let window_effects = opt_lit(self.window_effects.as_ref());
       let incognito = self.incognito;
+      let parent = opt_str_lit(self.parent.as_ref());
 
       literal_struct!(
         tokens,
@@ -2351,7 +2365,8 @@ mod build {
         additional_browser_args,
         shadow,
         window_effects,
-        incognito
+        incognito,
+        parent
       );
     }
   }

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

@@ -169,7 +169,7 @@ mod window_effects {
 pub use window_effects::{WindowEffect, WindowEffectState};
 
 /// How the window title bar should be displayed on macOS.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
 #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
 pub enum TitleBarStyle {
   /// A normal title bar.

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
core/tauri/scripts/bundle.global.js


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

@@ -1706,7 +1706,7 @@ fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
     .collect::<Vec<_>>();
 
   for window_config in app.config().tauri.windows.clone() {
-    WebviewWindowBuilder::from_config(app.handle(), window_config)
+    WebviewWindowBuilder::from_config(app.handle(), &window_config)?
       .build_internal(&window_labels, &webview_labels)?;
   }
 

+ 11 - 0
core/tauri/src/lib.rs

@@ -518,6 +518,17 @@ impl<A: Assets> Context<A> {
     &self.pattern
   }
 
+  /// A mutable reference to the resolved ACL.
+  ///
+  /// # Stability
+  ///
+  /// This API is unstable.
+  #[doc(hidden)]
+  #[inline(always)]
+  pub fn resolved_acl(&mut self) -> &mut Resolved {
+    &mut self.resolved_acl
+  }
+
   /// Create a new [`Context`] from the minimal required items.
   #[inline(always)]
   #[allow(clippy::too_many_arguments)]

+ 16 - 5
core/tauri/src/test/mock_runtime.rs

@@ -274,7 +274,7 @@ impl WindowBuilder for MockWindowBuilder {
     Self {}
   }
 
-  fn with_config(config: WindowConfig) -> Self {
+  fn with_config(config: &WindowConfig) -> Self {
     Self {}
   }
 
@@ -376,17 +376,28 @@ impl WindowBuilder for MockWindowBuilder {
   }
 
   #[cfg(windows)]
-  fn parent_window(self, parent: HWND) -> Self {
+  fn owner(self, owner: HWND) -> Self {
+    self
+  }
+
+  #[cfg(windows)]
+  fn parent(self, parent: HWND) -> Self {
     self
   }
 
   #[cfg(target_os = "macos")]
-  fn parent_window(self, parent: *mut std::ffi::c_void) -> Self {
+  fn parent(self, parent: *mut std::ffi::c_void) -> Self {
     self
   }
 
-  #[cfg(windows)]
-  fn owner_window(self, owner: HWND) -> Self {
+  #[cfg(any(
+    target_os = "linux",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "netbsd",
+    target_os = "openbsd"
+  ))]
+  fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
     self
   }
 

+ 4 - 3
core/tauri/src/webview/mod.rs

@@ -354,7 +354,8 @@ async fn create_window(app: tauri::AppHandle) {
 ```
 #[tauri::command]
 async fn reopen_window(app: tauri::AppHandle) {
-  let window = tauri::window::WindowBuilder::from_config(&app, app.config().tauri.windows.get(0).unwrap().clone())
+  let window = tauri::window::WindowBuilder::from_config(&app, &app.config().tauri.windows.get(0).unwrap().clone())
+    .unwrap()
     .build()
     .unwrap();
 }
@@ -363,10 +364,10 @@ async fn reopen_window(app: tauri::AppHandle) {
   )]
   ///
   /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
-  pub fn from_config(config: WindowConfig) -> Self {
+  pub fn from_config(config: &WindowConfig) -> Self {
     Self {
       label: config.label.clone(),
-      webview_attributes: WebviewAttributes::from(&config),
+      webview_attributes: WebviewAttributes::from(config),
       web_resource_request_handler: None,
       navigation_handler: None,
       on_page_load_handler: None,

+ 1 - 1
core/tauri/src/webview/plugin.rs

@@ -47,7 +47,7 @@ mod desktop_commands {
     app: AppHandle<R>,
     options: WindowConfig,
   ) -> crate::Result<()> {
-    WebviewWindowBuilder::from_config(&app, options).build()?;
+    WebviewWindowBuilder::from_config(&app, &options)?.build()?;
     Ok(())
   }
   #[cfg(not(feature = "unstable"))]

+ 83 - 19
core/tauri/src/webview/webview_window.rs

@@ -123,7 +123,8 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
 ```
 #[tauri::command]
 async fn reopen_window(app: tauri::AppHandle) {
-  let webview_window = tauri::window::WindowBuilder::from_config(&app, app.config().tauri.windows.get(0).unwrap().clone())
+  let webview_window = tauri::window::WindowBuilder::from_config(&app, &app.config().tauri.windows.get(0).unwrap().clone())
+    .unwrap()
     .build()
     .unwrap();
 }
@@ -132,11 +133,11 @@ async fn reopen_window(app: tauri::AppHandle) {
   )]
   ///
   /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
-  pub fn from_config(manager: &'a M, config: WindowConfig) -> Self {
-    Self {
-      window_builder: WindowBuilder::from_config(manager, config.clone()),
+  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.
@@ -562,22 +563,33 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
 
   /// 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.
+  /// ## Platform-specific
   ///
-  /// 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
+  /// - **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.webview.window)?;
+    Ok(self)
   }
 
-  /// Sets a parent to the window to be created.
-  #[cfg(target_os = "macos")]
+  /// 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 parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
-    self.window_builder = self.window_builder.parent_window(parent);
-    self
+  pub fn owner(mut self, owner: &WebviewWindow<R>) -> crate::Result<Self> {
+    self.window_builder = self.window_builder.owner(&owner.webview.window)?;
+    Ok(self)
   }
 
   /// Set an owner to the window to be created.
@@ -590,8 +602,60 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
   /// 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);
+  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.webview.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"
+  ))]
+  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
   }
 

+ 123 - 17
core/tauri/src/window/mod.rs

@@ -227,7 +227,8 @@ async fn create_window(app: tauri::AppHandle) {
 ```
 #[tauri::command]
 async fn reopen_window(app: tauri::AppHandle) {
-  let window = tauri::window::WindowBuilder::from_config(&app, app.config().tauri.windows.get(0).unwrap().clone())
+  let window = tauri::window::WindowBuilder::from_config(&app, &app.config().tauri.windows.get(0).unwrap().clone())
+    .unwrap()
     .build()
     .unwrap();
 }
@@ -236,8 +237,9 @@ async fn reopen_window(app: tauri::AppHandle) {
   )]
   ///
   /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583
-  pub fn from_config(manager: &'a M, config: WindowConfig) -> Self {
-    Self {
+  pub fn from_config(manager: &'a M, config: &WindowConfig) -> crate::Result<Self> {
+    #[cfg_attr(not(unstable), allow(unused_mut))]
+    let mut builder = Self {
       manager,
       label: config.label.clone(),
       window_effects: config.window_effects.clone(),
@@ -249,7 +251,18 @@ async fn reopen_window(app: tauri::AppHandle) {
       menu: None,
       #[cfg(desktop)]
       on_menu_event: None,
+    };
+
+    #[cfg(desktop)]
+    if let Some(parent) = &config.parent {
+      let window = manager
+        .manager()
+        .get_window(parent)
+        .ok_or(crate::Error::WindowNotFound)?;
+      builder = builder.parent(&window)?;
     }
+
+    Ok(builder)
   }
 
   /// Registers a global menu event listener.
@@ -650,22 +663,53 @@ impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
 
   /// 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.
+  /// ## Platform-specific
   ///
-  /// 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
+  /// - **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: &Window<R>) -> crate::Result<Self> {
+    #[cfg(windows)]
+    {
+      self.window_builder = self.window_builder.owner(parent.hwnd()?);
+    }
+
+    #[cfg(any(
+      target_os = "linux",
+      target_os = "dragonfly",
+      target_os = "freebsd",
+      target_os = "netbsd",
+      target_os = "openbsd"
+    ))]
+    {
+      self.window_builder = self.window_builder.transient_for(&parent.gtk_window()?);
+    }
+
+    #[cfg(target_os = "macos")]
+    {
+      self.window_builder = self.window_builder.parent(parent.ns_window()?);
+    }
+
+    Ok(self)
   }
 
-  /// Sets a parent to the window to be created.
-  #[cfg(target_os = "macos")]
+  /// 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 parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
-    self.window_builder = self.window_builder.parent_window(parent);
-    self
+  pub fn owner(mut self, owner: &Window<R>) -> crate::Result<Self> {
+    self.window_builder = self.window_builder.owner(owner.hwnd()?);
+    Ok(self)
   }
 
   /// Set an owner to the window to be created.
@@ -676,10 +720,72 @@ impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
   /// - 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>
+  ///
+  /// **Note:** This is a low level API. See [`Self::parent`] for a higher level wrapper for Tauri windows.
   #[cfg(windows)]
   #[must_use]
-  pub fn owner_window(mut self, owner: HWND) -> Self {
-    self.window_builder = self.window_builder.owner_window(owner);
+  pub fn owner_raw(mut self, owner: HWND) -> Self {
+    self.window_builder = self.window_builder.owner(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>
+  ///
+  /// **Note:** This is a low level API. See [`Self::parent`] for a higher level wrapper for Tauri windows.
+  #[cfg(windows)]
+  #[must_use]
+  pub fn parent_raw(mut self, parent: HWND) -> Self {
+    self.window_builder = self.window_builder.parent(parent);
+    self
+  }
+
+  /// Sets a parent to the window to be created.
+  ///
+  /// See <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
+  ///
+  /// **Note:** This is a low level API. See [`Self::parent`] for a higher level wrapper for Tauri windows.
+  #[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(parent);
+    self
+  }
+
+  /// Sets the window to be created transient for parent.
+  ///
+  /// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
+  ///
+  /// **Note:** This is a low level API. See [`Self::parent`] for a higher level wrapper for Tauri windows.
+  #[cfg(any(
+    target_os = "linux",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "netbsd",
+    target_os = "openbsd"
+  ))]
+  pub fn transient_for(mut self, parent: &Window<R>) -> crate::Result<Self> {
+    self.window_builder = self.window_builder.transient_for(&parent.gtk_window()?);
+    Ok(self)
+  }
+
+  /// Sets the window to be created transient for parent.
+  ///
+  /// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
+  ///
+  /// **Note:** This is a low level API. See [`Self::parent`] and [`Self::transient_for`] for higher level wrappers for Tauri windows.
+  #[cfg(any(
+    target_os = "linux",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "netbsd",
+    target_os = "openbsd"
+  ))]
+  pub fn transient_for_raw(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
+    self.window_builder = self.window_builder.transient_for(parent);
     self
   }
 

+ 1 - 1
core/tauri/src/window/plugin.rs

@@ -61,7 +61,7 @@ mod desktop_commands {
 
   #[command(root = "crate")]
   pub async fn create<R: Runtime>(app: AppHandle<R>, options: WindowConfig) -> crate::Result<()> {
-    WindowBuilder::from_config(&app, options).build()?;
+    WindowBuilder::from_config(&app, &options)?.build()?;
     Ok(())
   }
 

+ 1 - 1
examples/navigation/public/index.js

@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-const WebviewWindow = window.__TAURI__.window.WebviewWindow
+const WebviewWindow = window.__TAURI__.webview.WebviewWindow
 
 const routeSelect = document.querySelector('#route')
 const link = document.querySelector('#link')

+ 50 - 45
examples/parent-window/index.html

@@ -1,54 +1,59 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <style>
-      #response {
-        white-space: pre-wrap;
-      }
-    </style>
-  </head>
 
-  <body>
-    <div id="window-label"></div>
-    <div id="container"></div>
-    <div id="response"></div>
+<head>
+  <style>
+    #response {
+      white-space: pre-wrap;
+    }
+  </style>
+</head>
 
-    <script>
-      const WebviewWindow = window.__TAURI__.window.WebviewWindow
-      const thisTauriWindow = window.__TAURI__.window.getCurrent()
-      const windowLabel = thisTauriWindow.label
-      const windowLabelContainer = document.getElementById('window-label')
-      windowLabelContainer.innerText = 'This is the ' + windowLabel + ' window.'
+<body>
+  <div id="window-label"></div>
+  <div id="container"></div>
+  <div id="response"></div>
 
-      const container = document.getElementById('container')
+  <script>
+    const WebviewWindow = window.__TAURI__.webview.WebviewWindow
+    const thisTauriWindow = window.__TAURI__.window.getCurrent()
+    const windowLabel = thisTauriWindow.label
+    const windowLabelContainer = document.getElementById('window-label')
+    windowLabelContainer.innerText = 'This is the ' + windowLabel + ' window.'
 
-      const responseContainer = document.getElementById('response')
-      function runCommand(commandName, args, optional) {
-        window.__TAURI__.core
-          .invoke(commandName, args)
-          .then((response) => {
-            responseContainer.innerText += `Ok(${response})\n\n`
-          })
-          .catch((error) => {
-            responseContainer.innerText += `Err(${error})\n\n`
-          })
-      }
-      window.__TAURI__.event.listen('tauri://window-created', function (event) {
-        responseContainer.innerText += 'Got window-created event\n\n'
-      })
+    const container = document.getElementById('container')
 
-      const createWindowButton = document.createElement('button')
-      const windowId = Math.random().toString().replace('.', '')
-      const windowNumber = 1
-      createWindowButton.innerHTML = 'Create child window ' + windowNumber
-      createWindowButton.addEventListener('click', function () {
-        runCommand('create_child_window', {
-          id: `child-${windowId}-${windowNumber}`
+    const responseContainer = document.getElementById('response')
+    function runCommand(commandName, args, optional) {
+      window.__TAURI__.core
+        .invoke(commandName, args)
+        .then((response) => {
+          responseContainer.innerText += `Ok(${response})\n\n`
+        })
+        .catch((error) => {
+          responseContainer.innerText += `Err(${error})\n\n`
         })
-        windowNumber += 1
-        createWindowButton.innerHTML = 'Create child window ' + windowNumber
+    }
+    window.__TAURI__.event.listen('tauri://window-created', function (event) {
+      responseContainer.innerText += 'Got window-created event\n\n'
+    })
+
+    const createWindowButton = document.createElement('button')
+    const windowId = Math.random().toString().replace('.', '')
+    let windowNumber = 1
+    createWindowButton.innerHTML = 'Create child window ' + windowNumber
+    createWindowButton.addEventListener('click', function () {
+      new WebviewWindow(`child-${windowId}-${windowNumber}`, {
+        title: 'Child',
+        width: 400,
+        height: 300,
+        parent: thisTauriWindow
       })
-      container.appendChild(createWindowButton)
-    </script>
-  </body>
-</html>
+      windowNumber += 1
+      createWindowButton.innerHTML = 'Create child window ' + windowNumber
+    })
+    container.appendChild(createWindowButton)
+  </script>
+</body>
+
+</html>

+ 25 - 19
examples/parent-window/main.rs

@@ -4,23 +4,32 @@
 
 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
 
-use tauri::{command, webview::PageLoadEvent, WebviewUrl, WebviewWindowBuilder, Window};
-
-#[command]
-async fn create_child_window(id: String, window: Window) {
-  let builder = WebviewWindowBuilder::new(&window, &id, WebviewUrl::default())
-    .title("Child")
-    .inner_size(400.0, 300.0);
-
-  #[cfg(target_os = "macos")]
-  let builder = builder.parent_window(window.ns_window().unwrap());
-  #[cfg(windows)]
-  let builder = builder.parent_window(window.hwnd().unwrap());
-
-  let _webview = builder.build().unwrap();
-}
+use tauri::{webview::PageLoadEvent, WebviewUrl, WebviewWindowBuilder};
+use tauri_utils::acl::{
+  resolved::{CommandKey, ResolvedCommand},
+  ExecutionContext,
+};
 
 fn main() {
+  let mut context = tauri::generate_context!("../../examples/parent-window/tauri.conf.json");
+  for cmd in [
+    "plugin:event|listen",
+    "plugin:webview|create_webview_window",
+    "plugin:window|internal_on_mousemove",
+    "plugin:window|internal_on_mousedown",
+  ] {
+    context.resolved_acl().allowed_commands.insert(
+      CommandKey {
+        name: cmd.into(),
+        context: ExecutionContext::Local,
+      },
+      ResolvedCommand {
+        windows: vec!["*".parse().unwrap()],
+        ..Default::default()
+      },
+    );
+  }
+
   tauri::Builder::default()
     .on_page_load(|webview, payload| {
       if payload.event() == PageLoadEvent::Finished {
@@ -30,7 +39,6 @@ fn main() {
         });
       }
     })
-    .invoke_handler(tauri::generate_handler![create_child_window])
     .setup(|app| {
       let _webview = WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
         .title("Main")
@@ -39,8 +47,6 @@ fn main() {
 
       Ok(())
     })
-    .run(tauri::generate_context!(
-      "../../examples/parent-window/tauri.conf.json"
-    ))
+    .run(context)
     .expect("failed to run tauri application");
 }

+ 4 - 0
tooling/api/src/webview.ts

@@ -556,6 +556,10 @@ class WebviewWindow {
       invoke('plugin:webview|create_webview_window', {
         options: {
           ...options,
+          parent:
+            typeof options.parent === 'string'
+              ? options.parent
+              : options.parent?.label,
           label
         }
       })

+ 19 - 1
tooling/api/src/window.ts

@@ -31,6 +31,7 @@ import type {
 } from './event'
 import { TauriEvent, emit, listen, once } from './event'
 import { invoke } from './core'
+import { WebviewWindow } from './webview'
 
 /**
  * Allows you to retrieve information about a given monitor.
@@ -298,6 +299,10 @@ class Window {
       invoke('plugin:window|create', {
         options: {
           ...options,
+          parent:
+            typeof options.parent === 'string'
+              ? options.parent
+              : options.parent?.label,
           label
         }
       })
@@ -2033,7 +2038,20 @@ interface WindowOptions {
    */
   closable?: boolean
   /**
-   * Whether the window should be visible on all workspaces or virtual desktops.
+   * Sets a parent to the window to be created. Can be either a {@linkcode Window} or a label of the window.
+   *
+   * #### 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>
+   */
+  parent?: Window | WebviewWindow | string
+  /** Whether the window should be visible on all workspaces or virtual desktops.
    *
    * ## Platform-specific
    *

+ 7 - 0
tooling/cli/schema.json

@@ -565,6 +565,13 @@
           "description": "Whether or not the webview should be launched in incognito  mode.\n\n## Platform-specific:\n\n- **Android**: Unsupported.",
           "default": false,
           "type": "boolean"
+        },
+        "parent": {
+          "description": "Sets the window associated with this label to be the parent of the window to be created.\n\n## Platform-specific\n\n- **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>",
+          "type": [
+            "string",
+            "null"
+          ]
         }
       },
       "additionalProperties": false

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels