From 20cb051aba28009c70dad838b2a9b1575cb5363a Mon Sep 17 00:00:00 2001 From: chip Date: Thu, 1 Apr 2021 22:04:14 -0700 Subject: [PATCH] feat: add WindowProxy to file drop handler closure (#140) * add WindowProxy to file drop handler closure linux only for right now * add support for windows + osx (i think) * add changefile * docs and move low level handler to webview module * cargo fmt * Fix resize method Co-authored-by: Ngo Iok Ui --- .changes/window-file-drop-handler.md | 5 +++++ examples/dragndrop.rs | 2 +- src/application/general.rs | 30 +++++++++++++++++-------- src/application/gtkrs.rs | 28 +++++++++++++++++------ src/application/mod.rs | 12 +++++----- src/file_drop.rs | 30 +++++++++++++++++++++++-- src/lib.rs | 33 ++++++++++++++-------------- src/webview/linux/file_drop.rs | 4 ++-- src/webview/linux/mod.rs | 14 ++++++------ src/webview/macos/file_drop.rs | 11 ++++------ src/webview/macos/mod.rs | 17 +++++++------- src/webview/mod.rs | 15 ++++++++++++- 12 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 .changes/window-file-drop-handler.md diff --git a/.changes/window-file-drop-handler.md b/.changes/window-file-drop-handler.md new file mode 100644 index 000000000..ae0a5d1a4 --- /dev/null +++ b/.changes/window-file-drop-handler.md @@ -0,0 +1,5 @@ +--- +"wry": minor +--- + +Adds a `WindowProxy` to the file drop handler closure - `WindowFileDropHandler`. \ No newline at end of file diff --git a/examples/dragndrop.rs b/examples/dragndrop.rs index c5364e2e2..a7deebd6b 100644 --- a/examples/dragndrop.rs +++ b/examples/dragndrop.rs @@ -16,7 +16,7 @@ fn main() -> Result<()> { }, None, None, - Some(Box::new(|data| { + Some(Box::new(|_, data| { println!("Window 1: {:?}", data); false // Returning true will block the OS default behaviour. })), diff --git a/src/application/general.rs b/src/application/general.rs index c86823757..196928f6d 100644 --- a/src/application/general.rs +++ b/src/application/general.rs @@ -1,8 +1,8 @@ use crate::{ application::{App, AppProxy, InnerWebViewAttributes, InnerWindowAttributes}, ApplicationProxy, Attributes, CustomProtocol, Error, Event as WryEvent, Icon, Message, Result, - WebView, WebViewBuilder, WindowEvent as WryWindowEvent, WindowMessage, WindowProxy, - WindowRpcHandler, + WebView, WebViewBuilder, WindowEvent as WryWindowEvent, WindowFileDropHandler, WindowMessage, + WindowProxy, WindowRpcHandler, }; #[cfg(target_os = "windows")] @@ -46,8 +46,6 @@ use { winit::platform::windows::WindowExtWindows, }; -use crate::FileDropHandler; - type EventLoopProxy = winit::event_loop::EventLoopProxy; #[derive(Clone)] @@ -68,7 +66,7 @@ impl AppProxy for InnerApplicationProxy { fn add_window( &self, attributes: Attributes, - file_drop_handler: Option, + file_drop_handler: Option, rpc_handler: Option, custom_protocol: Option, ) -> Result { @@ -150,7 +148,7 @@ impl App for InnerApplication { fn create_webview( &mut self, attributes: Attributes, - file_drop_handler: Option, + file_drop_handler: Option, rpc_handler: Option, custom_protocol: Option, ) -> Result { @@ -407,7 +405,7 @@ fn _create_webview( window: Window, custom_protocol: Option, rpc_handler: Option, - file_drop_handler: Option, + file_drop_handler: Option, attributes: InnerWebViewAttributes, ) -> Result { @@ -425,10 +423,11 @@ fn _create_webview( webview = webview.register_protocol(protocol.name, protocol.handler) } + let proxy_ = proxy.clone(); webview = webview.set_rpc_handler(Box::new(move |mut request| { let proxy = WindowProxy::new( ApplicationProxy { - inner: proxy.clone(), + inner: proxy_.clone(), }, window_id, ); @@ -448,7 +447,20 @@ fn _create_webview( } })); - webview = webview.set_file_drop_handler(file_drop_handler); + webview = webview.set_file_drop_handler(Some(Box::new(move |event| { + let proxy = WindowProxy::new( + ApplicationProxy { + inner: proxy.clone(), + }, + window_id, + ); + + if let Some(file_drop_handler) = &file_drop_handler { + file_drop_handler(proxy, event) + } else { + false + } + }))); webview = match attributes.url { Some(url) => webview.load_url(&url)?, diff --git a/src/application/gtkrs.rs b/src/application/gtkrs.rs index 2fe202035..647638be5 100644 --- a/src/application/gtkrs.rs +++ b/src/application/gtkrs.rs @@ -1,7 +1,7 @@ use crate::{ application::{App, AppProxy, InnerWebViewAttributes, InnerWindowAttributes}, - ApplicationProxy, Attributes, CustomProtocol, Error, Event as WryEvent, FileDropHandler, Icon, - Message, Result, WebView, WebViewBuilder, WindowEvent as WryWindowEvent, WindowMessage, + ApplicationProxy, Attributes, CustomProtocol, Error, Event as WryEvent, Icon, Message, Result, + WebView, WebViewBuilder, WindowEvent as WryWindowEvent, WindowFileDropHandler, WindowMessage, WindowProxy, WindowRpcHandler, }; @@ -51,7 +51,7 @@ impl AppProxy for InnerApplicationProxy { fn add_window( &self, attributes: Attributes, - file_drop_handler: Option, + file_drop_handler: Option, rpc_handler: Option, custom_protocol: Option, ) -> Result { @@ -104,7 +104,7 @@ impl App for InnerApplication { fn create_webview( &mut self, attributes: Attributes, - file_drop_handler: Option, + file_drop_handler: Option, rpc_handler: Option, custom_protocol: Option, ) -> Result { @@ -452,7 +452,7 @@ fn _create_webview( window: ApplicationWindow, custom_protocol: Option, rpc_handler: Option, - file_drop_handler: Option, + file_drop_handler: Option, attributes: InnerWebViewAttributes, ) -> Result { @@ -470,10 +470,11 @@ fn _create_webview( webview = webview.register_protocol(protocol.name, protocol.handler); } + let proxy_ = proxy.clone(); webview = webview.set_rpc_handler(Box::new(move |mut request| { let proxy = WindowProxy::new( ApplicationProxy { - inner: proxy.clone(), + inner: proxy_.clone(), }, window_id, ); @@ -493,7 +494,20 @@ fn _create_webview( } })); - webview = webview.set_file_drop_handler(file_drop_handler); + webview = webview.set_file_drop_handler(Some(Box::new(move |event| { + let proxy = WindowProxy::new( + ApplicationProxy { + inner: proxy.clone(), + }, + window_id, + ); + + if let Some(file_drop_handler) = &file_drop_handler { + file_drop_handler(proxy, event) + } else { + false + } + }))); let webview = webview.build()?; Ok(webview) diff --git a/src/application/mod.rs b/src/application/mod.rs index 83e10df20..090d00e9b 100644 --- a/src/application/mod.rs +++ b/src/application/mod.rs @@ -15,7 +15,7 @@ mod attributes; pub use attributes::{Attributes, CustomProtocol, Icon, WindowRpcHandler}; pub(crate) use attributes::{InnerWebViewAttributes, InnerWindowAttributes}; -use crate::{FileDropHandler, Result}; +use crate::{Result, WindowFileDropHandler}; use std::sync::mpsc::Sender; @@ -64,7 +64,7 @@ pub enum Message { NewWindow( Attributes, Sender, - Option, + Option, Option, Option, ), @@ -97,7 +97,7 @@ impl ApplicationProxy { attributes: Attributes, rpc_handler: Option, custom_protocol: Option, - file_drop_handler: Option, + file_drop_handler: Option, ) -> Result { let id = self .inner @@ -116,7 +116,7 @@ trait AppProxy { fn add_window( &self, attributes: Attributes, - file_drop_handler: Option, + file_drop_handler: Option, rpc_handler: Option, custom_protocol: Option, ) -> Result; @@ -353,7 +353,7 @@ impl Application { attributes: Attributes, rpc_handler: Option, custom_protocol: Option, - file_drop_handler: Option, + file_drop_handler: Option, ) -> Result { let id = self @@ -392,7 +392,7 @@ trait App: Sized { fn create_webview( &mut self, attributes: Attributes, - file_drop_handler: Option, + file_drop_handler: Option, rpc_handler: Option, custom_protocol: Option, ) -> Result; diff --git a/src/file_drop.rs b/src/file_drop.rs index c29e00a84..8a90f0fc6 100644 --- a/src/file_drop.rs +++ b/src/file_drop.rs @@ -1,5 +1,7 @@ use std::path::PathBuf; +use crate::WindowProxy; + /// An event enumeration sent to [`FileDropHandler`]. #[derive(Debug, Serialize, Clone)] pub enum FileDropEvent { @@ -11,11 +13,35 @@ pub enum FileDropEvent { Cancelled, } -/// A listener closure to process incoming [`FileDropEvent`] of the window. +/// A listener closure to process incoming [`FileDropEvent`] of the webview. +/// +/// Users can pass a [`WindowFileDropHandler`] to [`Application::add_window_with_configs`](crate::Application::add_window_with_configs) +/// to register incoming file drop events to a closure. /// /// # Blocking OS Default Behavior /// Return `true` in the callback to block the OS' default behavior of handling a file drop. /// /// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. /// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. -pub type FileDropHandler = Box bool + Send>; +/// +/// # Example +/// +/// ```no_run +/// use wry::{Application, Result, WindowProxy, FileDropEvent}; +/// +/// fn main() -> Result<()> { +/// let mut app = Application::new()?; +/// let file_drop = Box::new(|window: WindowProxy, event: FileDropEvent| { +/// // Use the `WindowProxy` to modify the window, eg: `set_fullscreen` etc. +/// // +/// // Use the `FileDropEvent` to see the current state of the file drop. +/// // +/// // Return `true` to block the default file drop behavior of the OS. +/// false +/// }); +/// app.add_window_with_configs(Default::default(), None, None, Some(file_drop))?; +/// app.run(); +/// Ok(()) +/// } +/// ``` +pub type WindowFileDropHandler = Box bool + Send>; diff --git a/src/lib.rs b/src/lib.rs index 132559230..0eb75e7d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,34 +70,35 @@ extern crate thiserror; #[macro_use] extern crate objc; -mod file_drop; -#[cfg(feature = "file-drop")] -pub use file_drop::{FileDropEvent, FileDropHandler}; -#[cfg(not(feature = "file-drop"))] -pub(crate) use file_drop::{FileDropEvent, FileDropHandler}; +use std::sync::mpsc::{RecvError, SendError}; + +pub use serde_json::Value; +use url::ParseError; +#[cfg(not(target_os = "linux"))] +use winit::window::BadIcon; #[cfg(feature = "protocol")] pub use application::CustomProtocol; #[cfg(not(feature = "protocol"))] pub(crate) use application::CustomProtocol; - -mod application; -pub mod webview; - pub use application::{ Application, ApplicationProxy, Attributes, Event, Icon, Message, WindowEvent, WindowId, WindowMessage, WindowProxy, WindowRpcHandler, }; -pub use serde_json::Value; +#[cfg(not(feature = "file-drop"))] +pub(crate) use file_drop::FileDropEvent; +#[cfg(feature = "file-drop")] +pub use file_drop::{FileDropEvent, WindowFileDropHandler}; +#[cfg(not(feature = "file-drop"))] +pub(crate) use webview::FileDropHandler; +#[cfg(feature = "file-drop")] +pub use webview::FileDropHandler; pub(crate) use webview::{RpcHandler, WebView, WebViewBuilder}; pub use webview::{RpcRequest, RpcResponse}; -#[cfg(not(target_os = "linux"))] -use winit::window::BadIcon; - -use std::sync::mpsc::{RecvError, SendError}; - -use url::ParseError; +mod application; +mod file_drop; +pub mod webview; /// Convenient type alias of Result type for wry. pub type Result = std::result::Result; diff --git a/src/webview/linux/file_drop.rs b/src/webview/linux/file_drop.rs index acc6f384b..6ecb4c261 100644 --- a/src/webview/linux/file_drop.rs +++ b/src/webview/linux/file_drop.rs @@ -1,10 +1,10 @@ -use crate::{FileDropEvent, FileDropHandler}; - use std::{cell::Cell, path::PathBuf, rc::Rc}; use gtk::WidgetExt; use webkit2gtk::WebView; +use crate::{webview::FileDropHandler, FileDropEvent}; + pub(crate) fn connect_drag_event(webview: Rc, handler: FileDropHandler) { let listener = Rc::new((handler, Cell::new(None))); diff --git a/src/webview/linux/mod.rs b/src/webview/linux/mod.rs index 5b434fc54..8c144d415 100644 --- a/src/webview/linux/mod.rs +++ b/src/webview/linux/mod.rs @@ -1,10 +1,3 @@ -mod file_drop; - -use crate::{ - webview::{mimetype::MimeType, WV}, - Error, FileDropHandler, Result, RpcHandler, -}; - use std::{path::PathBuf, rc::Rc}; use gdk::RGBA; @@ -18,6 +11,13 @@ use webkit2gtk::{ WebContextExt, WebView, WebViewExt, WebViewExtManual, }; +use crate::{ + webview::{mimetype::MimeType, FileDropHandler, WV}, + Error, Result, RpcHandler, +}; + +mod file_drop; + pub struct InnerWebView { webview: Rc, } diff --git a/src/webview/macos/file_drop.rs b/src/webview/macos/file_drop.rs index db7127597..e31bd2e4d 100644 --- a/src/webview/macos/file_drop.rs +++ b/src/webview/macos/file_drop.rs @@ -1,24 +1,21 @@ -use crate::{FileDropEvent, FileDropHandler}; - use std::{ ffi::{c_void, CStr}, path::PathBuf, }; -use once_cell::sync::Lazy; - use cocoa::base::{id, BOOL, YES}; use objc::{ declare::ClassDecl, - runtime::{Object, Sel}, + runtime::{class_getInstanceMethod, method_getImplementation, Object, Sel}, }; +use once_cell::sync::Lazy; + +use crate::{webview::FileDropHandler, FileDropEvent}; pub(crate) type NSDragOperation = cocoa::foundation::NSUInteger; #[allow(non_upper_case_globals)] const NSDragOperationLink: NSDragOperation = 2; -use objc::runtime::{class_getInstanceMethod, method_getImplementation}; - static OBJC_DRAGGING_ENTERED: Lazy NSDragOperation> = Lazy::new(|| unsafe { std::mem::transmute(method_getImplementation(class_getInstanceMethod( diff --git a/src/webview/macos/mod.rs b/src/webview/macos/mod.rs index 59e1ce352..be0520fcb 100644 --- a/src/webview/macos/mod.rs +++ b/src/webview/macos/mod.rs @@ -1,11 +1,3 @@ -mod file_drop; - -use crate::{ - webview::{mimetype::MimeType, WV}, - FileDropHandler, Result, RpcHandler, -}; -use file_drop::{add_file_drop_methods, set_file_drop_handler}; - use std::{ ffi::{c_void, CStr}, os::raw::c_char, @@ -27,6 +19,15 @@ use objc_id::Id; use url::Url; use winit::{platform::macos::WindowExtMacOS, window::Window}; +use file_drop::{add_file_drop_methods, set_file_drop_handler}; + +use crate::{ + webview::{mimetype::MimeType, FileDropHandler, WV}, + Result, RpcHandler, +}; + +mod file_drop; + pub struct InnerWebView { webview: Id, manager: id, diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 7c95ba6ee..051ae6615 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -23,7 +23,7 @@ mod win32; #[cfg(feature = "win32")] use win32::*; -use crate::{Error, FileDropHandler, Result}; +use crate::{Error, FileDropEvent, Result}; use std::{ path::PathBuf, @@ -57,6 +57,19 @@ use winit::window::Window; /// Both functions return promises but `notify()` resolves immediately. pub type RpcHandler = Box Option + Send>; +/// A listener closure to process incoming [`FileDropEvent`] of the webview. +/// +/// This is the handler for lower level webview creation. For higher application level, please see +/// [`WindowFileDropHandler`](create::WindowFileDropHandler). Users can pass a `FileDropHandler` to +/// [`WebViewBuilder::set_file_drop_handler`] to register an incoming file drop event to a closure. +/// +/// # Blocking OS Default Behavior +/// Return `true` in the callback to block the OS' default behavior of handling a file drop. +/// +/// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. +/// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. +pub type FileDropHandler = Box bool + Send>; + // Helper so all platforms handle RPC messages consistently. fn rpc_proxy(js: String, handler: &RpcHandler) -> Result> { let req = serde_json::from_str::(&js)