mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-08-17 11:50:26 +03:00
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:
parent
b5eb64728a
commit
fdcaf935fa
8
.changes/reparent.md
Normal file
8
.changes/reparent.md
Normal 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.
|
@ -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(),
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
],
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
]);
|
||||
|
@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user