refactor(core): update wry (#1242)

This commit is contained in:
Lucas Fernandes Nogueira
2021-02-16 14:42:08 -03:00
committed by GitHub
parent ddcb70c8fe
commit 25e47e8f9b
17 changed files with 453 additions and 354 deletions

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ struct Context;
fn main() {
tauri::AppBuilder::<tauri::flavors::Wry, Context>::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()

View File

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

View File

@@ -19,7 +19,7 @@ struct Context;
fn main() {
tauri::AppBuilder::<tauri::flavors::Wry, Context>::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);

View File

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

View File

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

View File

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

View File

@@ -87,7 +87,7 @@ pub fn emit<D: ApplicationDispatcherExt, S: Serialize>(
event.as_ref(),
js_payload,
salt
));
))?;
Ok(())
}

View File

@@ -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<A: ApplicationExt + 'static>(
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<A: ApplicationExt + 'static>(
.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<A: ApplicationExt + 'static>(
}
}
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<A: ApplicationExt + 'static>(
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<A: ApplicationExt + 'static>(
window.__TAURI__.__currentWindow = {{ label: "{current_window_label}" }}
"#,
window_labels_array =
serde_json::to_string(&dispatchers.keys().collect::<Vec<&String>>()).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());
}
}

View File

@@ -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<Self::Webview>;
@@ -158,13 +152,76 @@ pub struct Callback<D> {
/// Function name to bind.
pub name: String,
/// Function callback handler.
pub function: Box<dyn FnMut(&D, i32, Vec<String>) -> i32 + Send>,
pub function: Box<dyn FnMut(D, i32, Vec<String>) -> 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<S: Into<String>>(&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<S: Into<String>>(&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<Self>;
/// 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<Self::Window>;
/// Creates a new webview.
fn create_webview(
&mut self,
webview_builder: Self::WebviewBuilder,
window: Self::Window,
callbacks: Vec<Callback<Self::Dispatcher>>,
) -> crate::Result<()>;
) -> crate::Result<Self::Dispatcher>;
/// Run the application.
fn run(self);

View File

@@ -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<wry::Icon> 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<Self::Webview> {
Ok(self)
@@ -140,101 +129,215 @@ impl WebviewBuilderExt for wry::WebViewAttributes {
}
#[derive(Clone)]
pub struct WryDispatcher {
inner: Arc<Mutex<wry::AppDispatcher<()>>>,
current_window: wry::WindowId,
}
struct WryMessage(wry::Message<wry::WindowId, ()>);
impl TryFrom<(wry::WindowId, Message)> for WryMessage {
type Error = crate::Error;
fn try_from((id, message): (wry::WindowId, Message)) -> crate::Result<Self> {
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<Mutex<wry::WindowDispatcher>>);
impl ApplicationDispatcherExt for WryDispatcher {
fn send_message(&self, message: Message) {
let message_res: crate::Result<WryMessage> = (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<S: Into<String>>(&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<S: Into<String>>(&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<Mutex<wry::AppDispatcher<()>>>,
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<Self::Dispatcher> {
@@ -244,61 +347,30 @@ impl ApplicationExt for WryApplication {
fn new() -> crate::Result<Self> {
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<Self::Window> {
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<Callback<Self::Dispatcher>>,
) -> crate::Result<()> {
) -> crate::Result<Self::Dispatcher> {
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) {

View File

@@ -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<A: ApplicationDispatcherExt> WebviewDispatcher<A> {
}
/// 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<f64>) {
self
.dispatcher
.send_message(Message::SetWidth(width.into()))
pub fn set_width(&self, width: impl Into<f64>) -> crate::Result<()> {
self.dispatcher.set_width(width.into())
}
/// Sets the window height.
pub fn set_height(&self, height: impl Into<f64>) {
self
.dispatcher
.send_message(Message::SetHeight(height.into()))
pub fn set_height(&self, height: impl Into<f64>) -> crate::Result<()> {
self.dispatcher.set_height(height.into())
}
/// Resizes the window.
pub fn resize(&self, width: impl Into<f64>, height: impl Into<f64>) {
self.dispatcher.send_message(Message::Resize {
width: width.into(),
height: height.into(),
})
pub fn resize(&self, width: impl Into<f64>, height: impl Into<f64>) -> crate::Result<()> {
self.dispatcher.resize(width.into(), height.into())
}
/// Sets the window min size.
pub fn set_min_size(&self, min_width: impl Into<f64>, min_height: impl Into<f64>) {
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<f64>,
min_height: impl Into<f64>,
) -> 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<f64>, max_height: impl Into<f64>) {
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<f64>,
max_height: impl Into<f64>,
) -> 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<f64>) {
self.dispatcher.send_message(Message::SetX(x.into()))
pub fn set_x(&self, x: impl Into<f64>) -> crate::Result<()> {
self.dispatcher.set_x(x.into())
}
/// Sets the window y position.
pub fn set_y(&self, y: impl Into<f64>) {
self.dispatcher.send_message(Message::SetY(y.into()))
pub fn set_y(&self, y: impl Into<f64>) -> crate::Result<()> {
self.dispatcher.set_y(y.into())
}
/// Sets the window position.
pub fn set_position(&self, x: impl Into<f64>, y: impl Into<f64>) {
self.dispatcher.send_message(Message::SetPosition {
x: x.into(),
y: y.into(),
})
pub fn set_position(&self, x: impl Into<f64>, y: impl Into<f64>) -> 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<A: Clone> {
dispatchers: HashMap<String, WebviewDispatcher<A>>,
dispatchers: Arc<Mutex<HashMap<String, WebviewDispatcher<A>>>>,
current_webview_window_label: String,
}
impl<A: ApplicationDispatcherExt> WebviewManager<A> {
pub(crate) fn new(dispatchers: HashMap<String, WebviewDispatcher<A>>, label: String) -> Self {
pub(crate) fn new(
dispatchers: Arc<Mutex<HashMap<String, WebviewDispatcher<A>>>>,
label: String,
) -> Self {
Self {
dispatchers,
current_webview_window_label: label,
}
}
/// Gets the map of the current webviews.
pub fn webviews(&self) -> &HashMap<String, WebviewDispatcher<A>> {
&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<A>> {
self.get_webview(&self.current_webview_window_label)
pub async fn current_webview(&self) -> crate::Result<WebviewDispatcher<A>> {
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<A>> {
pub async fn get_webview(&self, window_label: &str) -> crate::Result<WebviewDispatcher<A>> {
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<A: ApplicationDispatcherExt> WebviewManager<A> {
}
/// Emits an event to all webviews.
pub fn emit<S: Serialize + Clone>(
pub async fn emit<S: Serialize + Clone>(
&self,
event: impl AsRef<str>,
payload: Option<S>,
) -> 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(())

View File

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

View File

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

View File

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

View File

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