diff --git a/cli/tauri.js/templates/tauri.js b/cli/tauri.js/templates/tauri.js index d951da75f..0f2a27f6a 100644 --- a/cli/tauri.js/templates/tauri.js +++ b/cli/tauri.js/templates/tauri.js @@ -18,6 +18,20 @@ * and also whitelist them based upon the developer's settings. */ + // makes the window.external.invoke API available after window.location.href changes +if (navigator.platform != "Win64" && navigator.plaform != "Win32") { + window.external = this + if (navigator.platform == "MacIntel") { + invoke = function (x) { + webkit.messageHandlers.invoke.postMessage(x); + } + } else { + invoke = function (x) { + window.webkit.messageHandlers.external.postMessage(x); + } + } +} + function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) diff --git a/examples/vue/quasar-app/src-tauri/src/main.rs b/examples/vue/quasar-app/src-tauri/src/main.rs index cb0b5e460..d0ef1f11e 100644 --- a/examples/vue/quasar-app/src-tauri/src/main.rs +++ b/examples/vue/quasar-app/src-tauri/src/main.rs @@ -13,8 +13,8 @@ use std::io::BufRead; fn main() { tauri::AppBuilder::new() - .setup(|_webview| { - let handle1 = _webview.handle(); + .setup(|webview, _| { + let handle1 = webview.handle(); std::thread::spawn(move || { let resource_dir = tauri::api::platform::resource_dir().expect("failed to get resource dir"); let node_package_path = resource_dir.join("resources/packaged-node.js"); @@ -35,7 +35,7 @@ fn main() { }); }); - let handle2 = _webview.handle(); + let handle2 = webview.handle(); tauri::event::listen(String::from("hello"), move |msg| { #[derive(Serialize)] pub struct Reply { diff --git a/tauri/src/app.rs b/tauri/src/app.rs index cc8ecc3b6..9632fad6b 100644 --- a/tauri/src/app.rs +++ b/tauri/src/app.rs @@ -6,7 +6,8 @@ mod runner; pub struct App { invoke_handler: Option, &str)>>, - setup: Option)>>, + setup: Option, String)>>, + splashscreen_html: Option, } impl App { @@ -23,19 +24,24 @@ impl App { } } - pub(crate) fn run_setup(&mut self, webview: &mut WebView<'_, ()>) { + pub(crate) fn run_setup(&mut self, webview: &mut WebView<'_, ()>, source: String) { match self.setup { Some(ref mut setup) => { - setup(webview); + setup(webview, source); } None => {} } } + + pub fn splashscreen_html(&self) -> Option<&String> { + self.splashscreen_html.as_ref() + } } pub struct AppBuilder { invoke_handler: Option, &str)>>, - setup: Option)>>, + setup: Option, String)>>, + splashscreen_html: Option } impl AppBuilder { @@ -43,6 +49,7 @@ impl AppBuilder { Self { invoke_handler: None, setup: None, + splashscreen_html: None, } } @@ -54,15 +61,21 @@ impl AppBuilder { self } - pub fn setup) + 'static>(mut self, setup: F) -> Self { + pub fn setup, String) + 'static>(mut self, setup: F) -> Self { self.setup = Some(Box::new(setup)); self } + pub fn splashscreen_html(mut self, html: &str) -> Self { + self.splashscreen_html = Some(html.to_string()); + self + } + pub fn build(self) -> App { App { invoke_handler: self.invoke_handler, setup: self.setup, + splashscreen_html: self.splashscreen_html, } } } diff --git a/tauri/src/app/runner.rs b/tauri/src/app/runner.rs index 61d3fcfb5..238368de4 100644 --- a/tauri/src/app/runner.rs +++ b/tauri/src/app/runner.rs @@ -35,12 +35,12 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> { let config = get()?; // setup the content using the config struct depending on the compile target - let content = setup_content(config.clone())?; + let main_content = setup_content(config.clone())?; // setup the server url for the embedded-server #[cfg(feature = "embedded-server")] let server_url = { - if let Content::Url(ref url) = &content { + if let Content::Url(ref url) = &main_content { String::from(url) } else { String::from("") @@ -48,8 +48,16 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> { }; // build the webview - let mut webview = build_webview(application, config, content)?; - webview.set_color((255, 255, 255)); + let webview = build_webview( + application, + config, + main_content, + if application.splashscreen_html().is_some() { + Some(Content::Html(application.splashscreen_html().expect("failed to get splashscreen_html").to_string())) + } else { + None + }, + )?; // on dev-server grab a handler and execute the tauri.js API entry point. #[cfg(feature = "dev-server")] @@ -172,7 +180,12 @@ fn build_webview( application: &mut App, config: Config, content: Content, + splashscreen_content: Option> ) -> crate::Result> { + let content_clone = match content { + Content::Html(ref html) => Content::Html(html.clone()), + Content::Url(ref url) => Content::Url(url.clone()), + }; let debug = cfg!(debug_assertions); // get properties from config struct let width = config.tauri.window.width; @@ -180,28 +193,54 @@ fn build_webview( let resizable = config.tauri.window.resizable; let title = config.tauri.window.title.into_boxed_str(); - Ok( - builder() - .title(Box::leak(title)) - .size(width, height) - .resizable(resizable) - .debug(debug) - .user_data(()) - .invoke_handler(move |webview, arg| { - if arg == r#"{"cmd":"__initialized"}"# { - application.run_setup(webview); - webview.eval(JS_STRING)?; - } else if let Ok(b) = crate::endpoints::handle(webview, arg) { - if !b { - application.run_invoke_handler(webview, arg); - } - } + let has_splashscreen = splashscreen_content.is_some(); + let mut initialized_splashscreen = false; - Ok(()) - }) - .content(content) - .build()?, - ) + let webview = builder() + .title(Box::leak(title)) + .size(width, height) + .resizable(resizable) + .debug(debug) + .user_data(()) + .invoke_handler(move |webview, arg| { + if arg == r#"{"cmd":"__initialized"}"# { + let source = if has_splashscreen && !initialized_splashscreen { + initialized_splashscreen = true; + "splashscreen" + } else { + "window-1" + }; + application.run_setup(webview, source.to_string()); + webview.eval(JS_STRING)?; + } else if arg == r#"{"cmd":"closeSplashscreen"}"# { + let content_href = match content_clone { + Content::Html(ref html) => html, + Content::Url(ref url) => url, + }; + webview.eval(&format!("window.location.href = `{}`", content_href))?; + } else if let Ok(b) = crate::endpoints::handle(webview, arg) { + if !b { + application.run_invoke_handler(webview, arg); + } + } + + Ok(()) + }) + .content(if splashscreen_content.is_some() { + splashscreen_content.expect("failed to get splashscreen content") + } else { + content + }) + .build()?; + + if has_splashscreen { + // trigger the init hook for the splashscreen since we're not injecting the tauri.js entry point + webview.handle().dispatch(|webview| { + webview.eval(r#"window.external.invoke(JSON.stringify({ cmd: "__initialized" }))"#) + }).expect("failed to initialize splashscreen"); + } + + Ok(webview) } #[cfg(test)] diff --git a/tauri/src/lib.rs b/tauri/src/lib.rs index 4c2dae740..b355f3e90 100644 --- a/tauri/src/lib.rs +++ b/tauri/src/lib.rs @@ -36,7 +36,7 @@ use threadpool::ThreadPool; use error_chain::error_chain; pub use app::*; -use web_view::WebView; +use web_view::{WebView, Handle}; pub use tauri_api as api; @@ -106,6 +106,14 @@ pub fn call( ); } +pub fn close_splashscreen(webview_handle: &Handle) -> crate::Result<()> { + webview_handle.dispatch(|webview| { + // send a signal to the runner so it knows that it should redirect to the main app content + webview.eval(r#"window.external.invoke(JSON.stringify({ cmd: "closeSplashscreen" }))"#) + })?; + Ok(()) +} + #[cfg(test)] mod test { use proptest::prelude::*;