Browse Source

feat(core): add `WindowEvent::FileDrop`, closes #3664 (#3686)

Lucas Fernandes Nogueira 3 years ago
parent
commit
07d1584cf0

+ 6 - 0
.changes/refactor-file-drop.md

@@ -0,0 +1,6 @@
+---
+"tauri-runtime": minor
+"tauri-runtime-wry": minor
+---
+
+The file drop event is now part of the `WindowEvent` enum instead of a having a dedicated handler.

+ 5 - 0
.changes/runtime-file-drop-event.md

@@ -0,0 +1,5 @@
+---
+"tauri-runtime": patch
+---
+
+**Breaking change:** Move the `FileDropEvent` struct to the `window` module.

+ 7 - 0
.changes/window-event-file-drop.md

@@ -0,0 +1,7 @@
+---
+"tauri": patch
+"tauri-runtime": minor
+"tauri-runtime-wry": minor
+---
+
+Added the `WindowEvent::FileDrop` variant.

+ 22 - 30
core/tauri-runtime-wry/src/lib.rs

@@ -11,10 +11,10 @@ use tauri_runtime::{
   },
   menu::{CustomMenuItem, Menu, MenuEntry, MenuHash, MenuId, MenuItem, MenuUpdate},
   monitor::Monitor,
-  webview::{FileDropEvent, FileDropHandler, WebviewIpcHandler, WindowBuilder, WindowBuilderBase},
+  webview::{WebviewIpcHandler, WindowBuilder, WindowBuilderBase},
   window::{
     dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
-    DetachedWindow, JsEventListenerKey, PendingWindow, WindowEvent,
+    DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent,
   },
   ClipboardManager, Dispatch, Error, ExitRequestedEventAction, GlobalShortcutManager, Result,
   RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType, WindowIcon,
@@ -2624,7 +2624,6 @@ fn create_webview(
     uri_scheme_protocols,
     mut window_builder,
     ipc_handler,
-    file_drop_handler,
     label,
     url,
     menu_ids,
@@ -2663,17 +2662,11 @@ fn create_webview(
     .with_url(&url)
     .unwrap() // safe to unwrap because we validate the URL beforehand
     .with_transparent(is_window_transparent);
+  if webview_attributes.file_drop_handler_enabled {
+    webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(&context));
+  }
   if let Some(handler) = ipc_handler {
     webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
-      context.clone(),
-      label.clone(),
-      menu_ids.clone(),
-      js_event_listeners.clone(),
-      handler,
-    ));
-  }
-  if let Some(handler) = file_drop_handler {
-    webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(
       context,
       label.clone(),
       menu_ids,
@@ -2763,26 +2756,25 @@ fn create_ipc_handler(
   })
 }
 
-/// Create a wry file drop handler from a tauri file drop handler.
+/// Create a wry file drop handler.
 fn create_file_drop_handler(
-  context: Context,
-  label: String,
-  menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
-  js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
-  handler: FileDropHandler<Wry>,
+  context: &Context,
 ) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
+  let window_event_listeners = context.window_event_listeners.clone();
   Box::new(move |window, event| {
-    handler(
-      FileDropEventWrapper(event).into(),
-      DetachedWindow {
-        dispatcher: WryDispatcher {
-          window_id: window.id(),
-          context: context.clone(),
-        },
-        label: label.clone(),
-        menu_ids: menu_ids.clone(),
-        js_event_listeners: js_event_listeners.clone(),
-      },
-    )
+    let event: FileDropEvent = FileDropEventWrapper(event).into();
+    let window_event = WindowEvent::FileDrop(event);
+    let listeners = window_event_listeners.lock().unwrap();
+    if let Some(window_listeners) = listeners.get(&window.id()) {
+      let listeners_map = window_listeners.lock().unwrap();
+      let has_listener = !listeners_map.is_empty();
+      for listener in listeners_map.values() {
+        listener(&window_event);
+      }
+      // block the default OS action on file drop if we had a listener
+      has_listener
+    } else {
+      false
+    }
   })
 }

+ 0 - 16
core/tauri-runtime/src/webview.rs

@@ -184,21 +184,5 @@ pub trait WindowBuilder: WindowBuilderBase {
   fn get_menu(&self) -> Option<&Menu>;
 }
 
-/// The file drop event payload.
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub enum FileDropEvent {
-  /// The file(s) have been dragged onto the window, but have not been dropped yet.
-  Hovered(Vec<PathBuf>),
-  /// The file(s) have been dropped onto the window.
-  Dropped(Vec<PathBuf>),
-  /// The file drop was aborted.
-  Cancelled,
-}
-
 /// IPC handler.
 pub type WebviewIpcHandler<R> = Box<dyn Fn(DetachedWindow<R>, String) + Send>;
-
-/// File drop handler callback
-/// Return `true` in the callback to block the OS' default behavior of handling a file drop.
-pub type FileDropHandler<R> = Box<dyn Fn(FileDropEvent, DetachedWindow<R>) -> bool + Send>;

+ 16 - 6
core/tauri-runtime/src/window.rs

@@ -7,7 +7,7 @@
 use crate::{
   http::{Request as HttpRequest, Response as HttpResponse},
   menu::{Menu, MenuEntry, MenuHash, MenuId},
-  webview::{FileDropHandler, WebviewAttributes, WebviewIpcHandler},
+  webview::{WebviewAttributes, WebviewIpcHandler},
   Dispatch, Runtime, WindowBuilder,
 };
 use serde::Serialize;
@@ -16,6 +16,7 @@ use tauri_utils::config::WindowConfig;
 use std::{
   collections::{HashMap, HashSet},
   hash::{Hash, Hasher},
+  path::PathBuf,
   sync::{mpsc::Sender, Arc, Mutex},
 };
 
@@ -59,6 +60,20 @@ pub enum WindowEvent {
     /// The window inner size.
     new_inner_size: dpi::PhysicalSize<u32>,
   },
+  /// An event associated with the file drop action.
+  FileDrop(FileDropEvent),
+}
+
+/// The file drop event payload.
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum FileDropEvent {
+  /// The file(s) have been dragged onto the window, but have not been dropped yet.
+  Hovered(Vec<PathBuf>),
+  /// The file(s) have been dropped onto the window.
+  Dropped(Vec<PathBuf>),
+  /// The file drop was aborted.
+  Cancelled,
 }
 
 /// A menu event.
@@ -96,9 +111,6 @@ pub struct PendingWindow<R: Runtime> {
   /// How to handle IPC calls on the webview window.
   pub ipc_handler: Option<WebviewIpcHandler<R>>,
 
-  /// How to handle a file dropping onto the webview window.
-  pub file_drop_handler: Option<FileDropHandler<R>>,
-
   /// The resolved URL to load on the webview.
   pub url: String,
 
@@ -143,7 +155,6 @@ impl<R: Runtime> PendingWindow<R> {
         uri_scheme_protocols: Default::default(),
         label,
         ipc_handler: None,
-        file_drop_handler: None,
         url: "tauri://localhost".to_string(),
         menu_ids: Arc::new(Mutex::new(menu_ids)),
         js_event_listeners: Default::default(),
@@ -172,7 +183,6 @@ impl<R: Runtime> PendingWindow<R> {
         uri_scheme_protocols: Default::default(),
         label,
         ipc_handler: None,
-        file_drop_handler: None,
         url: "tauri://localhost".to_string(),
         menu_ids: Arc::new(Mutex::new(menu_ids)),
         js_event_listeners: Default::default(),

+ 1 - 1
core/tauri/src/lib.rs

@@ -213,7 +213,7 @@ pub use {
     webview::{WebviewAttributes, WindowBuilder},
     window::{
       dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
-      WindowEvent,
+      FileDropEvent, WindowEvent,
     },
     ClipboardManager, GlobalShortcutManager, RunIteration, Runtime, TrayIcon, UserAttentionType,
   },

+ 19 - 31
core/tauri/src/manager.rs

@@ -39,8 +39,8 @@ use crate::{
       MimeType, Request as HttpRequest, Response as HttpResponse,
       ResponseBuilder as HttpResponseBuilder,
     },
-    webview::{FileDropEvent, FileDropHandler, WebviewIpcHandler, WindowBuilder},
-    window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
+    webview::{WebviewIpcHandler, WindowBuilder},
+    window::{dpi::PhysicalSize, DetachedWindow, FileDropEvent, PendingWindow, WindowEvent},
     Runtime,
   },
   utils::{
@@ -838,30 +838,6 @@ impl<R: Runtime> WindowManager<R> {
     })
   }
 
-  fn prepare_file_drop(&self, app_handle: AppHandle<R>) -> FileDropHandler<R> {
-    let manager = self.clone();
-    Box::new(move |event, window| {
-      let window = Window::new(manager.clone(), window, app_handle.clone());
-      let _ = match event {
-        FileDropEvent::Hovered(paths) => window.emit_and_trigger("tauri://file-drop-hover", paths),
-        FileDropEvent::Dropped(paths) => {
-          let scopes = window.state::<Scopes>();
-          for path in &paths {
-            if path.is_file() {
-              let _ = scopes.allow_file(path);
-            } else {
-              let _ = scopes.allow_directory(path, false);
-            }
-          }
-          window.emit_and_trigger("tauri://file-drop", paths)
-        }
-        FileDropEvent::Cancelled => window.emit_and_trigger("tauri://file-drop-cancelled", ()),
-        _ => unimplemented!(),
-      };
-      true
-    })
-  }
-
   fn initialization_script(
     &self,
     ipc_script: &str,
@@ -1082,11 +1058,7 @@ impl<R: Runtime> WindowManager<R> {
         app_handle.clone(),
         web_resource_request_handler,
       )?;
-      pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle.clone()));
-    }
-
-    if pending.webview_attributes.file_drop_handler_enabled {
-      pending.file_drop_handler = Some(self.prepare_file_drop(app_handle));
+      pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle));
     }
 
     // in `Windows`, we need to force a data_directory
@@ -1291,6 +1263,22 @@ fn on_window_event<R: Runtime>(
         size: *new_inner_size,
       },
     )?,
+    WindowEvent::FileDrop(event) => match event {
+      FileDropEvent::Hovered(paths) => window.emit_and_trigger("tauri://file-drop-hover", paths)?,
+      FileDropEvent::Dropped(paths) => {
+        let scopes = window.state::<Scopes>();
+        for path in paths {
+          if path.is_file() {
+            let _ = scopes.allow_file(path);
+          } else {
+            let _ = scopes.allow_directory(path, false);
+          }
+        }
+        window.emit_and_trigger("tauri://file-drop", paths)?
+      }
+      FileDropEvent::Cancelled => window.emit_and_trigger("tauri://file-drop-cancelled", ())?,
+      _ => unimplemented!(),
+    },
     _ => unimplemented!(),
   }
   Ok(())