Эх сурвалжийг харах

feat(tauri-runtime-wry): add plugin API (#4094)

Lucas Fernandes Nogueira 3 жил өмнө
parent
commit
c8e0e5b97d

+ 5 - 0
.changes/tauri-runtime-wry-plugin.md

@@ -0,0 +1,5 @@
+---
+"tauri-runtime-wry": patch
+---
+
+Added the `plugin` method to the `Wry` runtime, allowing extensions to the event loop.

+ 5 - 0
.changes/wry-plugin.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Adds the `App#wry_plugin` API to inject a plugin for the wry integration.

+ 105 - 34
core/tauri-runtime-wry/src/lib.rs

@@ -70,6 +70,7 @@ use wry::{
   webview::{FileDropEvent as WryFileDropEvent, WebContext, WebView, WebViewBuilder},
 };
 
+pub use wry;
 pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId};
 
 #[cfg(windows)]
@@ -98,7 +99,7 @@ use std::{
   thread::{current as current_thread, ThreadId},
 };
 
-type WebviewId = u64;
+pub type WebviewId = u64;
 
 mod webview;
 pub use webview::Webview;
@@ -118,25 +119,25 @@ mod clipboard;
 #[cfg(feature = "clipboard")]
 use clipboard::*;
 
-type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
+pub type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
 // window
 type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
 type WindowEventListenersMap = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;
-type WindowEventListeners = Arc<Mutex<HashMap<WebviewId, WindowEventListenersMap>>>;
+pub type WindowEventListeners = Arc<Mutex<HashMap<WebviewId, WindowEventListenersMap>>>;
 // menu
 pub type MenuEventHandler = Box<dyn Fn(&MenuEvent) + Send>;
 pub type MenuEventListeners = Arc<Mutex<HashMap<WebviewId, WindowMenuEventListeners>>>;
 pub type WindowMenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
 
 #[derive(Debug, Clone, Default)]
-struct WebviewIdStore(Arc<Mutex<HashMap<WindowId, WebviewId>>>);
+pub struct WebviewIdStore(Arc<Mutex<HashMap<WindowId, WebviewId>>>);
 
 impl WebviewIdStore {
-  fn insert(&self, w: WindowId, id: WebviewId) {
+  pub fn insert(&self, w: WindowId, id: WebviewId) {
     self.0.lock().unwrap().insert(w, id);
   }
 
-  fn get(&self, w: &WindowId) -> WebviewId {
+  pub fn get(&self, w: &WindowId) -> WebviewId {
     *self.0.lock().unwrap().get(w).unwrap()
   }
 
@@ -192,7 +193,7 @@ fn send_user_message<T: UserEvent>(context: &Context<T>, message: Message<T>) ->
 
 #[derive(Clone)]
 pub struct Context<T: UserEvent> {
-  webview_id_map: WebviewIdStore,
+  pub webview_id_map: WebviewIdStore,
   main_thread_id: ThreadId,
   proxy: WryEventLoopProxy<Message<T>>,
   window_event_listeners: WindowEventListeners,
@@ -495,7 +496,7 @@ impl TryFrom<WindowIcon> for WryIcon {
   }
 }
 
-struct WindowEventWrapper(Option<WindowEvent>);
+pub struct WindowEventWrapper(pub Option<WindowEvent>);
 
 impl WindowEventWrapper {
   fn parse(webview: &Option<WindowHandle>, event: &WryWindowEvent<'_>) -> Self {
@@ -1555,7 +1556,7 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
 
 #[cfg(feature = "system-tray")]
 #[derive(Clone, Default)]
-struct TrayContext {
+pub struct TrayContext {
   tray: Arc<Mutex<Option<Arc<Mutex<WrySystemTray>>>>>,
   listeners: SystemTrayEventListeners,
   items: SystemTrayItems,
@@ -1616,10 +1617,24 @@ impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
   }
 }
 
+pub trait Plugin<T: UserEvent> {
+  fn on_event(
+    &mut self,
+    event: &Event<Message<T>>,
+    event_loop: &EventLoopWindowTarget<Message<T>>,
+    proxy: &WryEventLoopProxy<Message<T>>,
+    control_flow: &mut ControlFlow,
+    context: EventLoopIterationContext<'_, T>,
+    web_context: &WebContextStore,
+  ) -> bool;
+}
+
 /// A Tauri [`Runtime`] wrapper around wry.
 pub struct Wry<T: UserEvent> {
   main_thread_id: ThreadId,
 
+  plugins: Vec<Box<dyn Plugin<T>>>,
+
   #[cfg(feature = "global-shortcut")]
   global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
   #[cfg(feature = "global-shortcut")]
@@ -1791,6 +1806,8 @@ impl<T: UserEvent> Wry<T> {
     Ok(Self {
       main_thread_id,
 
+      plugins: Default::default(),
+
       #[cfg(feature = "global-shortcut")]
       global_shortcut_manager,
       #[cfg(feature = "global-shortcut")]
@@ -1815,6 +1832,10 @@ impl<T: UserEvent> Wry<T> {
       tray_context,
     })
   }
+
+  pub fn plugin<P: Plugin<T> + 'static>(&mut self, plugin: P) {
+    self.plugins.push(Box::new(plugin));
+  }
 }
 
 impl<T: UserEvent> Runtime<T> for Wry<T> {
@@ -1999,6 +2020,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
     let windows = self.windows.clone();
     let webview_id_map = self.webview_id_map.clone();
     let web_context = &self.web_context;
+    let plugins = &mut self.plugins;
     let window_event_listeners = self.window_event_listeners.clone();
     let menu_event_listeners = self.menu_event_listeners.clone();
     #[cfg(feature = "system-tray")]
@@ -2013,6 +2035,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
     let clipboard_manager = self.clipboard_manager.clone();
     let mut iteration = RunIteration::default();
 
+    let proxy = self.event_loop.create_proxy();
+
     self
       .event_loop
       .run_return(|event, event_loop, control_flow| {
@@ -2021,6 +2045,34 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
           *control_flow = ControlFlow::Exit;
         }
 
+        for p in plugins.iter_mut() {
+          let prevent_default = p.on_event(
+            &event,
+            event_loop,
+            &proxy,
+            control_flow,
+            EventLoopIterationContext {
+              callback: &mut callback,
+              webview_id_map: webview_id_map.clone(),
+              windows: windows.clone(),
+              window_event_listeners: &window_event_listeners,
+              #[cfg(feature = "global-shortcut")]
+              global_shortcut_manager: global_shortcut_manager.clone(),
+              #[cfg(feature = "global-shortcut")]
+              global_shortcut_manager_handle: &global_shortcut_manager_handle,
+              #[cfg(feature = "clipboard")]
+              clipboard_manager: clipboard_manager.clone(),
+              menu_event_listeners: &menu_event_listeners,
+              #[cfg(feature = "system-tray")]
+              tray_context: &tray_context,
+            },
+            web_context,
+          );
+          if prevent_default {
+            return;
+          }
+        }
+
         iteration = handle_event_loop(
           event,
           event_loop,
@@ -2051,6 +2103,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
     let windows = self.windows.clone();
     let webview_id_map = self.webview_id_map.clone();
     let web_context = self.web_context;
+    let mut plugins = self.plugins;
     let window_event_listeners = self.window_event_listeners.clone();
     let menu_event_listeners = self.menu_event_listeners.clone();
 
@@ -2065,7 +2118,36 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
     #[cfg(feature = "clipboard")]
     let clipboard_manager = self.clipboard_manager.clone();
 
+    let proxy = self.event_loop.create_proxy();
+
     self.event_loop.run(move |event, event_loop, control_flow| {
+      for p in &mut plugins {
+        let prevent_default = p.on_event(
+          &event,
+          event_loop,
+          &proxy,
+          control_flow,
+          EventLoopIterationContext {
+            callback: &mut callback,
+            webview_id_map: webview_id_map.clone(),
+            windows: windows.clone(),
+            window_event_listeners: &window_event_listeners,
+            #[cfg(feature = "global-shortcut")]
+            global_shortcut_manager: global_shortcut_manager.clone(),
+            #[cfg(feature = "global-shortcut")]
+            global_shortcut_manager_handle: &global_shortcut_manager_handle,
+            #[cfg(feature = "clipboard")]
+            clipboard_manager: clipboard_manager.clone(),
+            menu_event_listeners: &menu_event_listeners,
+            #[cfg(feature = "system-tray")]
+            tray_context: &tray_context,
+          },
+          &web_context,
+        );
+        if prevent_default {
+          return;
+        }
+      }
       handle_event_loop(
         event,
         event_loop,
@@ -2092,19 +2174,19 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
 }
 
 pub struct EventLoopIterationContext<'a, T: UserEvent> {
-  callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
-  webview_id_map: WebviewIdStore,
-  windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
-  window_event_listeners: &'a WindowEventListeners,
+  pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
+  pub webview_id_map: WebviewIdStore,
+  pub windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
+  pub window_event_listeners: &'a WindowEventListeners,
   #[cfg(feature = "global-shortcut")]
-  global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
+  pub global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
   #[cfg(feature = "global-shortcut")]
-  global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
+  pub global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
   #[cfg(feature = "clipboard")]
-  clipboard_manager: Arc<Mutex<Clipboard>>,
-  menu_event_listeners: &'a MenuEventListeners,
+  pub clipboard_manager: Arc<Mutex<Clipboard>>,
+  pub menu_event_listeners: &'a MenuEventListeners,
   #[cfg(feature = "system-tray")]
-  tray_context: &'a TrayContext,
+  pub tray_context: &'a TrayContext,
 }
 
 struct UserMessageContext<'a> {
@@ -2653,16 +2735,13 @@ fn handle_event_loop<T: UserEvent>(
 
       match event {
         WryWindowEvent::CloseRequested => {
-          on_close_requested(
-            callback,
-            window_id,
-            windows.clone(),
-            window_event_listeners,
-            menu_event_listeners.clone(),
-          );
+          on_close_requested(callback, window_id, windows.clone(), window_event_listeners);
         }
         WryWindowEvent::Destroyed => {
           if windows.lock().unwrap().remove(&window_id).is_some() {
+            menu_event_listeners.lock().unwrap().remove(&window_id);
+            window_event_listeners.lock().unwrap().remove(&window_id);
+
             let is_empty = windows.lock().unwrap().is_empty();
             if is_empty {
               let (tx, rx) = channel();
@@ -2695,11 +2774,7 @@ fn handle_event_loop<T: UserEvent>(
     }
     Event::UserEvent(message) => match message {
       Message::Window(id, WindowMessage::Close) => {
-        on_window_close(
-          id,
-          windows.lock().expect("poisoned webview collection"),
-          menu_event_listeners.clone(),
-        );
+        on_window_close(id, windows.lock().expect("poisoned webview collection"));
       }
       Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
       message => {
@@ -2736,7 +2811,6 @@ fn on_close_requested<'a, T: UserEvent>(
   window_id: WebviewId,
   windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
   window_event_listeners: &WindowEventListeners,
-  menu_event_listeners: MenuEventListeners,
 ) {
   let (tx, rx) = channel();
   let windows_guard = windows.lock().expect("poisoned webview collection");
@@ -2765,7 +2839,6 @@ fn on_close_requested<'a, T: UserEvent>(
       on_window_close(
         window_id,
         windows.lock().expect("poisoned webview collection"),
-        menu_event_listeners,
       );
     }
   }
@@ -2774,11 +2847,9 @@ fn on_close_requested<'a, T: UserEvent>(
 fn on_window_close(
   window_id: WebviewId,
   mut windows: MutexGuard<'_, HashMap<WebviewId, WindowWrapper>>,
-  menu_event_listeners: MenuEventListeners,
 ) {
   if let Some(mut window_wrapper) = windows.get_mut(&window_id) {
     window_wrapper.inner = None;
-    menu_event_listeners.lock().unwrap().remove(&window_id);
   }
 }
 

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

@@ -470,6 +470,21 @@ impl<R: Runtime> ManagerBase<R> for App<R> {
   }
 }
 
+#[cfg(feature = "wry")]
+impl App<crate::Wry> {
+  /// Adds a [`tauri_runtime_wry::Plugin`].
+  ///
+  /// # Stability
+  ///
+  /// This API is unstable.
+  pub fn wry_plugin<P: tauri_runtime_wry::Plugin<EventLoopMessage> + 'static>(
+    &mut self,
+    plugin: P,
+  ) {
+    self.runtime.as_mut().unwrap().plugin(plugin);
+  }
+}
+
 macro_rules! shared_app_impl {
   ($app: ty) => {
     impl<R: Runtime> $app {

+ 16 - 20
core/tests/app-updater/tests/update.rs

@@ -116,20 +116,18 @@ fn update_app() {
 
   let cli_bin_path = if let Some(p) = get_cli_bin_path(&cli_dir, false) {
     p
+  } else if let Some(p) = get_cli_bin_path(&cli_dir, true) {
+    p
   } else {
-    if let Some(p) = get_cli_bin_path(&cli_dir, true) {
-      p
-    } else {
-      let status = Command::new("cargo")
-        .arg("build")
-        .current_dir(&cli_dir)
-        .status()
-        .expect("failed to run cargo");
-      if !status.success() {
-        panic!("failed to build CLI");
-      }
-      get_cli_bin_path(&cli_dir, true).expect("cargo did not build the Tauri CLI")
+    let status = Command::new("cargo")
+      .arg("build")
+      .current_dir(&cli_dir)
+      .status()
+      .expect("failed to run cargo");
+    if !status.success() {
+      panic!("failed to build CLI");
     }
+    get_cli_bin_path(&cli_dir, true).expect("cargo did not build the Tauri CLI")
   };
 
   let mut config = Config {
@@ -214,15 +212,13 @@ fn update_app() {
     Command::new(root_dir.join("target/debug/app-updater.exe"))
   } else if cfg!(target_os = "macos") {
     Command::new(bundle_path(&root_dir, "0.1.0").join("Contents/MacOS/app-updater"))
+  } else if std::env::var("CI").map(|v| v == "true").unwrap_or_default() {
+    let mut c = Command::new("xvfb-run");
+    c.arg("--auto-servernum")
+      .arg(bundle_path(&root_dir, "0.1.0"));
+    c
   } else {
-    if std::env::var("CI").map(|v| v == "true").unwrap_or_default() {
-      let mut c = Command::new("xvfb-run");
-      c.arg("--auto-servernum")
-        .arg(bundle_path(&root_dir, "0.1.0"));
-      c
-    } else {
-      Command::new(bundle_path(&root_dir, "0.1.0"))
-    }
+    Command::new(bundle_path(&root_dir, "0.1.0"))
   };
 
   let status = binary_cmd.status().expect("failed to run app");