diff --git a/tauri/Cargo.toml b/tauri/Cargo.toml index 61db7f979..41714b43e 100644 --- a/tauri/Cargo.toml +++ b/tauri/Cargo.toml @@ -32,7 +32,7 @@ thiserror = "1.0.23" once_cell = "1.5.2" tauri-api = { version = "0.7.5", path = "../tauri-api" } tauri-macros = { version = "0.1", path = "../tauri-macros" } -wry = { git = "https://github.com/tauri-apps/wry", rev = "b36b3e2d07edddf2ef49dcc901ae2c5888873ad2" } +wry = { git = "https://github.com/tauri-apps/wry", rev = "af07c28503e41a0a164cb7256fa0ec938d5daee4" } rand = "0.8" [target."cfg(target_os = \"windows\")".dependencies] diff --git a/tauri/examples/api/src-tauri/Cargo.lock b/tauri/examples/api/src-tauri/Cargo.lock index 3e9c74c00..b68825a07 100644 --- a/tauri/examples/api/src-tauri/Cargo.lock +++ b/tauri/examples/api/src-tauri/Cargo.lock @@ -3601,7 +3601,7 @@ dependencies = [ [[package]] name = "wry" version = "0.4.1" -source = "git+https://github.com/tauri-apps/wry?rev=b36b3e2d07edddf2ef49dcc901ae2c5888873ad2#b36b3e2d07edddf2ef49dcc901ae2c5888873ad2" +source = "git+https://github.com/tauri-apps/wry?rev=af07c28503e41a0a164cb7256fa0ec938d5daee4#af07c28503e41a0a164cb7256fa0ec938d5daee4" dependencies = [ "cc", "cocoa", diff --git a/tauri/examples/api/src-tauri/src/main.rs b/tauri/examples/api/src-tauri/src/main.rs index e4fdc8222..48ff38c16 100644 --- a/tauri/examples/api/src-tauri/src/main.rs +++ b/tauri/examples/api/src-tauri/src/main.rs @@ -18,7 +18,7 @@ struct Context; fn main() { tauri::AppBuilder::::new() .setup(|webview_manager| async move { - let dispatcher = webview_manager.current_webview().unwrap(); + let dispatcher = webview_manager.current_webview().await.unwrap(); let dispatcher_ = dispatcher.clone(); dispatcher.listen("js-event", move |msg| { println!("got js-event with message '{:?}'", msg); @@ -35,21 +35,16 @@ fn main() { use cmd::Cmd::*; match serde_json::from_str(&arg) { Err(e) => Err(e.into()), - Ok(command) => { - match command { - LogOperation { event, payload } => { - println!("{} {:?}", event, payload); - Ok(serde_json::Value::Null) - } - PerformRequest { - endpoint, - body, - } => { - println!("{} {:?}", endpoint, body); - Ok(serde_json::Value::String("message response".to_string())) - } + Ok(command) => match command { + LogOperation { event, payload } => { + println!("{} {:?}", event, payload); + Ok(serde_json::Value::Null) } - } + PerformRequest { endpoint, body } => { + println!("{} {:?}", endpoint, body); + Ok(serde_json::Value::String("message response".to_string())) + } + }, } }) .build() diff --git a/tauri/examples/communication/src-tauri/Cargo.lock b/tauri/examples/communication/src-tauri/Cargo.lock index 3e9c74c00..b68825a07 100644 --- a/tauri/examples/communication/src-tauri/Cargo.lock +++ b/tauri/examples/communication/src-tauri/Cargo.lock @@ -3601,7 +3601,7 @@ dependencies = [ [[package]] name = "wry" version = "0.4.1" -source = "git+https://github.com/tauri-apps/wry?rev=b36b3e2d07edddf2ef49dcc901ae2c5888873ad2#b36b3e2d07edddf2ef49dcc901ae2c5888873ad2" +source = "git+https://github.com/tauri-apps/wry?rev=af07c28503e41a0a164cb7256fa0ec938d5daee4#af07c28503e41a0a164cb7256fa0ec938d5daee4" dependencies = [ "cc", "cocoa", diff --git a/tauri/examples/communication/src-tauri/src/main.rs b/tauri/examples/communication/src-tauri/src/main.rs index 98b93e45b..8b5fd01df 100644 --- a/tauri/examples/communication/src-tauri/src/main.rs +++ b/tauri/examples/communication/src-tauri/src/main.rs @@ -19,7 +19,7 @@ struct Context; fn main() { tauri::AppBuilder::::new() .setup(|webview_manager| async move { - let dispatcher = webview_manager.current_webview().unwrap(); + let dispatcher = webview_manager.current_webview().await.unwrap(); let dispatcher_ = dispatcher.clone(); dispatcher.listen("js-event", move |msg| { println!("got js-event with message '{:?}'", msg); diff --git a/tauri/examples/multiwindow/src-tauri/Cargo.lock b/tauri/examples/multiwindow/src-tauri/Cargo.lock index 5b6469027..b3551ba3f 100644 --- a/tauri/examples/multiwindow/src-tauri/Cargo.lock +++ b/tauri/examples/multiwindow/src-tauri/Cargo.lock @@ -3513,7 +3513,7 @@ dependencies = [ [[package]] name = "wry" version = "0.4.1" -source = "git+https://github.com/tauri-apps/wry?rev=b36b3e2d07edddf2ef49dcc901ae2c5888873ad2#b36b3e2d07edddf2ef49dcc901ae2c5888873ad2" +source = "git+https://github.com/tauri-apps/wry?rev=af07c28503e41a0a164cb7256fa0ec938d5daee4#af07c28503e41a0a164cb7256fa0ec938d5daee4" dependencies = [ "cc", "cocoa", diff --git a/tauri/examples/multiwindow/src-tauri/src/main.rs b/tauri/examples/multiwindow/src-tauri/src/main.rs index 21a5580db..363d29089 100644 --- a/tauri/examples/multiwindow/src-tauri/src/main.rs +++ b/tauri/examples/multiwindow/src-tauri/src/main.rs @@ -15,7 +15,7 @@ fn main() { println!("got 'clicked' event on global channel"); }); } - let current_webview = webview_manager.current_webview().unwrap().clone(); + let current_webview = webview_manager.current_webview().await.unwrap(); let label = webview_manager.current_window_label().to_string(); current_webview.listen("clicked", move |_| { println!("got 'clicked' event on window '{}'", label) diff --git a/tauri/src/app.rs b/tauri/src/app.rs index 3a04991ff..d45730589 100644 --- a/tauri/src/app.rs +++ b/tauri/src/app.rs @@ -10,7 +10,7 @@ mod webview_manager; pub use webview::{ wry::WryApplication, ApplicationDispatcherExt, ApplicationExt, Callback, Icon, Message, - WebviewBuilderExt, WindowBuilderExt, + WebviewBuilderExt, }; pub use webview_manager::{WebviewDispatcher, WebviewManager}; diff --git a/tauri/src/app/event.rs b/tauri/src/app/event.rs index e1388f0e8..a5221774d 100644 --- a/tauri/src/app/event.rs +++ b/tauri/src/app/event.rs @@ -87,7 +87,7 @@ pub fn emit( event.as_ref(), js_payload, salt - )); + ))?; Ok(()) } diff --git a/tauri/src/app/runner.rs b/tauri/src/app/runner.rs index cfaa7919a..5579b33ed 100644 --- a/tauri/src/app/runner.rs +++ b/tauri/src/app/runner.rs @@ -13,10 +13,10 @@ use crate::{ ApplicationExt, WebviewBuilderExt, }; -use super::{App, ApplicationDispatcherExt, WebviewDispatcher, WebviewManager, WindowBuilderExt}; +use super::{App, ApplicationDispatcherExt, WebviewDispatcher, WebviewManager}; #[cfg(embedded_server)] use crate::api::tcp::{get_available_port, port_is_available}; -use crate::app::Context; +use crate::{app::Context, async_runtime::Mutex}; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; @@ -287,11 +287,15 @@ fn build_webview( let mut webview_application = A::new()?; - let mut window_refs = Vec::new(); - let mut dispatchers = HashMap::new(); + let dispatchers = Arc::new(Mutex::new(HashMap::new())); + let mut window_labels = Vec::new(); + + for window_config in &application.context.config.tauri.windows { + window_labels.push(window_config.label.to_string()); + } for window_config in application.context.config.tauri.windows.clone() { - let mut window = A::WindowBuilder::new() + let mut webview = A::WebviewBuilder::new() .title(window_config.title.to_string()) .width(window_config.width) .height(window_config.height) @@ -303,34 +307,24 @@ fn build_webview( .transparent(window_config.transparent) .always_on_top(window_config.always_on_top); if let Some(min_width) = window_config.min_width { - window = window.min_width(min_width); + webview = webview.min_width(min_width); } if let Some(min_height) = window_config.min_height { - window = window.min_height(min_height); + webview = webview.min_height(min_height); } if let Some(max_width) = window_config.max_width { - window = window.max_width(max_width); + webview = webview.max_width(max_width); } if let Some(max_height) = window_config.max_height { - window = window.max_height(max_height); + webview = webview.max_height(max_height); } if let Some(x) = window_config.x { - window = window.x(x); + webview = webview.x(x); } if let Some(y) = window_config.y { - window = window.y(y); + webview = webview.y(y); } - let window = webview_application.create_window(window)?; - let dispatcher = webview_application.dispatcher(&window); - dispatchers.insert( - window_config.label.to_string(), - WebviewDispatcher::new(dispatcher, window_config.label.to_string()), - ); - window_refs.push((window_config, window)); - } - - for (window_config, window) in window_refs { let webview_manager = WebviewManager::new(dispatchers.clone(), window_config.label.to_string()); let application = application.clone(); @@ -371,9 +365,11 @@ fn build_webview( } } Err(e) => { - if let Ok(dispatcher) = webview_manager.current_webview() { + if let Ok(dispatcher) = + crate::async_runtime::block_on(webview_manager.current_webview()) + { let error: crate::Error = e.into(); - dispatcher.eval(&format!( + let _ = dispatcher.eval(&format!( r#"console.error({})"#, JsonValue::String(error.to_string()) )); @@ -389,9 +385,7 @@ fn build_webview( WindowUrl::Custom(url) => url.to_string(), }; - webview_application.create_webview( - A::WebviewBuilder::new() - .url(webview_url) + webview = webview.url(webview_url) .initialization_script(&initialization_script) .initialization_script(&format!( r#" @@ -399,12 +393,15 @@ fn build_webview( window.__TAURI__.__currentWindow = {{ label: "{current_window_label}" }} "#, window_labels_array = - serde_json::to_string(&dispatchers.keys().collect::>()).unwrap(), + serde_json::to_string(&window_labels).unwrap(), current_window_label = window_config.label, - )), - window, - vec![tauri_invoke_handler], - )?; + )); + + let dispatcher = webview_application.create_webview(webview, vec![tauri_invoke_handler])?; + crate::async_runtime::block_on(dispatchers.lock()).insert( + window_config.label.to_string(), + WebviewDispatcher::new(dispatcher, window_config.label.to_string()), + ); crate::async_runtime::spawn(async move { crate::plugin::created(A::plugin_store(), &webview_manager).await @@ -437,8 +434,8 @@ async fn execute_promise< Ok(callback_string) => callback_string, Err(e) => format_callback(error_callback, e.to_string()), }; - if let Ok(dispatcher) = webview_manager.current_webview() { - dispatcher.eval(callback_string.as_str()); + if let Ok(dispatcher) = webview_manager.current_webview().await { + let _ = dispatcher.eval(callback_string.as_str()); } } diff --git a/tauri/src/app/webview.rs b/tauri/src/app/webview.rs index 3c6a270ab..65c1f1cbc 100644 --- a/tauri/src/app/webview.rs +++ b/tauri/src/app/webview.rs @@ -80,11 +80,20 @@ pub enum Message { SetIcon(Icon), } -/// The window builder. -pub trait WindowBuilderExt: Sized { - /// Initializes a new window builder. +/// The webview builder. +pub trait WebviewBuilderExt: Sized { + /// The webview object that this builder creates. + type Webview; + + /// Initializes a new webview builder. fn new() -> Self; + /// Sets the webview url. + fn url(self, url: String) -> Self; + + /// Sets the init script. + fn initialization_script(self, init: &str) -> Self; + /// The horizontal position of the window's top left corner. fn x(self, x: f64) -> Self; @@ -133,21 +142,6 @@ pub trait WindowBuilderExt: Sized { /// Whether the window should always be on top of other windows. fn always_on_top(self, always_on_top: bool) -> Self; -} - -/// The webview builder. -pub trait WebviewBuilderExt: Sized { - /// The webview object that this builder creates. - type Webview; - - /// Initializes a new webview builder. - fn new() -> Self; - - /// Sets the webview url. - fn url(self, url: String) -> Self; - - /// Sets the init script. - fn initialization_script(self, init: &str) -> Self; /// Builds the webview instance. fn finish(self) -> crate::Result; @@ -158,13 +152,76 @@ pub struct Callback { /// Function name to bind. pub name: String, /// Function callback handler. - pub function: Box) -> i32 + Send>, + pub function: Box) -> i32 + Send>, } /// Webview dispatcher. A thread-safe handle to the webview API. pub trait ApplicationDispatcherExt: Clone + Send + Sync + Sized { - /// Sends a message to the window. - fn send_message(&self, message: Message); + /// Updates the window resizable flag. + fn set_resizable(&self, resizable: bool) -> crate::Result<()>; + + /// Updates the window title. + fn set_title>(&self, title: S) -> crate::Result<()>; + + /// Maximizes the window. + fn maximize(&self) -> crate::Result<()>; + + /// Unmaximizes the window. + fn unmaximize(&self) -> crate::Result<()>; + + /// Minimizes the window. + fn minimize(&self) -> crate::Result<()>; + + /// Unminimizes the window. + fn unminimize(&self) -> crate::Result<()>; + + /// Shows the window. + fn show(&self) -> crate::Result<()>; + + /// Hides the window. + fn hide(&self) -> crate::Result<()>; + + /// Updates the transparency flag. + fn set_transparent(&self, resizable: bool) -> crate::Result<()>; + + /// Updates the hasDecorations flag. + fn set_decorations(&self, decorations: bool) -> crate::Result<()>; + + /// Updates the window alwaysOnTop flag. + fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()>; + + /// Updates the window width. + fn set_width(&self, width: f64) -> crate::Result<()>; + + /// Updates the window height. + fn set_height(&self, height: f64) -> crate::Result<()>; + + /// Resizes the window. + fn resize(&self, width: f64, height: f64) -> crate::Result<()>; + + /// Updates the window min size. + fn set_min_size(&self, min_width: f64, min_height: f64) -> crate::Result<()>; + + /// Updates the window max size. + fn set_max_size(&self, max_width: f64, max_height: f64) -> crate::Result<()>; + + /// Updates the X position. + fn set_x(&self, x: f64) -> crate::Result<()>; + + /// Updates the Y position. + fn set_y(&self, y: f64) -> crate::Result<()>; + + /// Updates the window position. + fn set_position(&self, x: f64, y: f64) -> crate::Result<()>; + + /// Updates the window fullscreen state. + fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()>; + + /// Updates the window icon. + fn set_icon(&self, icon: Icon) -> crate::Result<()>; + + /// Evals a script on the webview. + fn eval_script>(&self, script: S) -> crate::Result<()>; } /// The application interface. @@ -172,10 +229,6 @@ pub trait ApplicationDispatcherExt: Clone + Send + Sync + Sized { pub trait ApplicationExt: Sized { /// The webview builder. type WebviewBuilder: WebviewBuilderExt; - /// The window builder. - type WindowBuilder: WindowBuilderExt; - /// The window type. - type Window; /// The message dispatcher. type Dispatcher: ApplicationDispatcherExt; @@ -185,19 +238,12 @@ pub trait ApplicationExt: Sized { /// Creates a new application. fn new() -> crate::Result; - /// Gets the message dispatcher for the given window. - fn dispatcher(&self, window: &Self::Window) -> Self::Dispatcher; - - /// Creates a new window. - fn create_window(&self, window_builder: Self::WindowBuilder) -> crate::Result; - /// Creates a new webview. fn create_webview( &mut self, webview_builder: Self::WebviewBuilder, - window: Self::Window, callbacks: Vec>, - ) -> crate::Result<()>; + ) -> crate::Result; /// Run the application. fn run(self); diff --git a/tauri/src/app/webview/wry.rs b/tauri/src/app/webview/wry.rs index b42121ddb..14b1b2b8d 100644 --- a/tauri/src/app/webview/wry.rs +++ b/tauri/src/app/webview/wry.rs @@ -1,16 +1,11 @@ -use super::{ - ApplicationDispatcherExt, ApplicationExt, Callback, Icon, Message, WebviewBuilderExt, - WindowBuilderExt, -}; - -use wry::{ApplicationDispatcher, ApplicationExt as _, WebviewMessage, WindowExt, WindowMessage}; +use super::{ApplicationDispatcherExt, ApplicationExt, Callback, Icon, WebviewBuilderExt}; use once_cell::sync::Lazy; use crate::plugin::PluginStore; use std::{ - convert::{TryFrom, TryInto}, + convert::TryInto, sync::{Arc, Mutex}, }; @@ -29,11 +24,25 @@ impl TryInto for Icon { } } -impl WindowBuilderExt for wry::AppWindowAttributes { +/// The webview builder. +impl WebviewBuilderExt for wry::WebViewAttributes { + /// The webview object that this builder creates. + type Webview = Self; + fn new() -> Self { Default::default() } + fn url(mut self, url: String) -> Self { + self.url.replace(url); + self + } + + fn initialization_script(mut self, init: &str) -> Self { + self.initialization_scripts.push(init.to_string()); + self + } + fn x(mut self, x: f64) -> Self { self.x = Some(x); self @@ -113,26 +122,6 @@ impl WindowBuilderExt for wry::AppWindowAttributes { self.always_on_top = always_on_top; self } -} - -/// The webview builder. -impl WebviewBuilderExt for wry::WebViewAttributes { - /// The webview object that this builder creates. - type Webview = Self; - - fn new() -> Self { - Default::default() - } - - fn url(mut self, url: String) -> Self { - self.url.replace(url); - self - } - - fn initialization_script(mut self, init: &str) -> Self { - self.initialization_script.push(init.to_string()); - self - } fn finish(self) -> crate::Result { Ok(self) @@ -140,101 +129,215 @@ impl WebviewBuilderExt for wry::WebViewAttributes { } #[derive(Clone)] -pub struct WryDispatcher { - inner: Arc>>, - current_window: wry::WindowId, -} - -struct WryMessage(wry::Message); - -impl TryFrom<(wry::WindowId, Message)> for WryMessage { - type Error = crate::Error; - fn try_from((id, message): (wry::WindowId, Message)) -> crate::Result { - let message = match message { - Message::EvalScript(js) => wry::Message::Webview(id, WebviewMessage::EvalScript(js)), - Message::SetResizable(resizable) => { - wry::Message::Window(id, WindowMessage::SetResizable(resizable)) - } - Message::SetTitle(title) => wry::Message::Window(id, WindowMessage::SetTitle(title)), - Message::Maximize => wry::Message::Window(id, WindowMessage::Maximize), - Message::Unmaximize => wry::Message::Window(id, WindowMessage::Unmaximize), - Message::Minimize => wry::Message::Window(id, WindowMessage::Minimize), - Message::Unminimize => wry::Message::Window(id, WindowMessage::Unminimize), - Message::Show => wry::Message::Window(id, WindowMessage::Show), - Message::Hide => wry::Message::Window(id, WindowMessage::Hide), - Message::SetTransparent(transparent) => { - wry::Message::Window(id, WindowMessage::SetTransparent(transparent)) - } - Message::SetDecorations(decorations) => { - wry::Message::Window(id, WindowMessage::SetDecorations(decorations)) - } - Message::SetAlwaysOnTop(always_on_top) => { - wry::Message::Window(id, WindowMessage::SetAlwaysOnTop(always_on_top)) - } - Message::SetWidth(width) => wry::Message::Window(id, WindowMessage::SetWidth(width)), - Message::SetHeight(height) => wry::Message::Window(id, WindowMessage::SetHeight(height)), - Message::Resize { width, height } => { - wry::Message::Window(id, WindowMessage::Resize { width, height }) - } - Message::SetMinSize { - min_width, - min_height, - } => wry::Message::Window( - id, - WindowMessage::SetMinSize { - min_width, - min_height, - }, - ), - Message::SetMaxSize { - max_width, - max_height, - } => wry::Message::Window( - id, - WindowMessage::SetMaxSize { - max_width, - max_height, - }, - ), - Message::SetX(x) => wry::Message::Window(id, WindowMessage::SetX(x)), - Message::SetY(y) => wry::Message::Window(id, WindowMessage::SetY(y)), - Message::SetPosition { x, y } => { - wry::Message::Window(id, WindowMessage::SetPosition { x, y }) - } - Message::SetFullscreen(fullscreen) => { - wry::Message::Window(id, WindowMessage::SetFullscreen(fullscreen)) - } - Message::SetIcon(icon) => wry::Message::Window(id, WindowMessage::SetIcon(icon.try_into()?)), - }; - Ok(WryMessage(message)) - } -} +pub struct WryDispatcher(Arc>); impl ApplicationDispatcherExt for WryDispatcher { - fn send_message(&self, message: Message) { - let message_res: crate::Result = (self.current_window, message).try_into(); - // TODO error propagation - if let Ok(message) = message_res { - self - .inner - .lock() - .unwrap() - .dispatch_message(message.0) - .unwrap(); - } + fn set_resizable(&self, resizable: bool) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_resizable(resizable) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_title>(&self, title: S) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_title(title) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn maximize(&self) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .maximize() + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn unmaximize(&self) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .unmaximize() + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn minimize(&self) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .minimize() + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn unminimize(&self) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .unminimize() + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn show(&self) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .show() + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn hide(&self) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .hide() + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_transparent(&self, transparent: bool) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_transparent(transparent) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_decorations(&self, decorations: bool) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_decorations(decorations) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_always_on_top(always_on_top) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_width(&self, width: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_width(width) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_height(&self, height: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_height(height) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn resize(&self, width: f64, height: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .resize(width, height) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_min_size(&self, min_width: f64, min_height: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_min_size(min_width, min_height) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_max_size(&self, max_width: f64, max_height: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_max_size(max_width, max_height) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_x(&self, x: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_x(x) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_y(&self, y: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_y(y) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_position(&self, x: f64, y: f64) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_position(x, y) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_fullscreen(fullscreen) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn set_icon(&self, icon: Icon) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .set_icon(icon.try_into()?) + .map_err(|_| crate::Error::FailedToSendMessage) + } + + fn eval_script>(&self, script: S) -> crate::Result<()> { + self + .0 + .lock() + .unwrap() + .eval_script(script) + .map_err(|_| crate::Error::FailedToSendMessage) } } /// A wrapper around the wry Application interface. pub struct WryApplication { - inner: wry::Application<()>, - dispatcher_handle: Arc>>, + inner: wry::Application, } impl ApplicationExt for WryApplication { type WebviewBuilder = wry::WebViewAttributes; - type WindowBuilder = wry::AppWindowAttributes; - type Window = wry::Window; type Dispatcher = WryDispatcher; fn plugin_store() -> &'static PluginStore { @@ -244,61 +347,30 @@ impl ApplicationExt for WryApplication { fn new() -> crate::Result { let app = wry::Application::new().map_err(|_| crate::Error::CreateWebview)?; - let dispatcher = app.dispatcher(); - - Ok(Self { - inner: app, - dispatcher_handle: Arc::new(Mutex::new(dispatcher)), - }) - } - - fn dispatcher(&self, window: &Self::Window) -> Self::Dispatcher { - WryDispatcher { - inner: self.dispatcher_handle.clone(), - current_window: window.id(), - } - } - - fn create_window(&self, window_builder: Self::WindowBuilder) -> crate::Result { - let window = self - .inner - .create_window(window_builder) - .map_err(|_| crate::Error::CreateWindow)?; - Ok(window) + Ok(Self { inner: app }) } fn create_webview( &mut self, webview_builder: Self::WebviewBuilder, - window: Self::Window, callbacks: Vec>, - ) -> crate::Result<()> { + ) -> crate::Result { let mut wry_callbacks = Vec::new(); for mut callback in callbacks { - let dispatcher_handle = self.dispatcher_handle.clone(); - let window_id = window.id(); - let callback = wry::Callback { name: callback.name.to_string(), - function: Box::new(move |_, seq, req| { - (callback.function)( - &WryDispatcher { - inner: dispatcher_handle.clone(), - current_window: window_id, - }, - seq, - req, - ) + function: Box::new(move |dispatcher, seq, req| { + (callback.function)(WryDispatcher(Arc::new(Mutex::new(dispatcher))), seq, req) }), }; wry_callbacks.push(callback); } - self + let dispatcher = self .inner - .create_webview(window, webview_builder.finish()?, Some(wry_callbacks)) + .create_webview(webview_builder.finish()?, Some(wry_callbacks)) .map_err(|_| crate::Error::CreateWebview)?; - Ok(()) + Ok(WryDispatcher(Arc::new(Mutex::new(dispatcher)))) } fn run(self) { diff --git a/tauri/src/app/webview_manager.rs b/tauri/src/app/webview_manager.rs index 807b19805..1647d0c0d 100644 --- a/tauri/src/app/webview_manager.rs +++ b/tauri/src/app/webview_manager.rs @@ -1,6 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; -use super::{ApplicationDispatcherExt, Icon, Message}; +use super::{ApplicationDispatcherExt, Icon}; +use crate::async_runtime::Mutex; use serde::Serialize; @@ -48,182 +49,165 @@ impl WebviewDispatcher { } /// Evaluates a JS script. - pub fn eval(&self, js: &str) { - self - .dispatcher - .send_message(Message::EvalScript(js.to_string())) + pub fn eval(&self, js: &str) -> crate::Result<()> { + self.dispatcher.eval_script(js) } /// Updates the window resizable flag. - pub fn set_resizable(&self, resizable: bool) { - self - .dispatcher - .send_message(Message::SetResizable(resizable)) + pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> { + self.dispatcher.set_resizable(resizable) } /// Updates the window title. - pub fn set_title(&self, title: &str) { - self - .dispatcher - .send_message(Message::SetTitle(title.to_string())) + pub fn set_title(&self, title: &str) -> crate::Result<()> { + self.dispatcher.set_title(title.to_string()) } /// Maximizes the window. - pub fn maximize(&self) { - self.dispatcher.send_message(Message::Maximize) + pub fn maximize(&self) -> crate::Result<()> { + self.dispatcher.maximize() } /// Unmaximizes the window. - pub fn unmaximize(&self) { - self.dispatcher.send_message(Message::Unmaximize) + pub fn unmaximize(&self) -> crate::Result<()> { + self.dispatcher.unmaximize() } /// Minimizes the window. - pub fn minimize(&self) { - self.dispatcher.send_message(Message::Minimize) + pub fn minimize(&self) -> crate::Result<()> { + self.dispatcher.minimize() } /// Unminimizes the window. - pub fn unminimize(&self) { - self.dispatcher.send_message(Message::Unminimize) + pub fn unminimize(&self) -> crate::Result<()> { + self.dispatcher.unminimize() } /// Sets the window visibility to true. - pub fn show(&self) { - self.dispatcher.send_message(Message::Show) + pub fn show(&self) -> crate::Result<()> { + self.dispatcher.show() } /// Sets the window visibility to false. - pub fn hide(&self) { - self.dispatcher.send_message(Message::Hide) + pub fn hide(&self) -> crate::Result<()> { + self.dispatcher.hide() } /// Sets the window transparent flag. - pub fn set_transparent(&self, transparent: bool) { - self - .dispatcher - .send_message(Message::SetTransparent(transparent)) + pub fn set_transparent(&self, transparent: bool) -> crate::Result<()> { + self.dispatcher.set_transparent(transparent) } /// Whether the window should have borders and bars. - pub fn set_decorations(&self, decorations: bool) { - self - .dispatcher - .send_message(Message::SetDecorations(decorations)) + pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> { + self.dispatcher.set_decorations(decorations) } /// Whether the window should always be on top of other windows. - pub fn set_always_on_top(&self, always_on_top: bool) { - self - .dispatcher - .send_message(Message::SetAlwaysOnTop(always_on_top)) + pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> { + self.dispatcher.set_always_on_top(always_on_top) } /// Sets the window width. - pub fn set_width(&self, width: impl Into) { - self - .dispatcher - .send_message(Message::SetWidth(width.into())) + pub fn set_width(&self, width: impl Into) -> crate::Result<()> { + self.dispatcher.set_width(width.into()) } /// Sets the window height. - pub fn set_height(&self, height: impl Into) { - self - .dispatcher - .send_message(Message::SetHeight(height.into())) + pub fn set_height(&self, height: impl Into) -> crate::Result<()> { + self.dispatcher.set_height(height.into()) } /// Resizes the window. - pub fn resize(&self, width: impl Into, height: impl Into) { - self.dispatcher.send_message(Message::Resize { - width: width.into(), - height: height.into(), - }) + pub fn resize(&self, width: impl Into, height: impl Into) -> crate::Result<()> { + self.dispatcher.resize(width.into(), height.into()) } /// Sets the window min size. - pub fn set_min_size(&self, min_width: impl Into, min_height: impl Into) { - self.dispatcher.send_message(Message::SetMinSize { - min_width: min_width.into(), - min_height: min_height.into(), - }) + pub fn set_min_size( + &self, + min_width: impl Into, + min_height: impl Into, + ) -> crate::Result<()> { + self + .dispatcher + .set_min_size(min_width.into(), min_height.into()) } /// Sets the window max size. - pub fn set_max_size(&self, max_width: impl Into, max_height: impl Into) { - self.dispatcher.send_message(Message::SetMaxSize { - max_width: max_width.into(), - max_height: max_height.into(), - }) + pub fn set_max_size( + &self, + max_width: impl Into, + max_height: impl Into, + ) -> crate::Result<()> { + self + .dispatcher + .set_max_size(max_width.into(), max_height.into()) } /// Sets the window x position. - pub fn set_x(&self, x: impl Into) { - self.dispatcher.send_message(Message::SetX(x.into())) + pub fn set_x(&self, x: impl Into) -> crate::Result<()> { + self.dispatcher.set_x(x.into()) } /// Sets the window y position. - pub fn set_y(&self, y: impl Into) { - self.dispatcher.send_message(Message::SetY(y.into())) + pub fn set_y(&self, y: impl Into) -> crate::Result<()> { + self.dispatcher.set_y(y.into()) } /// Sets the window position. - pub fn set_position(&self, x: impl Into, y: impl Into) { - self.dispatcher.send_message(Message::SetPosition { - x: x.into(), - y: y.into(), - }) + pub fn set_position(&self, x: impl Into, y: impl Into) -> crate::Result<()> { + self.dispatcher.set_position(x.into(), y.into()) } /// Sets the window fullscreen state. - pub fn set_fullscreen(&self, fullscreen: bool) { - self - .dispatcher - .send_message(Message::SetFullscreen(fullscreen)) + pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> { + self.dispatcher.set_fullscreen(fullscreen) } /// Sets the window icon. - pub fn set_icon(&self, icon: Icon) { - self.dispatcher.send_message(Message::SetIcon(icon)) + pub fn set_icon(&self, icon: Icon) -> crate::Result<()> { + self.dispatcher.set_icon(icon) } } /// The webview manager. #[derive(Clone)] pub struct WebviewManager { - dispatchers: HashMap>, + dispatchers: Arc>>>, current_webview_window_label: String, } impl WebviewManager { - pub(crate) fn new(dispatchers: HashMap>, label: String) -> Self { + pub(crate) fn new( + dispatchers: Arc>>>, + label: String, + ) -> Self { Self { dispatchers, current_webview_window_label: label, } } - /// Gets the map of the current webviews. - pub fn webviews(&self) -> &HashMap> { - &self.dispatchers - } - /// Returns the label of the window associated with the current context. pub fn current_window_label(&self) -> &str { &self.current_webview_window_label } /// Gets the webview associated with the current context. - pub fn current_webview(&self) -> crate::Result<&WebviewDispatcher> { - self.get_webview(&self.current_webview_window_label) + pub async fn current_webview(&self) -> crate::Result> { + self.get_webview(&self.current_webview_window_label).await } /// Gets the webview associated with the given window label. - pub fn get_webview(&self, window_label: &str) -> crate::Result<&WebviewDispatcher> { + pub async fn get_webview(&self, window_label: &str) -> crate::Result> { self .dispatchers + .lock() + .await .get(window_label) .ok_or(crate::Error::WebviewNotFound) + .map(|d| d.clone()) } /// Listen to a global event. @@ -237,12 +221,12 @@ impl WebviewManager { } /// Emits an event to all webviews. - pub fn emit( + pub async fn emit( &self, event: impl AsRef, payload: Option, ) -> crate::Result<()> { - for dispatcher in self.dispatchers.values() { + for dispatcher in self.dispatchers.lock().await.values() { super::event::emit(&dispatcher, event.as_ref(), payload.clone())?; } Ok(()) diff --git a/tauri/src/endpoints/event.rs b/tauri/src/endpoints/event.rs index 746200fc0..40b5b1960 100644 --- a/tauri/src/endpoints/event.rs +++ b/tauri/src/endpoints/event.rs @@ -36,7 +36,7 @@ impl Cmd { once, } => { let js_string = listen_fn(event, handler, once)?; - webview_manager.current_webview()?.eval(&js_string); + webview_manager.current_webview().await?.eval(&js_string)?; Ok(JsonValue::Null) } Self::Emit { @@ -45,7 +45,7 @@ impl Cmd { payload, } => { if let Some(label) = window_label { - let dispatcher = webview_manager.get_webview(&label)?; + let dispatcher = webview_manager.get_webview(&label).await?; // dispatch the event to Rust listeners dispatcher.on_event(event.to_string(), payload.clone()); // dispatch the event to JS listeners @@ -54,7 +54,7 @@ impl Cmd { // dispatch the event to Rust listeners webview_manager.on_event(event.to_string(), payload.clone()); // dispatch the event to JS listeners - webview_manager.emit(event, payload)?; + webview_manager.emit(event, payload).await?; } Ok(JsonValue::Null) } diff --git a/tauri/src/endpoints/global_shortcut.rs b/tauri/src/endpoints/global_shortcut.rs index 52e1272c8..56d61d579 100644 --- a/tauri/src/endpoints/global_shortcut.rs +++ b/tauri/src/endpoints/global_shortcut.rs @@ -34,12 +34,12 @@ impl Cmd { #[cfg(global_shortcut)] match self { Self::Register { shortcut, handler } => { - let dispatcher = webview_manager.current_webview()?.clone(); + let dispatcher = webview_manager.current_webview().await?.clone(); let mut manager = manager_handle().lock().await; manager.register_shortcut(shortcut, move || { let callback_string = crate::api::rpc::format_callback(handler.to_string(), serde_json::Value::Null); - dispatcher.eval(callback_string.as_str()); + let _ = dispatcher.eval(callback_string.as_str()); })?; Ok(JsonValue::Null) } diff --git a/tauri/src/endpoints/window.rs b/tauri/src/endpoints/window.rs index 0d33f9ab2..41640e871 100644 --- a/tauri/src/endpoints/window.rs +++ b/tauri/src/endpoints/window.rs @@ -90,35 +90,37 @@ impl Cmd { if cfg!(not(window)) { Err(crate::Error::ApiNotAllowlisted("setTitle".to_string())) } else { - let current_webview = webview_manager.current_webview()?; + let current_webview = webview_manager.current_webview().await?; match self { - Self::SetResizable { resizable } => current_webview.set_resizable(resizable), - Self::SetTitle { title } => current_webview.set_title(&title), - Self::Maximize => current_webview.maximize(), - Self::Unmaximize => current_webview.unmaximize(), - Self::Minimize => current_webview.minimize(), - Self::Unminimize => current_webview.unminimize(), - Self::Show => current_webview.show(), - Self::Hide => current_webview.hide(), - Self::SetTransparent { transparent } => current_webview.set_transparent(transparent), - Self::SetDecorations { decorations } => current_webview.set_decorations(decorations), - Self::SetAlwaysOnTop { always_on_top } => current_webview.set_always_on_top(always_on_top), - Self::SetWidth { width } => current_webview.set_width(width), - Self::SetHeight { height } => current_webview.set_height(height), - Self::Resize { width, height } => current_webview.resize(width, height), + Self::SetResizable { resizable } => current_webview.set_resizable(resizable)?, + Self::SetTitle { title } => current_webview.set_title(&title)?, + Self::Maximize => current_webview.maximize()?, + Self::Unmaximize => current_webview.unmaximize()?, + Self::Minimize => current_webview.minimize()?, + Self::Unminimize => current_webview.unminimize()?, + Self::Show => current_webview.show()?, + Self::Hide => current_webview.hide()?, + Self::SetTransparent { transparent } => current_webview.set_transparent(transparent)?, + Self::SetDecorations { decorations } => current_webview.set_decorations(decorations)?, + Self::SetAlwaysOnTop { always_on_top } => { + current_webview.set_always_on_top(always_on_top)? + } + Self::SetWidth { width } => current_webview.set_width(width)?, + Self::SetHeight { height } => current_webview.set_height(height)?, + Self::Resize { width, height } => current_webview.resize(width, height)?, Self::SetMinSize { min_width, min_height, - } => current_webview.set_min_size(min_width, min_height), + } => current_webview.set_min_size(min_width, min_height)?, Self::SetMaxSize { max_width, max_height, - } => current_webview.set_max_size(max_width, max_height), - Self::SetX { x } => current_webview.set_x(x), - Self::SetY { y } => current_webview.set_y(y), - Self::SetPosition { x, y } => current_webview.set_position(x, y), - Self::SetFullscreen { fullscreen } => current_webview.set_fullscreen(fullscreen), - Self::SetIcon { icon } => current_webview.set_icon(icon.into()), + } => current_webview.set_max_size(max_width, max_height)?, + Self::SetX { x } => current_webview.set_x(x)?, + Self::SetY { y } => current_webview.set_y(y)?, + Self::SetPosition { x, y } => current_webview.set_position(x, y)?, + Self::SetFullscreen { fullscreen } => current_webview.set_fullscreen(fullscreen)?, + Self::SetIcon { icon } => current_webview.set_icon(icon.into())?, } Ok(JsonValue::Null) } diff --git a/tauri/src/error.rs b/tauri/src/error.rs index e447be804..c818ad039 100644 --- a/tauri/src/error.rs +++ b/tauri/src/error.rs @@ -10,6 +10,9 @@ pub enum Error { /// Can't access webview dispatcher because the webview was closed or not found. #[error("webview not found: invalid label or it was closed")] WebviewNotFound, + /// Failed to send message to webview. + #[error("failed to send message to the webview")] + FailedToSendMessage, /// Embedded asset not found. #[error("asset not found: {0}")] AssetNotFound(String),