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

This commit is contained in:
Lucas Fernandes Nogueira 2022-03-13 11:28:16 -03:00 committed by GitHub
parent c40f640ddc
commit 07d1584cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 76 additions and 84 deletions

View File

@ -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.

View File

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

View File

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

View File

@ -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
}
})
}

View File

@ -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>;

View File

@ -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(),

View File

@ -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,
},

View File

@ -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(())