Jelajahi Sumber

fix(core): multiwebview mode fixes for auto resize, reparent and add_child (#8999)

* refactor(core): simplify multiwebview impl, fix autoresize

* add_child on main thread

* fix reparent

* revert some changes

* add change files
Lucas Fernandes Nogueira 1 tahun lalu
induk
melakukan
222a96b74b

+ 5 - 0
.changes/fix-add-child-deadlock.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch:bug
+---
+
+Fixes `Window::add_child` deadlock.

+ 5 - 0
.changes/fix-reparent.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch:bug
+---
+
+Fixes `Webview::reparent` not updating the webview parent window reference.

+ 5 - 0
.changes/multiwebview-bounds-fixes.md

@@ -0,0 +1,5 @@
+---
+"tauri-runtime-wry": patch:bug
+---
+
+Fixes auto resize and positioning when using the multiwebview mode.

+ 23 - 20
core/tauri-runtime-wry/src/lib.rs

@@ -2829,10 +2829,10 @@ fn handle_user_message<T: UserEvent>(
             bounds.height = size.height;
 
             if let Some(b) = &webview.bounds {
-              let window_size = window.inner_size();
+              let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
               let mut bounds = b.lock().unwrap();
-              bounds.width_rate = size.width as f32 / window_size.width as f32;
-              bounds.height_rate = size.height as f32 / window_size.height as f32;
+              bounds.width_rate = size.width as f32 / window_size.width;
+              bounds.height_rate = size.height as f32 / window_size.height;
             }
 
             webview.set_bounds(bounds);
@@ -2844,10 +2844,11 @@ fn handle_user_message<T: UserEvent>(
             bounds.y = position.y;
 
             if let Some(b) = &webview.bounds {
-              let window_size = window.inner_size();
+              let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
               let mut bounds = b.lock().unwrap();
-              bounds.width_rate = position.x as f32 / window_size.width as f32;
-              bounds.height_rate = position.y as f32 / window_size.height as f32;
+
+              bounds.x_rate = position.x as f32 / window_size.width;
+              bounds.y_rate = position.y as f32 / window_size.height;
             }
 
             webview.set_bounds(bounds);
@@ -2913,7 +2914,6 @@ fn handle_user_message<T: UserEvent>(
               f(webview.handle())
             }
           }
-
           #[cfg(any(debug_assertions, feature = "devtools"))]
           WebviewMessage::OpenDevTools => {
             webview.open_devtools();
@@ -2940,6 +2940,7 @@ fn handle_user_message<T: UserEvent>(
           Ok(webview) => {
             windows.0.borrow_mut().get_mut(&window_id).map(|w| {
               w.webviews.push(webview);
+              w.has_children.store(true, Ordering::Relaxed);
               w
             });
           }
@@ -3181,20 +3182,21 @@ fn handle_event_loop<T: UserEvent>(
             }
           }
           TaoWindowEvent::Resized(size) => {
-            if let Some(webviews) = windows
+            if let Some((Some(window), webviews)) = windows
               .0
               .borrow()
               .get(&window_id)
-              .map(|w| w.webviews.clone())
+              .map(|w| (w.inner.clone(), w.webviews.clone()))
             {
+              let size = size.to_logical::<f32>(window.scale_factor());
               for webview in webviews {
                 if let Some(bounds) = &webview.bounds {
                   let b = bounds.lock().unwrap();
                   webview.set_bounds(wry::Rect {
-                    x: (size.width as f32 * b.x_rate) as i32,
-                    y: (size.height as f32 * b.y_rate) as i32,
-                    width: (size.width as f32 * b.width_rate) as u32,
-                    height: (size.height as f32 * b.height_rate) as u32,
+                    x: (size.width * b.x_rate) as i32,
+                    y: (size.height * b.y_rate) as i32,
+                    width: (size.width * b.width_rate) as u32,
+                    height: (size.height * b.height_rate) as u32,
                   });
                 }
               }
@@ -3594,7 +3596,8 @@ fn create_webview<T: UserEvent>(
 
   if let Some(navigation_handler) = pending.navigation_handler {
     webview_builder = webview_builder.with_navigation_handler(move |url| {
-      Url::parse(&url)
+      url
+        .parse()
         .map(|url| navigation_handler(&url))
         .unwrap_or(true)
     });
@@ -3610,14 +3613,14 @@ fn create_webview<T: UserEvent>(
       height: size.height,
     });
 
-    let window_size = window.inner_size();
+    let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
 
     if webview_attributes.auto_resize {
       Some(WebviewBounds {
-        x_rate: (position.x as f32) / window_size.width as f32,
-        y_rate: (position.y as f32) / window_size.height as f32,
-        width_rate: (size.width as f32) / window_size.width as f32,
-        height_rate: (size.height as f32) / window_size.height as f32,
+        x_rate: (position.x as f32) / window_size.width,
+        y_rate: (position.y as f32) / window_size.height,
+        width_rate: (size.width as f32) / window_size.width,
+        height_rate: (size.height as f32) / window_size.height,
       })
     } else {
       None
@@ -3647,7 +3650,7 @@ fn create_webview<T: UserEvent>(
 
   if let Some(page_load_handler) = pending.on_page_load_handler {
     webview_builder = webview_builder.with_on_page_load_handler(move |event, url| {
-      let _ = Url::parse(&url).map(|url| {
+      let _ = url.parse().map(|url| {
         page_load_handler(
           url,
           match event {

+ 1 - 1
core/tauri/Cargo.toml

@@ -47,7 +47,7 @@ serde = { version = "1.0", features = [ "derive", "rc" ] }
 tokio = { version = "1", features = [ "rt", "rt-multi-thread", "sync", "fs", "io-util" ] }
 futures-util = "0.3"
 uuid = { version = "1", features = [ "v4" ], optional = true }
-url = { version = "2.4" }
+url = "2"
 anyhow = "1.0"
 thiserror = "1.0"
 tauri-runtime = { version = "2.0.0-beta.5", path = "../tauri-runtime" }

+ 3 - 0
core/tauri/src/error.rs

@@ -40,6 +40,9 @@ pub enum Error {
   /// Webview label must be unique.
   #[error("a webview with label `{0}` already exists")]
   WebviewLabelAlreadyExists(String),
+  /// Cannot use the webview reparent function on webview windows.
+  #[error("cannot reparent when using a WebviewWindow")]
+  CannotReparentWebviewWindow,
   /// Embedded asset not found.
   #[error("asset not found: {0}")]
   AssetNotFound(String),

+ 2 - 2
core/tauri/src/lib.rs

@@ -804,7 +804,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
   /// Fetch a single webview window from the manager.
   fn get_webview_window(&self, label: &str) -> Option<WebviewWindow<R>> {
     self.manager().get_webview(label).and_then(|webview| {
-      if webview.window().is_webview_window {
+      if webview.window().is_webview_window() {
         Some(WebviewWindow { webview })
       } else {
         None
@@ -819,7 +819,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
       .webviews()
       .into_iter()
       .filter_map(|(label, webview)| {
-        if webview.window().is_webview_window {
+        if webview.window().is_webview_window() {
           Some((label, WebviewWindow { webview }))
         } else {
           None

+ 5 - 5
core/tauri/src/manager/window.rs

@@ -79,7 +79,7 @@ impl<R: Runtime> WindowManager<R> {
     &self,
     app_handle: AppHandle<R>,
     window: DetachedWindow<EventLoopMessage, R>,
-    multiwebview: bool,
+    is_webview_window: bool,
     #[cfg(desktop)] menu: Option<crate::window::WindowMenu<R>>,
   ) -> Window<R> {
     let window = Window::new(
@@ -88,7 +88,7 @@ impl<R: Runtime> WindowManager<R> {
       app_handle,
       #[cfg(desktop)]
       menu,
-      multiwebview,
+      is_webview_window,
     );
 
     let window_ = window.clone();
@@ -197,7 +197,7 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
     WindowEvent::FileDrop(event) => match event {
       FileDropEvent::Hovered { paths, position } => {
         let payload = FileDropPayload { paths, position };
-        if window.is_webview_window {
+        if window.is_webview_window() {
           window.emit_to(
             EventTarget::labeled(window.label()),
             DROP_HOVER_EVENT,
@@ -218,14 +218,14 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
         }
         let payload = FileDropPayload { paths, position };
 
-        if window.is_webview_window {
+        if window.is_webview_window() {
           window.emit_to(EventTarget::labeled(window.label()), DROP_EVENT, payload)?
         } else {
           window.emit_to_window(DROP_EVENT, payload)?
         }
       }
       FileDropEvent::Cancelled => {
-        if window.is_webview_window {
+        if window.is_webview_window() {
           window.emit_to(
             EventTarget::labeled(window.label()),
             DROP_CANCELLED_EVENT,

+ 10 - 7
core/tauri/src/webview/mod.rs

@@ -894,7 +894,7 @@ impl<R: Runtime> Webview<R> {
   /// Closes this webview.
   pub fn close(&self) -> crate::Result<()> {
     let window = self.window();
-    if window.is_webview_window {
+    if window.is_webview_window() {
       window.close()
     } else {
       self.webview.dispatcher.close()?;
@@ -906,7 +906,7 @@ impl<R: Runtime> Webview<R> {
   /// Resizes this webview.
   pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
     let window = self.window();
-    if window.is_webview_window {
+    if window.is_webview_window() {
       window.set_size(size.into())
     } else {
       self
@@ -920,7 +920,7 @@ impl<R: Runtime> Webview<R> {
   /// Sets this webviews's position.
   pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
     let window = self.window();
-    if window.is_webview_window {
+    if window.is_webview_window() {
       window.set_position(position.into())
     } else {
       self
@@ -939,10 +939,13 @@ impl<R: Runtime> Webview<R> {
   /// Move the webview to the given window.
   pub fn reparent(&self, window: &Window<R>) -> crate::Result<()> {
     let current_window = self.window();
-    if !current_window.is_webview_window {
+    if current_window.is_webview_window() || window.is_webview_window() {
+      Err(crate::Error::CannotReparentWebviewWindow)
+    } else {
       self.webview.dispatcher.reparent(window.window.id)?;
+      *self.window_label.lock().unwrap() = window.label().to_string();
+      Ok(())
     }
-    Ok(())
   }
 
   /// Returns the webview position.
@@ -951,7 +954,7 @@ impl<R: Runtime> Webview<R> {
   /// - For webview window, returns the inner position of the window.
   pub fn position(&self) -> crate::Result<PhysicalPosition<i32>> {
     let window = self.window();
-    if window.is_webview_window {
+    if window.is_webview_window() {
       window.inner_position()
     } else {
       self.webview.dispatcher.position().map_err(Into::into)
@@ -961,7 +964,7 @@ impl<R: Runtime> Webview<R> {
   /// Returns the physical size of the webviews's client area.
   pub fn size(&self) -> crate::Result<PhysicalSize<u32>> {
     let window = self.window();
-    if window.is_webview_window {
+    if window.is_webview_window() {
       window.inner_size()
     } else {
       self.webview.dispatcher.size().map_err(Into::into)

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

@@ -880,7 +880,7 @@ 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();
-    if webview.window().is_webview_window {
+    if webview.window().is_webview_window() {
       Ok(Self { webview })
     } else {
       Err(InvokeError::from_anyhow(anyhow::anyhow!(

+ 16 - 2
core/tauri/src/window/mod.rs

@@ -866,7 +866,7 @@ pub struct Window<R: Runtime> {
   #[cfg(desktop)]
   pub(crate) menu: Arc<std::sync::Mutex<Option<WindowMenu<R>>>>,
   /// Whether this window is a Webview window (hosts only a single webview) or a container for multiple webviews
-  pub(crate) is_webview_window: bool,
+  is_webview_window: bool,
 }
 
 impl<R: Runtime> std::fmt::Debug for Window<R> {
@@ -981,7 +981,17 @@ impl<R: Runtime> Window<R> {
     position: P,
     size: S,
   ) -> crate::Result<Webview<R>> {
-    webview_builder.build(self.clone(), position.into(), size.into())
+    use std::sync::mpsc::channel;
+
+    let (tx, rx) = channel();
+    let position = position.into();
+    let size = size.into();
+    let window_ = self.clone();
+    self.run_on_main_thread(move || {
+      let res = webview_builder.build(window_, position, size);
+      tx.send(res.map_err(Into::into)).unwrap();
+    })?;
+    rx.recv().unwrap()
   }
 
   /// List of webviews associated with this window.
@@ -996,6 +1006,10 @@ impl<R: Runtime> Window<R> {
       .collect()
   }
 
+  pub(crate) fn is_webview_window(&self) -> bool {
+    self.is_webview_window
+  }
+
   /// Runs the given closure on the main thread.
   pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
     self