feat(webview): add reparent API (#8939)

* feat(webview): add reparent API

* fix build

* fix import

* remove cfg

* fix windows

* clone

* clone value

* ()
This commit is contained in:
Lucas Fernandes Nogueira 2024-02-22 08:52:27 -03:00 committed by GitHub
parent b5eb64728a
commit fdcaf935fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 368 additions and 199 deletions

8
.changes/reparent.md Normal file
View File

@ -0,0 +1,8 @@
---
"@tauri-apps/api": patch:feat
"tauri": patch:feat
"tauri-runtime": patch:feat
"tauri-runtime-wry": patch:feat
---
Added the `reparent` function to the webview API.

View File

@ -170,7 +170,11 @@ macro_rules! webview_getter {
getter!(
$self,
rx,
Message::Webview($self.window_id, $self.webview_id, $message(tx))
Message::Webview(
*$self.window_id.lock().unwrap(),
$self.webview_id,
$message(tx)
)
)
}};
}
@ -280,7 +284,7 @@ impl<T: UserEvent> Context<T> {
let detached_webview = webview_id.map(|id| DetachedWebview {
label: label.clone(),
dispatcher: WryWebviewDispatcher {
window_id,
window_id: Arc::new(Mutex::new(window_id)),
webview_id: id,
context: self.clone(),
},
@ -304,6 +308,9 @@ impl<T: UserEvent> Context<T> {
let webview_id = self.next_webview_id();
let window_id_wrapper = Arc::new(Mutex::new(window_id));
let window_id_wrapper_ = window_id_wrapper.clone();
send_user_message(
self,
Message::CreateWebview(
@ -312,7 +319,7 @@ impl<T: UserEvent> Context<T> {
create_webview(
WebviewKind::WindowChild,
window,
window_id,
window_id_wrapper_,
webview_id,
&context,
pending,
@ -322,7 +329,7 @@ impl<T: UserEvent> Context<T> {
)?;
let dispatcher = WryWebviewDispatcher {
window_id,
window_id: window_id_wrapper,
webview_id,
context: self.clone(),
};
@ -1170,6 +1177,7 @@ pub enum WebviewMessage {
SetPosition(Position),
SetSize(Size),
SetFocus,
Reparent(WindowId),
// Getters
Url(Sender<Url>),
Position(Sender<PhysicalPosition<i32>>),
@ -1220,7 +1228,7 @@ impl<T: UserEvent> Clone for Message<T> {
/// The Tauri [`WebviewDispatch`] for [`Wry`].
#[derive(Debug, Clone)]
pub struct WryWebviewDispatcher<T: UserEvent> {
window_id: WindowId,
window_id: Arc<Mutex<WindowId>>,
webview_id: WebviewId,
context: Context<T>,
}
@ -1235,7 +1243,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
let id = self.context.next_webview_event_id();
let _ = self.context.proxy.send_event(Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::AddEventListener(id, Box::new(f)),
));
@ -1246,7 +1254,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))),
),
@ -1258,7 +1266,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
let _ = send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::OpenDevTools,
),
@ -1270,7 +1278,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
let _ = send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::CloseDevTools,
),
@ -1303,7 +1311,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::Navigate(url),
),
@ -1313,14 +1321,22 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
fn print(&self) -> Result<()> {
send_user_message(
&self.context,
Message::Webview(self.window_id, self.webview_id, WebviewMessage::Print),
Message::Webview(
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::Print,
),
)
}
fn close(&self) -> Result<()> {
send_user_message(
&self.context,
Message::Webview(self.window_id, self.webview_id, WebviewMessage::Close),
Message::Webview(
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::Close,
),
)
}
@ -1328,7 +1344,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::SetSize(size),
),
@ -1339,7 +1355,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::SetPosition(position),
),
@ -1349,10 +1365,29 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
fn set_focus(&self) -> Result<()> {
send_user_message(
&self.context,
Message::Webview(self.window_id, self.webview_id, WebviewMessage::SetFocus),
Message::Webview(
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::SetFocus,
),
)
}
fn reparent(&self, window_id: WindowId) -> Result<()> {
let mut current_window_id = self.window_id.lock().unwrap();
send_user_message(
&self.context,
Message::Webview(
*current_window_id,
self.webview_id,
WebviewMessage::Reparent(window_id),
),
)?;
*current_window_id = window_id;
Ok(())
}
#[cfg(all(feature = "tracing", not(target_os = "android")))]
fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
// use a channel so the EvaluateScript task uses the current span as parent
@ -1361,7 +1396,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
self,
rx,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()),
)
@ -1373,7 +1408,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
send_user_message(
&self.context,
Message::Webview(
self.window_id,
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::EvaluateScript(script.into()),
),
@ -2235,7 +2270,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let detached_webview = webview_id.map(|id| DetachedWebview {
label: label.clone(),
dispatcher: WryWebviewDispatcher {
window_id,
window_id: Arc::new(Mutex::new(window_id)),
webview_id: id,
context: self.context.clone(),
},
@ -2265,12 +2300,14 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
.get(&window_id)
.and_then(|w| w.inner.clone());
if let Some(window) = window {
let window_id_wrapper = Arc::new(Mutex::new(window_id));
let webview_id = self.context.next_webview_id();
let webview = create_webview(
WebviewKind::WindowChild,
&window,
window_id,
window_id_wrapper.clone(),
webview_id,
&self.context,
pending,
@ -2290,7 +2327,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
});
let dispatcher = WryWebviewDispatcher {
window_id,
window_id: window_id_wrapper,
webview_id,
context: self.context.clone(),
};
@ -2685,87 +2722,33 @@ fn handle_user_message<T: UserEvent>(
}
}
}
Message::Webview(window_id, webview_id, webview_message) => {
let webview_handle = windows.0.borrow().get(&window_id).map(|w| {
(
w.inner.clone(),
w.webviews.iter().find(|w| w.id == webview_id).cloned(),
)
});
if let Some((Some(window), Some(webview))) = webview_handle {
match webview_message {
WebviewMessage::WebviewEvent(_) => { /* already handled */ }
WebviewMessage::SynthesizedWindowEvent(_) => { /* already handled */ }
if let WebviewMessage::Reparent(new_parent_window_id) = webview_message {
let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| {
w.webviews
.iter()
.position(|w| w.id == webview_id)
.map(|webview_index| w.webviews.remove(webview_index))
});
WebviewMessage::AddEventListener(id, listener) => {
webview
.webview_event_listeners
.lock()
.unwrap()
.insert(id, listener);
}
#[cfg(all(feature = "tracing", not(target_os = "android")))]
WebviewMessage::EvaluateScript(script, tx, span) => {
let _span = span.entered();
if let Err(e) = webview.evaluate_script(&script) {
debug_eprintln!("{}", e);
if let Some(webview) = webview_handle {
if let Some((Some(new_parent_window), new_parent_window_webviews)) = windows
.0
.borrow_mut()
.get_mut(&new_parent_window_id)
.map(|w| (w.inner.clone(), &mut w.webviews))
{
#[cfg(target_os = "macos")]
{
use wry::WebViewExtMacOS;
webview.inner.reparent(new_parent_window.ns_window() as _);
new_parent_window_webviews.push(webview);
}
tx.send(()).unwrap();
}
#[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
WebviewMessage::EvaluateScript(script) => {
if let Err(e) = webview.evaluate_script(&script) {
debug_eprintln!("{}", e);
#[cfg(windows)]
{
webview.inner.reparent(new_parent_window.hwnd());
new_parent_window_webviews.push(webview);
}
}
WebviewMessage::Navigate(url) => webview.load_url(url.as_str()),
WebviewMessage::Print => {
let _ = webview.print();
}
WebviewMessage::Close => {
windows.0.borrow_mut().get_mut(&window_id).map(|window| {
if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) {
window.webviews.remove(i);
}
window
});
}
WebviewMessage::SetSize(size) => {
let mut bounds = webview.bounds();
let size = size.to_logical(window.scale_factor());
bounds.width = size.width;
bounds.height = size.height;
if let Some(b) = &webview.bounds {
let window_size = window.inner_size();
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;
}
webview.set_bounds(bounds);
}
WebviewMessage::SetPosition(position) => {
let mut bounds = webview.bounds();
let position = position.to_logical(window.scale_factor());
bounds.x = position.x;
bounds.y = position.y;
if let Some(b) = &webview.bounds {
let window_size = window.inner_size();
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;
}
webview.set_bounds(bounds);
}
WebviewMessage::SetFocus => {
webview.focus();
}
WebviewMessage::WithWebview(f) => {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
@ -2774,68 +2757,163 @@ fn handle_user_message<T: UserEvent>(
target_os = "openbsd"
))]
{
f(webview.webview());
if let Some(container) = new_parent_window.default_vbox() {
webview.inner.reparent(container);
new_parent_window_webviews.push(webview);
}
}
#[cfg(target_os = "macos")]
{
use wry::WebViewExtMacOS;
f(Webview {
webview: webview.webview(),
manager: webview.manager(),
ns_window: webview.ns_window(),
}
}
} else {
let webview_handle = windows.0.borrow().get(&window_id).map(|w| {
(
w.inner.clone(),
w.webviews.iter().find(|w| w.id == webview_id).cloned(),
)
});
if let Some((Some(window), Some(webview))) = webview_handle {
match webview_message {
WebviewMessage::WebviewEvent(_) => { /* already handled */ }
WebviewMessage::SynthesizedWindowEvent(_) => { /* already handled */ }
WebviewMessage::Reparent(_window_id) => { /* already handled */ }
WebviewMessage::AddEventListener(id, listener) => {
webview
.webview_event_listeners
.lock()
.unwrap()
.insert(id, listener);
}
#[cfg(all(feature = "tracing", not(target_os = "android")))]
WebviewMessage::EvaluateScript(script, tx, span) => {
let _span = span.entered();
if let Err(e) = webview.evaluate_script(&script) {
debug_eprintln!("{}", e);
}
tx.send(()).unwrap();
}
#[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
WebviewMessage::EvaluateScript(script) => {
if let Err(e) = webview.evaluate_script(&script) {
debug_eprintln!("{}", e);
}
}
WebviewMessage::Navigate(url) => webview.load_url(url.as_str()),
WebviewMessage::Print => {
let _ = webview.print();
}
WebviewMessage::Close => {
windows.0.borrow_mut().get_mut(&window_id).map(|window| {
if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) {
window.webviews.remove(i);
}
window
});
}
#[cfg(target_os = "ios")]
{
use tao::platform::ios::WindowExtIOS;
use wry::WebViewExtIOS;
WebviewMessage::SetSize(size) => {
let mut bounds = webview.bounds();
let size = size.to_logical(window.scale_factor());
bounds.width = size.width;
bounds.height = size.height;
f(Webview {
webview: webview.inner.webview(),
manager: webview.inner.manager(),
view_controller: window.ui_view_controller() as cocoa::base::id,
});
}
#[cfg(windows)]
{
f(Webview {
controller: webview.controller(),
});
}
#[cfg(target_os = "android")]
{
f(webview.handle())
}
}
if let Some(b) = &webview.bounds {
let window_size = window.inner_size();
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;
}
#[cfg(any(debug_assertions, feature = "devtools"))]
WebviewMessage::OpenDevTools => {
webview.open_devtools();
}
#[cfg(any(debug_assertions, feature = "devtools"))]
WebviewMessage::CloseDevTools => {
webview.close_devtools();
}
#[cfg(any(debug_assertions, feature = "devtools"))]
WebviewMessage::IsDevToolsOpen(tx) => {
tx.send(webview.is_devtools_open()).unwrap();
}
webview.set_bounds(bounds);
}
WebviewMessage::SetPosition(position) => {
let mut bounds = webview.bounds();
let position = position.to_logical(window.scale_factor());
bounds.x = position.x;
bounds.y = position.y;
// Getters
WebviewMessage::Url(tx) => {
tx.send(webview.url().parse().unwrap()).unwrap();
}
WebviewMessage::Position(tx) => {
let bounds = webview.bounds();
let position =
LogicalPosition::new(bounds.x, bounds.y).to_physical(window.scale_factor());
tx.send(position).unwrap();
}
WebviewMessage::Size(tx) => {
let bounds = webview.bounds();
let size =
LogicalSize::new(bounds.width, bounds.height).to_physical(window.scale_factor());
tx.send(size).unwrap();
if let Some(b) = &webview.bounds {
let window_size = window.inner_size();
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;
}
webview.set_bounds(bounds);
}
// Getters
WebviewMessage::Url(tx) => {
tx.send(webview.url().parse().unwrap()).unwrap();
}
WebviewMessage::Position(tx) => {
let bounds = webview.bounds();
let position =
LogicalPosition::new(bounds.x, bounds.y).to_physical(window.scale_factor());
tx.send(position).unwrap();
}
WebviewMessage::Size(tx) => {
let bounds = webview.bounds();
let size =
LogicalSize::new(bounds.width, bounds.height).to_physical(window.scale_factor());
tx.send(size).unwrap();
}
WebviewMessage::SetFocus => {
webview.focus();
}
WebviewMessage::WithWebview(f) => {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
{
f(webview.webview());
}
#[cfg(target_os = "macos")]
{
use wry::WebViewExtMacOS;
f(Webview {
webview: webview.webview(),
manager: webview.manager(),
ns_window: webview.ns_window(),
});
}
#[cfg(target_os = "ios")]
{
use tao::platform::ios::WindowExtIOS;
use wry::WebViewExtIOS;
f(Webview {
webview: webview.inner.webview(),
manager: webview.inner.manager(),
view_controller: window.ui_view_controller() as cocoa::base::id,
});
}
#[cfg(windows)]
{
f(Webview {
controller: webview.controller(),
});
}
#[cfg(target_os = "android")]
{
f(webview.handle())
}
}
#[cfg(any(debug_assertions, feature = "devtools"))]
WebviewMessage::OpenDevTools => {
webview.open_devtools();
}
#[cfg(any(debug_assertions, feature = "devtools"))]
WebviewMessage::CloseDevTools => {
webview.close_devtools();
}
#[cfg(any(debug_assertions, feature = "devtools"))]
WebviewMessage::IsDevToolsOpen(tx) => {
tx.send(webview.is_devtools_open()).unwrap();
}
}
}
}
@ -3341,7 +3419,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
webviews.push(create_webview(
WebviewKind::WindowContent,
&window,
window_id,
Arc::new(Mutex::new(window_id)),
webview_id,
context,
webview,
@ -3390,7 +3468,7 @@ enum WebviewKind {
WindowChild,
}
#[derive(Clone)]
#[derive(Debug, Clone)]
struct WebviewBounds {
x_rate: f32,
y_rate: f32,
@ -3401,7 +3479,7 @@ struct WebviewBounds {
fn create_webview<T: UserEvent>(
kind: WebviewKind,
window: &Window,
window_id: WindowId,
window_id: Arc<Mutex<WindowId>>,
id: WebviewId,
context: &Context<T>,
pending: PendingWebview<T, Wry<T>>,
@ -3470,6 +3548,7 @@ fn create_webview<T: UserEvent>(
if webview_attributes.file_drop_handler_enabled {
let proxy = context.proxy.clone();
let window_id_ = window_id.clone();
webview_builder = webview_builder.with_file_drop_handler(move |event| {
let event = match event {
WryFileDropEvent::Hovered {
@ -3496,7 +3575,7 @@ fn create_webview<T: UserEvent>(
WebviewMessage::WebviewEvent(WebviewEvent::FileDrop(event))
};
let _ = proxy.send_event(Message::Webview(window_id, id, message));
let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message));
true
});
}
@ -3598,7 +3677,7 @@ fn create_webview<T: UserEvent>(
webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
kind,
window_id,
window_id.clone(),
id,
context.clone(),
label.clone(),
@ -3696,12 +3775,13 @@ fn create_webview<T: UserEvent>(
let controller = webview.controller();
let proxy = context.proxy.clone();
let proxy_ = proxy.clone();
let window_id_ = window_id.clone();
let mut token = EventRegistrationToken::default();
unsafe {
controller.add_GotFocus(
&FocusChangedEventHandler::create(Box::new(move |_, _| {
let _ = proxy_.send_event(Message::Webview(
window_id,
let _ = proxy.send_event(Message::Webview(
*window_id_.lock().unwrap(),
id,
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)),
));
@ -3714,8 +3794,8 @@ fn create_webview<T: UserEvent>(
unsafe {
controller.add_LostFocus(
&FocusChangedEventHandler::create(Box::new(move |_, _| {
let _ = proxy.send_event(Message::Webview(
window_id,
let _ = proxy_.send_event(Message::Webview(
*window_id.lock().unwrap(),
id,
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)),
));
@ -3745,7 +3825,7 @@ fn create_webview<T: UserEvent>(
/// Create a wry ipc handler from a tauri ipc handler.
fn create_ipc_handler<T: UserEvent>(
_kind: WebviewKind,
window_id: WindowId,
window_id: Arc<Mutex<WindowId>>,
webview_id: WebviewId,
context: Context<T>,
label: String,
@ -3754,7 +3834,7 @@ fn create_ipc_handler<T: UserEvent>(
Box::new(move |request| {
#[cfg(windows)]
if _kind == WebviewKind::WindowContent
&& undecorated_resizing::handle_request(context.clone(), window_id, &request)
&& undecorated_resizing::handle_request(context.clone(), *window_id.lock().unwrap(), &request)
{
return;
}
@ -3764,7 +3844,7 @@ fn create_ipc_handler<T: UserEvent>(
DetachedWebview {
label: label.clone(),
dispatcher: WryWebviewDispatcher {
window_id,
window_id: window_id.clone(),
webview_id,
context: context.clone(),
},

View File

@ -449,6 +449,9 @@ pub trait WebviewDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + '
/// Executes javascript on the window this [`WindowDispatch`] represents.
fn eval_script<S: Into<String>>(&self, script: S) -> Result<()>;
/// Moves the webview to the given window.
fn reparent(&self, window_id: WindowId) -> Result<()>;
}
/// Window dispatcher. A thread-safe handle to the window APIs.

View File

@ -122,6 +122,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("set_webview_position", false),
("set_webview_focus", false),
("print", false),
("reparent", false),
// internal
("internal_toggle_devtools", true),
],

View File

@ -32,6 +32,14 @@ Enables the print command without any pre-configured scope.
Denies the print command without any pre-configured scope.
## allow-reparent
Enables the reparent command without any pre-configured scope.
## deny-reparent
Denies the reparent command without any pre-configured scope.
## allow-set-webview-focus
Enables the set_webview_focus command without any pre-configured scope.

File diff suppressed because one or more lines are too long

View File

@ -244,11 +244,13 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
}
}
match invoke_message.unwrap_or_else(|| {
let message = invoke_message.unwrap_or_else(|| {
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("ipc::request::deserialize").entered();
serde_json::from_str::<Message>(&message).map_err(Into::into)
}) {
});
match message {
Ok(message) => {
let request = InvokeRequest {
cmd: message.cmd,

View File

@ -540,6 +540,10 @@ impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
fn set_focus(&self) -> Result<()> {
Ok(())
}
fn reparent(&self, window_id: WindowId) -> Result<()> {
Ok(())
}
}
impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {

View File

@ -606,7 +606,7 @@ tauri::Builder::default()
.webviews_lock()
.values()
.map(|w| WebviewLabelDef {
window_label: w.window.label().to_string(),
window_label: w.window().label().to_string(),
label: w.label().to_string(),
})
.collect::<Vec<_>>();
@ -794,7 +794,10 @@ fn main() {
/// Webview.
#[default_runtime(crate::Wry, wry)]
pub struct Webview<R: Runtime> {
pub(crate) window: Window<R>,
window_label: Arc<Mutex<String>>,
/// The manager to associate this webview with.
pub(crate) manager: Arc<AppManager<R>>,
pub(crate) app_handle: AppHandle<R>,
/// The webview created by the runtime.
pub(crate) webview: DetachedWebview<EventLoopMessage, R>,
}
@ -802,7 +805,7 @@ pub struct Webview<R: Runtime> {
impl<R: Runtime> std::fmt::Debug for Webview<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Window")
.field("window", &self.window)
.field("window_label", &self.window_label)
.field("webview", &self.webview)
.finish()
}
@ -811,7 +814,9 @@ impl<R: Runtime> std::fmt::Debug for Webview<R> {
impl<R: Runtime> Clone for Webview<R> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
window_label: self.window_label.clone(),
manager: self.manager.clone(),
app_handle: self.app_handle.clone(),
webview: self.webview.clone(),
}
}
@ -836,7 +841,12 @@ impl<R: Runtime> PartialEq for Webview<R> {
impl<R: Runtime> Webview<R> {
/// Create a new webview that is attached to the window.
pub(crate) fn new(window: Window<R>, webview: DetachedWebview<EventLoopMessage, R>) -> Self {
Self { window, webview }
Self {
window_label: Arc::new(Mutex::new(window.label().into())),
manager: window.manager.clone(),
app_handle: window.app_handle.clone(),
webview,
}
}
/// Initializes a webview builder with the given window label and URL to load on the webview.
@ -883,8 +893,9 @@ impl<R: Runtime> Webview<R> {
/// Closes this webview.
pub fn close(&self) -> crate::Result<()> {
if self.window.is_webview_window {
self.window.close()
let window = self.window();
if window.is_webview_window {
window.close()
} else {
self.webview.dispatcher.close()?;
self.manager().on_webview_close(self.label());
@ -894,8 +905,9 @@ impl<R: Runtime> Webview<R> {
/// Resizes this webview.
pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
if self.window.is_webview_window {
self.window.set_size(size.into())
let window = self.window();
if window.is_webview_window {
window.set_size(size.into())
} else {
self
.webview
@ -907,8 +919,9 @@ impl<R: Runtime> Webview<R> {
/// Sets this webviews's position.
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
if self.window.is_webview_window {
self.window.set_position(position.into())
let window = self.window();
if window.is_webview_window {
window.set_position(position.into())
} else {
self
.webview
@ -923,13 +936,23 @@ impl<R: Runtime> Webview<R> {
self.webview.dispatcher.set_focus().map_err(Into::into)
}
/// 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 {
self.webview.dispatcher.reparent(window.window.id)?;
}
Ok(())
}
/// Returns the webview position.
///
/// - For child webviews, returns the position of the top-left hand corner of the webviews's client area relative to the top-left hand corner of the parent window.
/// - For webview window, returns the inner position of the window.
pub fn position(&self) -> crate::Result<PhysicalPosition<i32>> {
if self.window.is_webview_window {
self.window.inner_position()
let window = self.window();
if window.is_webview_window {
window.inner_position()
} else {
self.webview.dispatcher.position().map_err(Into::into)
}
@ -937,8 +960,9 @@ impl<R: Runtime> Webview<R> {
/// Returns the physical size of the webviews's client area.
pub fn size(&self) -> crate::Result<PhysicalSize<u32>> {
if self.window.is_webview_window {
self.window.inner_size()
let window = self.window();
if window.is_webview_window {
window.inner_size()
} else {
self.webview.dispatcher.size().map_err(Into::into)
}
@ -948,8 +972,11 @@ impl<R: Runtime> Webview<R> {
/// Webview APIs.
impl<R: Runtime> Webview<R> {
/// The window that is hosting this webview.
pub fn window(&self) -> &Window<R> {
&self.window
pub fn window(&self) -> Window<R> {
self
.manager
.get_window(&self.window_label.lock().unwrap())
.expect("could not locate webview parent window")
}
/// Executes a closure, providing it with the webview handle that is specific to the current platform.
@ -1099,7 +1126,7 @@ fn main() {
);
#[cfg(mobile)]
let app_handle = self.window.app_handle.clone();
let app_handle = self.app_handle.clone();
let message = InvokeMessage::new(
self,
@ -1415,7 +1442,7 @@ tauri::Builder::default()
where
F: Fn(Event) + Send + 'static,
{
self.window.manager.listen(
self.manager.listen(
event.into(),
EventTarget::Webview {
label: self.label().to_string(),
@ -1454,7 +1481,7 @@ tauri::Builder::default()
"####
)]
pub fn unlisten(&self, id: EventId) {
self.window.manager.unlisten(id)
self.manager.unlisten(id)
}
/// Listen to an event on this webview only once.
@ -1464,7 +1491,7 @@ tauri::Builder::default()
where
F: FnOnce(Event) + Send + 'static,
{
self.window.manager.once(
self.manager.once(
event.into(),
EventTarget::Webview {
label: self.label().to_string(),
@ -1478,19 +1505,19 @@ impl<R: Runtime> Manager<R> for Webview<R> {}
impl<R: Runtime> ManagerBase<R> for Webview<R> {
fn manager(&self) -> &AppManager<R> {
&self.window.manager
&self.manager
}
fn manager_owned(&self) -> Arc<AppManager<R>> {
self.window.manager.clone()
self.manager.clone()
}
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
self.window.app_handle.runtime()
self.app_handle.runtime()
}
fn managed_app_handle(&self) -> &AppHandle<R> {
&self.window.app_handle
&self.app_handle
}
}

View File

@ -159,6 +159,19 @@ mod desktop_commands {
setter!(set_webview_position, set_position, Position);
setter!(set_webview_focus, set_focus);
#[command(root = "crate")]
pub async fn reparent<R: Runtime>(
webview: crate::Webview<R>,
label: Option<String>,
window: String,
) -> crate::Result<()> {
let webview = get_webview(webview, label)?;
if let Some(window) = webview.manager.get_window(&window) {
webview.reparent(&window)?;
}
Ok(())
}
#[cfg(any(debug_assertions, feature = "devtools"))]
#[command(root = "crate")]
pub async fn internal_toggle_devtools<R: Runtime>(
@ -227,6 +240,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
desktop_commands::set_webview_position,
desktop_commands::set_webview_focus,
desktop_commands::print,
desktop_commands::reparent,
#[cfg(any(debug_assertions, feature = "devtools"))]
desktop_commands::internal_toggle_devtools,
]);

View File

@ -572,7 +572,7 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
/// - **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)?;
self.window_builder = self.window_builder.parent(&parent.webview.window())?;
Ok(self)
}
@ -586,7 +586,7 @@ 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)]
pub fn owner(mut self, owner: &WebviewWindow<R>) -> crate::Result<Self> {
self.window_builder = self.window_builder.owner(&owner.webview.window)?;
self.window_builder = self.window_builder.owner(&owner.webview.window())?;
Ok(self)
}
@ -638,7 +638,9 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
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)?;
self.window_builder = self
.window_builder
.transient_for(&parent.webview.window())?;
Ok(self)
}
@ -868,7 +870,9 @@ 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> {
self.webview.window().window_handle()
Ok(unsafe {
raw_window_handle::WindowHandle::borrow_raw(self.webview.window().window_handle()?.as_raw())
})
}
}

View File

@ -333,7 +333,7 @@ tauri::Builder::default()
.webviews_lock()
.values()
.map(|w| WebviewLabelDef {
window_label: w.window.label().to_string(),
window_label: w.window().label().to_string(),
label: w.label().to_string(),
})
.collect::<Vec<_>>();
@ -988,7 +988,7 @@ impl<R: Runtime> Window<R> {
.webview
.webviews_lock()
.values()
.filter(|w| w.window() == self)
.filter(|w| &w.window() == self)
.cloned()
.collect()
}

View File

@ -31,6 +31,7 @@ import {
} from './event'
import { invoke } from './core'
import { Window, getCurrent as getCurrentWindow } from './window'
import { WebviewWindow } from './webviewWindow'
interface FileDropPayload {
paths: string[]
@ -474,6 +475,23 @@ class Webview {
})
}
/**
* Moves this webview to the given label.
* @example
* ```typescript
* import { getCurrent } from '@tauri-apps/api/webview';
* await getCurrent().reparent('other-window');
* ```
*
* @returns A promise indicating the success or failure of the operation.
*/
async reparent(window: Window | WebviewWindow | string): Promise<void> {
return invoke('plugin:webview|set_webview_focus', {
label: this.label,
window: typeof window === 'string' ? window : window.label
})
}
// Listeners
/**