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}, menu::{CustomMenuItem, Menu, MenuEntry, MenuHash, MenuId, MenuItem, MenuUpdate},
monitor::Monitor, monitor::Monitor,
webview::{FileDropEvent, FileDropHandler, WebviewIpcHandler, WindowBuilder, WindowBuilderBase}, webview::{WebviewIpcHandler, WindowBuilder, WindowBuilderBase},
window::{ window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, JsEventListenerKey, PendingWindow, WindowEvent, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent,
}, },
ClipboardManager, Dispatch, Error, ExitRequestedEventAction, GlobalShortcutManager, Result, ClipboardManager, Dispatch, Error, ExitRequestedEventAction, GlobalShortcutManager, Result,
RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType, WindowIcon, RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType, WindowIcon,
@ -2624,7 +2624,6 @@ fn create_webview(
uri_scheme_protocols, uri_scheme_protocols,
mut window_builder, mut window_builder,
ipc_handler, ipc_handler,
file_drop_handler,
label, label,
url, url,
menu_ids, menu_ids,
@ -2663,17 +2662,11 @@ fn create_webview(
.with_url(&url) .with_url(&url)
.unwrap() // safe to unwrap because we validate the URL beforehand .unwrap() // safe to unwrap because we validate the URL beforehand
.with_transparent(is_window_transparent); .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 { if let Some(handler) = ipc_handler {
webview_builder = webview_builder.with_ipc_handler(create_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, context,
label.clone(), label.clone(),
menu_ids, 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( fn create_file_drop_handler(
context: Context, context: &Context,
label: String,
menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
handler: FileDropHandler<Wry>,
) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> { ) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
let window_event_listeners = context.window_event_listeners.clone();
Box::new(move |window, event| { Box::new(move |window, event| {
handler( let event: FileDropEvent = FileDropEventWrapper(event).into();
FileDropEventWrapper(event).into(), let window_event = WindowEvent::FileDrop(event);
DetachedWindow { let listeners = window_event_listeners.lock().unwrap();
dispatcher: WryDispatcher { if let Some(window_listeners) = listeners.get(&window.id()) {
window_id: window.id(), let listeners_map = window_listeners.lock().unwrap();
context: context.clone(), let has_listener = !listeners_map.is_empty();
}, for listener in listeners_map.values() {
label: label.clone(), listener(&window_event);
menu_ids: menu_ids.clone(), }
js_event_listeners: js_event_listeners.clone(), // 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>; 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. /// IPC handler.
pub type WebviewIpcHandler<R> = Box<dyn Fn(DetachedWindow<R>, String) + Send>; 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::{ use crate::{
http::{Request as HttpRequest, Response as HttpResponse}, http::{Request as HttpRequest, Response as HttpResponse},
menu::{Menu, MenuEntry, MenuHash, MenuId}, menu::{Menu, MenuEntry, MenuHash, MenuId},
webview::{FileDropHandler, WebviewAttributes, WebviewIpcHandler}, webview::{WebviewAttributes, WebviewIpcHandler},
Dispatch, Runtime, WindowBuilder, Dispatch, Runtime, WindowBuilder,
}; };
use serde::Serialize; use serde::Serialize;
@ -16,6 +16,7 @@ use tauri_utils::config::WindowConfig;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
path::PathBuf,
sync::{mpsc::Sender, Arc, Mutex}, sync::{mpsc::Sender, Arc, Mutex},
}; };
@ -59,6 +60,20 @@ pub enum WindowEvent {
/// The window inner size. /// The window inner size.
new_inner_size: dpi::PhysicalSize<u32>, 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. /// A menu event.
@ -96,9 +111,6 @@ pub struct PendingWindow<R: Runtime> {
/// How to handle IPC calls on the webview window. /// How to handle IPC calls on the webview window.
pub ipc_handler: Option<WebviewIpcHandler<R>>, 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. /// The resolved URL to load on the webview.
pub url: String, pub url: String,
@ -143,7 +155,6 @@ impl<R: Runtime> PendingWindow<R> {
uri_scheme_protocols: Default::default(), uri_scheme_protocols: Default::default(),
label, label,
ipc_handler: None, ipc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(), url: "tauri://localhost".to_string(),
menu_ids: Arc::new(Mutex::new(menu_ids)), menu_ids: Arc::new(Mutex::new(menu_ids)),
js_event_listeners: Default::default(), js_event_listeners: Default::default(),
@ -172,7 +183,6 @@ impl<R: Runtime> PendingWindow<R> {
uri_scheme_protocols: Default::default(), uri_scheme_protocols: Default::default(),
label, label,
ipc_handler: None, ipc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(), url: "tauri://localhost".to_string(),
menu_ids: Arc::new(Mutex::new(menu_ids)), menu_ids: Arc::new(Mutex::new(menu_ids)),
js_event_listeners: Default::default(), js_event_listeners: Default::default(),

View File

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

View File

@ -39,8 +39,8 @@ use crate::{
MimeType, Request as HttpRequest, Response as HttpResponse, MimeType, Request as HttpRequest, Response as HttpResponse,
ResponseBuilder as HttpResponseBuilder, ResponseBuilder as HttpResponseBuilder,
}, },
webview::{FileDropEvent, FileDropHandler, WebviewIpcHandler, WindowBuilder}, webview::{WebviewIpcHandler, WindowBuilder},
window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent}, window::{dpi::PhysicalSize, DetachedWindow, FileDropEvent, PendingWindow, WindowEvent},
Runtime, Runtime,
}, },
utils::{ 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( fn initialization_script(
&self, &self,
ipc_script: &str, ipc_script: &str,
@ -1082,11 +1058,7 @@ impl<R: Runtime> WindowManager<R> {
app_handle.clone(), app_handle.clone(),
web_resource_request_handler, web_resource_request_handler,
)?; )?;
pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle.clone())); pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle));
}
if pending.webview_attributes.file_drop_handler_enabled {
pending.file_drop_handler = Some(self.prepare_file_drop(app_handle));
} }
// in `Windows`, we need to force a data_directory // in `Windows`, we need to force a data_directory
@ -1291,6 +1263,22 @@ fn on_window_event<R: Runtime>(
size: *new_inner_size, 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!(), _ => unimplemented!(),
} }
Ok(()) Ok(())