瀏覽代碼

feat(tauri) add splashscreen API (#400)

* feat(tauri) add splashscreen API

* fix(examples) quasar example building
Lucas Fernandes Nogueira 5 年之前
父節點
當前提交
3e1ce404f5
共有 5 個文件被更改,包括 107 次插入33 次删除
  1. 14 0
      cli/tauri.js/templates/tauri.js
  2. 3 3
      examples/vue/quasar-app/src-tauri/src/main.rs
  3. 18 5
      tauri/src/app.rs
  4. 63 24
      tauri/src/app/runner.rs
  5. 9 1
      tauri/src/lib.rs

+ 14 - 0
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)

+ 3 - 3
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 {

+ 18 - 5
tauri/src/app.rs

@@ -6,7 +6,8 @@ mod runner;
 
 pub struct App {
   invoke_handler: Option<Box<dyn FnMut(&mut WebView<'_, ()>, &str)>>,
-  setup: Option<Box<dyn FnMut(&mut WebView<'_, ()>)>>,
+  setup: Option<Box<dyn FnMut(&mut WebView<'_, ()>, String)>>,
+  splashscreen_html: Option<String>,
 }
 
 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<Box<dyn FnMut(&mut WebView<'_, ()>, &str)>>,
-  setup: Option<Box<dyn FnMut(&mut WebView<'_, ()>)>>,
+  setup: Option<Box<dyn FnMut(&mut WebView<'_, ()>, String)>>,
+  splashscreen_html: Option<String>
 }
 
 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<F: FnMut(&mut WebView<'_, ()>) + 'static>(mut self, setup: F) -> Self {
+  pub fn setup<F: FnMut(&mut WebView<'_, ()>, 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,
     }
   }
 }

+ 63 - 24
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<String>,
+  splashscreen_content: Option<Content<String>>
 ) -> crate::Result<WebView<'_, ()>> {
+  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;
+
+  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(content)
-      .build()?,
-  )
+      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)]

+ 9 - 1
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<T: 'static>(
   );
 }
 
+pub fn close_splashscreen<T: 'static>(webview_handle: &Handle<T>) -> 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::*;