Browse Source

feat(core): window, shortcut and clipboard API calls on main thread (#2659)

Lucas Fernandes Nogueira 3 years ago
parent
commit
2812c4464b

+ 6 - 0
.changes/main-thread-api-calls.md

@@ -0,0 +1,6 @@
+---
+"tauri": patch
+"tauri-runtime-wry": patch
+---
+
+Allow window, global shortcut and clipboard APIs to be called on the main thread (except window's `close` and `create_window`).

File diff suppressed because it is too large
+ 588 - 241
core/tauri-runtime-wry/src/lib.rs


+ 0 - 42
core/tauri-runtime/src/lib.rs

@@ -263,23 +263,9 @@ pub trait RuntimeHandle: Debug + Send + Sized + Clone + 'static {
 /// A global shortcut manager.
 pub trait GlobalShortcutManager: Debug {
   /// Whether the application has registered the given `accelerator`.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
-  ///
-  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn is_registered(&self, accelerator: &str) -> crate::Result<bool>;
 
   /// Register a global shortcut of `accelerator`.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
-  ///
-  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn register<F: Fn() + Send + 'static>(
     &mut self,
     accelerator: &str,
@@ -287,45 +273,17 @@ pub trait GlobalShortcutManager: Debug {
   ) -> crate::Result<()>;
 
   /// Unregister all accelerators registered by the manager instance.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
-  ///
-  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn unregister_all(&mut self) -> crate::Result<()>;
 
   /// Unregister the provided `accelerator`.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
-  ///
-  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn unregister(&mut self, accelerator: &str) -> crate::Result<()>;
 }
 
 /// Clipboard manager.
 pub trait ClipboardManager: Debug {
   /// Writes the text into the clipboard as plain text.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
-  ///
-  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn write_text<T: Into<String>>(&mut self, text: T) -> Result<()>;
   /// Read the content in the clipboard as plain text.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
-  ///
-  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn read_text(&self) -> Result<Option<String>>;
 }
 

+ 7 - 0
core/tauri/src/app.rs

@@ -324,6 +324,13 @@ macro_rules! shared_app_impl {
   ($app: ty) => {
     impl<R: Runtime> $app {
       /// Creates a new webview window.
+      ///
+      /// # Panics
+      ///
+      /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+      /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+      ///
+      /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
       pub fn create_window<F>(
         &self,
         label: impl Into<String>,

+ 13 - 105
core/tauri/src/window.rs

@@ -155,6 +155,13 @@ impl<R: Runtime> Window<R> {
   }
 
   /// Creates a new webview window.
+  ///
+  /// # Panics
+  ///
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn create_window<F>(
     &mut self,
     label: String,
@@ -310,37 +317,16 @@ impl<R: Runtime> Window<R> {
   }
 
   /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn scale_factor(&self) -> crate::Result<f64> {
     self.window.dispatcher.scale_factor().map_err(Into::into)
   }
 
   /// Returns the position of the top-left hand corner of the window's client area relative to the top-left hand corner of the desktop.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
     self.window.dispatcher.inner_position().map_err(Into::into)
   }
 
   /// Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
     self.window.dispatcher.outer_position().map_err(Into::into)
   }
@@ -348,13 +334,6 @@ impl<R: Runtime> Window<R> {
   /// Returns the physical size of the window's client area.
   ///
   /// The client area is the content of the window, excluding the title bar and borders.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
     self.window.dispatcher.inner_size().map_err(Into::into)
   }
@@ -362,73 +341,31 @@ impl<R: Runtime> Window<R> {
   /// Returns the physical size of the entire window.
   ///
   /// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
     self.window.dispatcher.outer_size().map_err(Into::into)
   }
 
   /// Gets the window's current fullscreen state.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_fullscreen(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_fullscreen().map_err(Into::into)
   }
 
   /// Gets the window's current maximized state.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_maximized(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_maximized().map_err(Into::into)
   }
 
   /// Gets the window’s current decoration state.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_decorated(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_decorated().map_err(Into::into)
   }
 
   /// Gets the window’s current resizable state.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_resizable(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_resizable().map_err(Into::into)
   }
 
   /// Gets the window's current vibility state.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_visible(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_visible().map_err(Into::into)
   }
@@ -440,13 +377,6 @@ impl<R: Runtime> Window<R> {
   /// ## Platform-specific
   ///
   /// - **Linux:** Unsupported
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
     self
       .window
@@ -463,13 +393,6 @@ impl<R: Runtime> Window<R> {
   /// ## Platform-specific
   ///
   /// - **Linux:** Unsupported
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
     self
       .window
@@ -484,13 +407,6 @@ impl<R: Runtime> Window<R> {
   /// ## Platform-specific
   ///
   /// - **Linux:** Unsupported
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
     self
       .window
@@ -501,25 +417,11 @@ impl<R: Runtime> Window<R> {
   }
 
   /// Returns the native handle that is used by this window.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   #[cfg(target_os = "macos")]
   pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
     self.window.dispatcher.ns_window().map_err(Into::into)
   }
   /// Returns the native handle that is used by this window.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   #[cfg(windows)]
   pub fn hwnd(&self) -> crate::Result<*mut std::ffi::c_void> {
     self
@@ -628,6 +530,12 @@ impl<R: Runtime> Window<R> {
   }
 
   /// Closes this window.
+  /// # Panics
+  ///
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn close(&self) -> crate::Result<()> {
     self.window.dispatcher.close().map_err(Into::into)
   }

+ 0 - 14
core/tauri/src/window/menu.rs

@@ -83,25 +83,11 @@ impl<R: Runtime> MenuHandle<R> {
   }
 
   /// Whether the menu is visible or not.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_visible(&self) -> crate::Result<bool> {
     self.dispatcher.is_menu_visible().map_err(Into::into)
   }
 
   /// Toggles the menu visibility.
-  ///
-  /// # Panics
-  ///
-  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
-  ///
-  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn toggle(&self) -> crate::Result<()> {
     if self.is_visible()? {
       self.hide()

+ 22 - 28
examples/api/src-tauri/src/main.rs

@@ -15,9 +15,8 @@ use std::path::PathBuf;
 
 use serde::{Deserialize, Serialize};
 use tauri::{
-  api::dialog::ask, async_runtime, http::ResponseBuilder, CustomMenuItem, Event,
-  GlobalShortcutManager, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowBuilder,
-  WindowUrl,
+  api::dialog::ask, http::ResponseBuilder, CustomMenuItem, Event, GlobalShortcutManager, Manager,
+  SystemTray, SystemTrayEvent, SystemTrayMenu, WindowBuilder, WindowUrl,
 };
 
 #[derive(Serialize)]
@@ -197,18 +196,14 @@ fn main() {
     // Application is ready (triggered only once)
     Event::Ready => {
       let app_handle = app_handle.clone();
-      // launch a new thread so it doesnt block any channel
-      async_runtime::spawn(async move {
-        let app_handle = app_handle.clone();
-        app_handle
-          .global_shortcut_manager()
-          .register("CmdOrCtrl+1", move || {
-            let app_handle = app_handle.clone();
-            let window = app_handle.get_window("main").unwrap();
-            window.set_title("New title!").unwrap();
-          })
-          .unwrap();
-      });
+      app_handle
+        .global_shortcut_manager()
+        .register("CmdOrCtrl+1", move || {
+          let app_handle = app_handle.clone();
+          let window = app_handle.get_window("main").unwrap();
+          window.set_title("New title!").unwrap();
+        })
+        .unwrap();
     }
 
     // Triggered when a window is trying to close
@@ -218,20 +213,19 @@ fn main() {
       // use the exposed close api, and prevent the event loop to close
       api.prevent_close();
       // ask the user if he wants to quit
-      // we need to run this on another thread because this is the event loop callback handler
-      // and the dialog API needs to communicate with the event loop.
-      std::thread::spawn(move || {
-        ask(
-          Some(&window),
-          "Tauri API",
-          "Are you sure that you want to close this window?",
-          move |answer| {
-            if answer {
+      ask(
+        Some(&window),
+        "Tauri API",
+        "Are you sure that you want to close this window?",
+        move |answer| {
+          if answer {
+            // .close() cannot be called on the main thread
+            std::thread::spawn(move || {
               app_handle.get_window(&label).unwrap().close().unwrap();
-            }
-          },
-        );
-      });
+            });
+          }
+        },
+      );
     }
 
     // Keep the event loop running even if all windows are closed

+ 1 - 1
examples/splashscreen/src-tauri/src/main.rs

@@ -22,7 +22,7 @@ mod rust {
       .setup(|app| {
         let splashscreen_window = app.get_window("splashscreen").unwrap();
         let main_window = app.get_window("main").unwrap();
-        // we perform the initialization code on a new task so the app doesn't freeze
+        // we perform the initialization code on a new task so the app doesn't crash
         tauri::async_runtime::spawn(async move {
           println!("Initializing...");
           sleep(Duration::from_secs(2));

Some files were not shown because too many files changed in this diff