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
This commit is contained in:
Lucas Fernandes Nogueira 2024-02-27 17:36:21 -03:00 committed by GitHub
parent de38c31163
commit 222a96b74b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 76 additions and 38 deletions

View File

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

5
.changes/fix-reparent.md Normal file
View File

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

View File

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

View File

@ -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 {

View File

@ -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" }

View File

@ -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),

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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!(

View File

@ -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