From 507f02ec8b590ca05335b0281778daadf16f3da4 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 3 Jan 2023 16:17:54 +0200 Subject: [PATCH 01/16] Add cursor position to `DroppedFile` and `HoveredFile` events --- CHANGELOG.md | 1 + examples/window_icon.rs | 2 +- src/event.rs | 26 +++++++++++---- src/platform_impl/linux/x11/dnd.rs | 3 ++ .../linux/x11/event_processor.rs | 25 +++++++++++---- src/platform_impl/macos/window_delegate.rs | 14 ++++++-- src/platform_impl/windows/definitions.rs | 6 ++-- src/platform_impl/windows/drop_handler.rs | 32 ++++++++++++------- 8 files changed, 79 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4d12343ca..92eb081e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- Add cursor position to `DroppedFile` and `HoveredFile` events. - **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`. - **Breaking:** Removed platform specific `WindowBuilder::with_parent` API in favor of `WindowBuilder::with_parent_window`. - On Windows, retain `WS_MAXIMIZE` window style when un-minimizing a maximized window. diff --git a/examples/window_icon.rs b/examples/window_icon.rs index ed98b3d577..343eb44096 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -37,7 +37,7 @@ fn main() { use winit::event::WindowEvent::*; match event { CloseRequested => control_flow.set_exit(), - DroppedFile(path) => { + DroppedFile { path, .. } => { window.set_window_icon(Some(load_icon(&path))); } _ => (), diff --git a/src/event.rs b/src/event.rs index 7e169c4b43..2828dd0b8e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -337,13 +337,21 @@ pub enum WindowEvent<'a> { /// /// When the user drops multiple files at once, this event will be emitted for each file /// separately. - DroppedFile(PathBuf), + DroppedFile { + path: PathBuf, + // (x,y) coords in pixels relative to the top-left corner of the window. + position: PhysicalPosition, + }, /// A file is being hovered over the window. /// /// When the user hovers multiple files at once, this event will be emitted for each file /// separately. - HoveredFile(PathBuf), + HoveredFile { + path: PathBuf, + // (x,y) coords in pixels relative to the top-left corner of the window. + position: PhysicalPosition, + }, /// A file was hovered, but has exited the window. /// @@ -529,8 +537,14 @@ impl Clone for WindowEvent<'static> { Moved(pos) => Moved(*pos), CloseRequested => CloseRequested, Destroyed => Destroyed, - DroppedFile(file) => DroppedFile(file.clone()), - HoveredFile(file) => HoveredFile(file.clone()), + DroppedFile { path, position } => DroppedFile { + path: path.clone(), + position: *position, + }, + HoveredFile { path, position } => HoveredFile { + path: path.clone(), + position: *position, + }, HoveredFileCancelled => HoveredFileCancelled, ReceivedCharacter(c) => ReceivedCharacter(*c), Focused(f) => Focused(*f), @@ -639,8 +653,8 @@ impl<'a> WindowEvent<'a> { Moved(position) => Some(Moved(position)), CloseRequested => Some(CloseRequested), Destroyed => Some(Destroyed), - DroppedFile(file) => Some(DroppedFile(file)), - HoveredFile(file) => Some(HoveredFile(file)), + DroppedFile { path, position } => Some(DroppedFile { path, position }), + HoveredFile { path, position } => Some(HoveredFile { path, position }), HoveredFileCancelled => Some(HoveredFileCancelled), ReceivedCharacter(c) => Some(ReceivedCharacter(c)), Focused(focused) => Some(Focused(focused)), diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index ab9ab56c2d..839e8e2f0c 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -95,6 +95,8 @@ pub struct Dnd { pub type_list: Option>, // Populated by XdndPosition event handler pub source_window: Option, + // Populated by XdndPosition event handler + pub position: (i64, i64), // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option, DndDataParseError>>, } @@ -108,6 +110,7 @@ impl Dnd { version: None, type_list: None, source_window: None, + position: (0, 0), result: None, }) } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 11c9a6a954..8b7b9fdea5 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -228,10 +228,11 @@ impl EventProcessor { // where `shift = mem::size_of::() * 8` // Note that coordinates are in "desktop space", not "window space" // (in X11 parlance, they're root window coordinates) - //let packed_coordinates = client_msg.data.get_long(2); - //let shift = mem::size_of::() * 8; - //let x = packed_coordinates >> shift; - //let y = packed_coordinates & !(x << shift); + let packed_coordinates = client_msg.data.get_long(2); + let shift = std::mem::size_of::() * 8; + let x = packed_coordinates >> shift; + let y = packed_coordinates & !(x << shift); + self.dnd.position = (x, y); // By our own state flow, `version` should never be `None` at this point. let version = self.dnd.version.unwrap_or(5); @@ -277,7 +278,13 @@ impl EventProcessor { for path in path_list { callback(Event::WindowEvent { window_id, - event: WindowEvent::DroppedFile(path.clone()), + event: WindowEvent::DroppedFile { + path: path.clone(), + position: PhysicalPosition::new( + self.dnd.position.0 as f64, + self.dnd.position.1 as f64, + ), + }, }); } } @@ -319,7 +326,13 @@ impl EventProcessor { for path in path_list { callback(Event::WindowEvent { window_id, - event: WindowEvent::HoveredFile(path.clone()), + event: WindowEvent::HoveredFile { + path: path.clone(), + position: PhysicalPosition::new( + self.dnd.position.0 as f64, + self.dnd.position.1 as f64, + ), + }, }); } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 0d77053134..cea5180208 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -3,7 +3,7 @@ use std::ptr; use objc2::declare::{Ivar, IvarDrop}; -use objc2::foundation::{NSArray, NSObject, NSString}; +use objc2::foundation::{NSArray, NSObject, NSPoint, NSString}; use objc2::rc::{autoreleasepool, Id, Shared}; use objc2::runtime::Object; use objc2::{class, declare_class, msg_send, msg_send_id, sel, ClassType}; @@ -172,9 +172,13 @@ declare_class!( let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; + let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let scale_factor = self.window.scale_factor(); + let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(scale_factor); + filenames.into_iter().for_each(|file| { let path = PathBuf::from(file.to_string()); - self.emit_event(WindowEvent::HoveredFile(path)); + self.emit_event(WindowEvent::HoveredFile { path, position }); }); true @@ -198,9 +202,13 @@ declare_class!( let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; + let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let scale_factor = self.window.scale_factor(); + let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(scale_factor); + filenames.into_iter().for_each(|file| { let path = PathBuf::from(file.to_string()); - self.emit_event(WindowEvent::DroppedFile(path)); + self.emit_event(WindowEvent::DroppedFile { path, position }); }); true diff --git a/src/platform_impl/windows/definitions.rs b/src/platform_impl/windows/definitions.rs index b4a461c947..b0c7e96eee 100644 --- a/src/platform_impl/windows/definitions.rs +++ b/src/platform_impl/windows/definitions.rs @@ -76,13 +76,13 @@ pub struct IDropTargetVtbl { This: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: u32, - pt: *const POINTL, + pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT, pub DragOver: unsafe extern "system" fn( This: *mut IDropTarget, grfKeyState: u32, - pt: *const POINTL, + pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT, pub DragLeave: unsafe extern "system" fn(This: *mut IDropTarget) -> HRESULT, @@ -90,7 +90,7 @@ pub struct IDropTargetVtbl { This: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: u32, - pt: *const POINTL, + pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT, } diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 8ffb87f04f..34ed3de055 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -9,7 +9,8 @@ use std::{ use windows_sys::{ core::{IUnknown, GUID, HRESULT}, Win32::{ - Foundation::{DV_E_FORMATETC, HWND, POINTL, S_OK}, + Foundation::{DV_E_FORMATETC, HWND, POINT, POINTL, S_OK}, + Graphics::Gdi::ScreenToClient, System::{ Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYMED_HGLOBAL}, Ole::{DROPEFFECT_COPY, DROPEFFECT_NONE}, @@ -19,9 +20,12 @@ use windows_sys::{ }, }; -use crate::platform_impl::platform::{ - definitions::{IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl}, - WindowId, +use crate::{ + dpi::PhysicalPosition, + platform_impl::platform::{ + definitions::{IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl}, + WindowId, + }, }; use crate::{event::Event, window::WindowId as RootWindowId}; @@ -89,15 +93,18 @@ impl FileDropHandler { this: *mut IDropTarget, pDataObj: *const IDataObject, _grfKeyState: u32, - _pt: *const POINTL, + pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { use crate::event::WindowEvent::HoveredFile; let drop_handler = Self::from_interface(this); - let hdrop = Self::iterate_filenames(pDataObj, |filename| { + let mut pt = POINT { x: pt.x, y: pt.y }; + ScreenToClient(drop_handler.window, &mut pt); + let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); + let hdrop = Self::iterate_filenames(pDataObj, |path| { drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: HoveredFile(filename), + event: HoveredFile { path, position }, }); }); drop_handler.hovered_is_valid = hdrop.is_some(); @@ -114,7 +121,7 @@ impl FileDropHandler { pub unsafe extern "system" fn DragOver( this: *mut IDropTarget, _grfKeyState: u32, - _pt: *const POINTL, + _pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { let drop_handler = Self::from_interface(this); @@ -140,15 +147,18 @@ impl FileDropHandler { this: *mut IDropTarget, pDataObj: *const IDataObject, _grfKeyState: u32, - _pt: *const POINTL, + pt: POINTL, _pdwEffect: *mut u32, ) -> HRESULT { use crate::event::WindowEvent::DroppedFile; let drop_handler = Self::from_interface(this); - let hdrop = Self::iterate_filenames(pDataObj, |filename| { + let mut pt = POINT { x: pt.x, y: pt.y }; + ScreenToClient(drop_handler.window, &mut pt); + let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); + let hdrop = Self::iterate_filenames(pDataObj, |path| { drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: DroppedFile(filename), + event: DroppedFile { path, position }, }); }); if let Some(hdrop) = hdrop { From 166189c0e02956b50da96f81c370337c8e0af2a5 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 3 Jan 2023 16:32:21 +0200 Subject: [PATCH 02/16] fix build on linux x86 --- src/platform_impl/linux/x11/dnd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 839e8e2f0c..88ce1fedc3 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -96,7 +96,7 @@ pub struct Dnd { // Populated by XdndPosition event handler pub source_window: Option, // Populated by XdndPosition event handler - pub position: (i64, i64), + pub position: (c_long, c_long), // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option, DndDataParseError>>, } From 4746750b2262ed74fef95cc0e0b109be852f1dd1 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Mon, 16 Jan 2023 22:14:55 +0200 Subject: [PATCH 03/16] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92eb081e58..bb2b0b425f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased -- Add cursor position to `DroppedFile` and `HoveredFile` events. +- **Breaking:** Add cursor position to `DroppedFile` and `HoveredFile` events. - **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`. - **Breaking:** Removed platform specific `WindowBuilder::with_parent` API in favor of `WindowBuilder::with_parent_window`. - On Windows, retain `WS_MAXIMIZE` window style when un-minimizing a maximized window. From 21f085278508879ac10f3b70a67e49a1c3e73a29 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 17 Jan 2023 13:58:13 +0200 Subject: [PATCH 04/16] Update src/event.rs Co-authored-by: Kirill Chibisov --- src/event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index 2828dd0b8e..56ce17f817 100644 --- a/src/event.rs +++ b/src/event.rs @@ -339,7 +339,7 @@ pub enum WindowEvent<'a> { /// separately. DroppedFile { path: PathBuf, - // (x,y) coords in pixels relative to the top-left corner of the window. + /// The position of the mouse cursor, similar to [`WindowEvent::CursorMoved`]. position: PhysicalPosition, }, From ad12fb91f3f6d6462c41a34e9986625c99b92bb6 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 17 Jan 2023 13:58:19 +0200 Subject: [PATCH 05/16] Update src/event.rs Co-authored-by: Kirill Chibisov --- src/event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index 56ce17f817..dfff494a79 100644 --- a/src/event.rs +++ b/src/event.rs @@ -349,7 +349,7 @@ pub enum WindowEvent<'a> { /// separately. HoveredFile { path: PathBuf, - // (x,y) coords in pixels relative to the top-left corner of the window. + /// The position of the mouse cursor, similar to [`WindowEvent::CursorMoved`]. position: PhysicalPosition, }, From 959eb5f2dcd92f1fcb28bf586d0ad57e34485686 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 17 Jan 2023 17:00:53 +0200 Subject: [PATCH 06/16] x11: translate root coords to window coords --- examples/window_icon.rs | 6 ++- .../linux/x11/event_processor.rs | 38 +++++++++++---- src/platform_impl/linux/x11/util/geometry.rs | 46 +++++++++++++++---- src/platform_impl/linux/x11/window.rs | 4 +- 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 343eb44096..187e3d7760 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -37,7 +37,11 @@ fn main() { use winit::event::WindowEvent::*; match event { CloseRequested => control_flow.set_exit(), - DroppedFile { path, .. } => { + HoveredFile { position, .. } => { + dbg!("Hovered", position); + } + DroppedFile { path, position } => { + dbg!("Dropped", position); window.set_window_icon(Some(load_icon(&path))); } _ => (), diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index ce5efb633d..c23ffc24f0 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -275,15 +275,25 @@ impl EventProcessor { let (source_window, state) = if let Some(source_window) = self.dnd.source_window { if let Some(Ok(ref path_list)) = self.dnd.result { + let coords = wt + .xconn + .translate_coords( + source_window, + window, + self.dnd.position.0 as _, + self.dnd.position.1 as _, + ) + .expect("Failed to translate window coordinates"); + + let position = + PhysicalPosition::new(coords.x_rel as f64, coords.y_rel as f64); + for path in path_list { callback(Event::WindowEvent { window_id, event: WindowEvent::DroppedFile { path: path.clone(), - position: PhysicalPosition::new( - self.dnd.position.0 as f64, - self.dnd.position.1 as f64, - ), + position, }, }); } @@ -323,15 +333,27 @@ impl EventProcessor { if let Ok(mut data) = unsafe { self.dnd.read_data(window) } { let parse_result = self.dnd.parse_data(&mut data); if let Ok(ref path_list) = parse_result { + let source_window = self.dnd.source_window.unwrap_or(wt.root); + + let coords = wt + .xconn + .translate_coords( + source_window, + window, + self.dnd.position.0 as _, + self.dnd.position.1 as _, + ) + .expect("Failed to translate window coordinates"); + + let position = + PhysicalPosition::new(coords.x_rel as f64, coords.y_rel as f64); + for path in path_list { callback(Event::WindowEvent { window_id, event: WindowEvent::HoveredFile { path: path.clone(), - position: PhysicalPosition::new( - self.dnd.position.0 as f64, - self.dnd.position.1 as f64, - ), + position, }, }); } diff --git a/src/platform_impl/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs index 7afced804a..28b28b5f94 100644 --- a/src/platform_impl/linux/x11/util/geometry.rs +++ b/src/platform_impl/linux/x11/util/geometry.rs @@ -42,8 +42,8 @@ impl AaRect { #[derive(Debug, Default)] pub struct TranslatedCoords { - pub x_rel_root: c_int, - pub y_rel_root: c_int, + pub x_rel: c_int, + pub y_rel: c_int, pub child: ffi::Window, } @@ -142,7 +142,7 @@ impl FrameExtentsHeuristic { impl XConnection { // This is adequate for inner_position - pub fn translate_coords( + pub fn translate_coords_root( &self, window: ffi::Window, root: ffi::Window, @@ -156,8 +156,34 @@ impl XConnection { root, 0, 0, - &mut coords.x_rel_root, - &mut coords.y_rel_root, + &mut coords.x_rel, + &mut coords.y_rel, + &mut coords.child, + ); + } + + self.check_errors()?; + Ok(coords) + } + + pub fn translate_coords( + &self, + src_w: ffi::Window, + dest_w: ffi::Window, + src_x: i32, + src_y: i32, + ) -> Result { + let mut coords = TranslatedCoords::default(); + + unsafe { + (self.xlib.XTranslateCoordinates)( + self.display, + src_w, + dest_w, + src_x, + src_y, + &mut coords.x_rel, + &mut coords.y_rel, &mut coords.child, ); } @@ -284,11 +310,11 @@ impl XConnection { // With rare exceptions, this is the position of a nested window. Cases where the window // isn't nested are outlined in the comments throghout this function, but in addition to // that, fullscreen windows often aren't nested. - let (inner_y_rel_root, child) = { + let (inner_y_rel, child) = { let coords = self - .translate_coords(window, root) + .translate_coords_root(window, root) .expect("Failed to translate window coordinates"); - (coords.y_rel_root, coords.child) + (coords.y_rel, coords.child) }; let (width, height, border) = { @@ -331,7 +357,7 @@ impl XConnection { // having a position (-10, -8). // * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px // on all sides, and there's no additional border. - // * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root. + // * Enlightenment otherwise gets a y position equivalent to inner_y_rel. // Without decorations, there's no difference. This is presumably related to // Enlightenment's fairly unique concept of window position; it interprets // positions given to XMoveWindow as a client area position rather than a position @@ -363,7 +389,7 @@ impl XConnection { // area, we can figure out what's in between. let diff_x = outer_width.saturating_sub(width); let diff_y = outer_height.saturating_sub(height); - let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint; + let offset_y = inner_y_rel.saturating_sub(outer_y) as c_uint; let left = diff_x / 2; let right = left; diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 9f61d22d68..9bfc7090c0 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1066,8 +1066,8 @@ impl UnownedWindow { // This should be okay to unwrap since the only error XTranslateCoordinates can return // is BadWindow, and if the window handle is bad we have bigger problems. self.xconn - .translate_coords(self.xwindow, self.root) - .map(|coords| (coords.x_rel_root, coords.y_rel_root)) + .translate_coords_root(self.xwindow, self.root) + .map(|coords| (coords.x_rel, coords.y_rel)) .unwrap() } From 3104f6e9c73605d38c57f59ca45147122c95a01c Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 18 Jan 2023 19:33:40 +0200 Subject: [PATCH 07/16] Update CHANGELOG.md Co-authored-by: Kirill Chibisov --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5903bc8dd7..9ed51e03a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased -- **Breaking:** Add cursor position to `DroppedFile` and `HoveredFile` events. +- **Breaking:** Add cursor position to `WindowEvent::DroppedFile` and `WindowEvent::HoveredFile` events. - Add `Window::has_focus`. - On Windows, fix `Window::set_minimized(false)` not working for windows minimized by `Win + D` hotkey. - **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`. From 6b91ea3085c4b1cae4f9ab8e3016e1f82e58b2e4 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 19 Jan 2023 14:05:23 +0200 Subject: [PATCH 08/16] macOS: convert position to winit coordinate system --- src/platform_impl/macos/window_delegate.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index cea5180208..afc7cd88bf 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -3,7 +3,7 @@ use std::ptr; use objc2::declare::{Ivar, IvarDrop}; -use objc2::foundation::{NSArray, NSObject, NSPoint, NSString}; +use objc2::foundation::{NSArray, NSObject, NSPoint, NSRect, NSString}; use objc2::rc::{autoreleasepool, Id, Shared}; use objc2::runtime::Object; use objc2::{class, declare_class, msg_send, msg_send_id, sel, ClassType}; @@ -173,8 +173,13 @@ declare_class!( let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let rect = NSRect { + origin: dl, + ..Default::default() + }; + let y = util::bottom_left_to_top_left(rect); let scale_factor = self.window.scale_factor(); - let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(scale_factor); + let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); filenames.into_iter().for_each(|file| { let path = PathBuf::from(file.to_string()); @@ -203,8 +208,13 @@ declare_class!( let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let rect = NSRect { + origin: dl, + ..Default::default() + }; + let y = util::bottom_left_to_top_left(rect); let scale_factor = self.window.scale_factor(); - let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(scale_factor); + let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); filenames.into_iter().for_each(|file| { let path = PathBuf::from(file.to_string()); From c8f8a99587db9e74acc3d6f926310f1d20d4729c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 21 Jan 2023 17:49:37 +0100 Subject: [PATCH 09/16] Properly convert DnD location coordinates --- src/platform_impl/macos/window_delegate.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index afc7cd88bf..1e99bcc21d 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -3,7 +3,7 @@ use std::ptr; use objc2::declare::{Ivar, IvarDrop}; -use objc2::foundation::{NSArray, NSObject, NSPoint, NSRect, NSString}; +use objc2::foundation::{NSArray, NSObject, NSPoint, NSString}; use objc2::rc::{autoreleasepool, Id, Shared}; use objc2::runtime::Object; use objc2::{class, declare_class, msg_send, msg_send_id, sel, ClassType}; @@ -173,11 +173,8 @@ declare_class!( let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; - let rect = NSRect { - origin: dl, - ..Default::default() - }; - let y = util::bottom_left_to_top_left(rect); + let y = self.window.frame().size.height - dl.y; + let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); @@ -208,11 +205,8 @@ declare_class!( let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; - let rect = NSRect { - origin: dl, - ..Default::default() - }; - let y = util::bottom_left_to_top_left(rect); + let y = self.window.frame().size.height - dl.y; + let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); From f8b12661502c48e18e2c1bc836de271aacef9a48 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 8 Feb 2023 15:18:07 +0200 Subject: [PATCH 10/16] begining of new events, windows implementation --- CHANGELOG.md | 15 +++--- examples/dnd.rs | 28 ++++++++++ examples/window_icon.rs | 8 +-- src/event.rs | 65 +++++++++++------------ src/platform_impl/windows/drop_handler.rs | 63 +++++++++++++--------- 5 files changed, 106 insertions(+), 73 deletions(-) create mode 100644 examples/dnd.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index bcea7e225b..21254f7514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased -- **Breaking:** Add cursor position to `WindowEvent::DroppedFile` and `WindowEvent::HoveredFile` events. +- Add `WindowEvent::DragEnter`, `WindowEvent::DragOver`, `WindowEvent::DragDrop` and `WindowEvent::DragLeave` events. +- **Breaking:** Removed `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled` events. - Add `Window::is_minimized`. - On X11, fix errors handled during `register_xlib_error_hook` invocation bleeding into winit. - Add `Window::has_focus`. @@ -301,7 +302,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On macOS, fix inverted horizontal scroll. - **Breaking:** `current_monitor` now returns `Option`. - **Breaking:** `primary_monitor` now returns `Option`. -- On macOS, updated core-* dependencies and cocoa. +- On macOS, updated core-\* dependencies and cocoa. - Bump `parking_lot` to 0.11 - On Android, bump `ndk`, `ndk-sys` and `ndk-glue` to 0.2. Checkout the new ndk-glue main proc attribute. - On iOS, fixed starting the app in landscape where the view still had portrait dimensions. @@ -343,7 +344,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Windows, fix `WindowBuilder::with_maximized` being ignored. - On Android, minimal platform support. - On iOS, touch positions are now properly converted to physical pixels. -- On macOS, updated core-* dependencies and cocoa +- On macOS, updated core-\* dependencies and cocoa # 0.22.1 (2020-04-16) @@ -530,7 +531,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On X11, non-resizable windows now have maximize explicitly disabled. - On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile` -and `WindowEvent::HoveredFile`. + and `WindowEvent::HoveredFile`. - On Mac, implement `DeviceEvent::Button`. - Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`. - On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor. @@ -872,20 +873,20 @@ _Yanked_ - Added event `WindowEvent::HiDPIFactorChanged`. - Added method `MonitorId::get_hidpi_factor`. - Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of -`get_inner_size`. + `get_inner_size`. - **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints, but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`. - `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`. - Rewrite of the wayland backend to use wayland-client-0.11 - Support for dead keys on wayland for keyboard utf8 input - Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of -`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`. + `EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`. - On Windows added `MonitorIdExt::hmonitor` method - Impl `Clone` for `EventsLoopProxy` - `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found - Support for touch event on wayland - `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to -`CursorMoved`, `CursorEntered`, and `CursorLeft`. + `CursorMoved`, `CursorEntered`, and `CursorLeft`. - New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`. - Send `CursorMoved` event after `CursorEntered` and `Focused` events. - Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend. diff --git a/examples/dnd.rs b/examples/dnd.rs new file mode 100644 index 0000000000..4d6cdd92e3 --- /dev/null +++ b/examples/dnd.rs @@ -0,0 +1,28 @@ +use simple_logger::SimpleLogger; +use winit::{event::Event, event_loop::EventLoop, window::WindowBuilder}; + +fn main() { + SimpleLogger::new().init().unwrap(); + + let event_loop = EventLoop::new(); + + let _window = WindowBuilder::new() + .with_title("An iconic window!") + .build(&event_loop) + .unwrap(); + + event_loop.run(move |event, _, control_flow| { + control_flow.set_wait(); + + if let Event::WindowEvent { event, .. } = event { + use winit::event::WindowEvent::*; + match event { + CloseRequested => control_flow.set_exit(), + DragEnter { .. } | DragOver { .. } | DragDrop { .. } | DragLeave => { + println!("{:?}", event); + } + _ => (), + } + } + }); +} diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 187e3d7760..6401db116e 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -37,12 +37,8 @@ fn main() { use winit::event::WindowEvent::*; match event { CloseRequested => control_flow.set_exit(), - HoveredFile { position, .. } => { - dbg!("Hovered", position); - } - DroppedFile { path, position } => { - dbg!("Dropped", position); - window.set_window_icon(Some(load_icon(&path))); + DragDrop { paths, .. } => { + window.set_window_icon(Some(load_icon(&paths[0]))); } _ => (), } diff --git a/src/event.rs b/src/event.rs index 95939d1adc..9c97a50631 100644 --- a/src/event.rs +++ b/src/event.rs @@ -342,31 +342,18 @@ pub enum WindowEvent<'a> { /// The window has been destroyed. Destroyed, - /// A file has been dropped into the window. - /// - /// When the user drops multiple files at once, this event will be emitted for each file - /// separately. - DroppedFile { - path: PathBuf, - /// The position of the mouse cursor, similar to [`WindowEvent::CursorMoved`]. + DragEnter { + paths: Vec, position: PhysicalPosition, }, - - /// A file is being hovered over the window. - /// - /// When the user hovers multiple files at once, this event will be emitted for each file - /// separately. - HoveredFile { - path: PathBuf, - /// The position of the mouse cursor, similar to [`WindowEvent::CursorMoved`]. + DragOver { position: PhysicalPosition, }, - - /// A file was hovered, but has exited the window. - /// - /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were - /// hovered. - HoveredFileCancelled, + DragDrop { + paths: Vec, + position: PhysicalPosition, + }, + DragLeave, /// The window received a unicode character. /// @@ -424,10 +411,14 @@ pub enum WindowEvent<'a> { }, /// The cursor has entered the window. - CursorEntered { device_id: DeviceId }, + CursorEntered { + device_id: DeviceId, + }, /// The cursor has left the window. - CursorLeft { device_id: DeviceId }, + CursorLeft { + device_id: DeviceId, + }, /// A mouse wheel movement or touchpad scroll occurred. MouseWheel { @@ -478,7 +469,9 @@ pub enum WindowEvent<'a> { /// ## Platform-specific /// /// - Only available on **macOS 10.8** and later. - SmartMagnify { device_id: DeviceId }, + SmartMagnify { + device_id: DeviceId, + }, /// Touchpad rotation event with two-finger rotation gesture. /// @@ -565,15 +558,18 @@ impl Clone for WindowEvent<'static> { Moved(pos) => Moved(*pos), CloseRequested => CloseRequested, Destroyed => Destroyed, - DroppedFile { path, position } => DroppedFile { - path: path.clone(), - position: *position, + DragEnter { paths, position } => DragEnter { + paths: paths.clone(), + position: position.clone(), }, - HoveredFile { path, position } => HoveredFile { - path: path.clone(), - position: *position, + DragOver { position } => DragOver { + position: position.clone(), + }, + DragDrop { paths, position } => DragDrop { + paths: paths.clone(), + position: position.clone(), }, - HoveredFileCancelled => HoveredFileCancelled, + DragLeave => DragLeave, ReceivedCharacter(c) => ReceivedCharacter(*c), Focused(f) => Focused(*f), KeyboardInput { @@ -684,9 +680,10 @@ impl<'a> WindowEvent<'a> { Moved(position) => Some(Moved(position)), CloseRequested => Some(CloseRequested), Destroyed => Some(Destroyed), - DroppedFile { path, position } => Some(DroppedFile { path, position }), - HoveredFile { path, position } => Some(HoveredFile { path, position }), - HoveredFileCancelled => Some(HoveredFileCancelled), + DragEnter { paths, position } => Some(DragEnter { paths, position }), + DragOver { position } => Some(DragOver { position }), + DragDrop { paths, position } => Some(DragDrop { paths, position }), + DragLeave => Some(DragLeave), ReceivedCharacter(c) => Some(ReceivedCharacter(c)), Focused(focused) => Some(Focused(focused)), KeyboardInput { diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index d6af8873a8..1adef6b280 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -36,7 +36,7 @@ pub struct FileDropHandlerData { window: HWND, send_event: Box)>, cursor_effect: u32, - hovered_is_valid: bool, /* If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted */ + enter_is_valid: bool, /* If the currently hovered item is not valid there must not be any `DragLeave` emitted */ } pub struct FileDropHandler { @@ -54,7 +54,7 @@ impl FileDropHandler { window, send_event, cursor_effect: DROPEFFECT_NONE, - hovered_is_valid: false, + enter_is_valid: false, }); FileDropHandler { data: Box::into_raw(data), @@ -95,19 +95,19 @@ impl FileDropHandler { pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::HoveredFile; + use crate::event::WindowEvent::DragEnter; let drop_handler = Self::from_interface(this); let mut pt = POINT { x: pt.x, y: pt.y }; ScreenToClient(drop_handler.window, &mut pt); let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); - let hdrop = Self::iterate_filenames(pDataObj, |path| { - drop_handler.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(drop_handler.window)), - event: HoveredFile { path, position }, - }); + let mut paths = Vec::new(); + let hdrop = Self::iterate_filenames(pDataObj, |path| paths.push(path)); + drop_handler.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(drop_handler.window)), + event: DragEnter { paths, position }, }); - drop_handler.hovered_is_valid = hdrop.is_some(); - drop_handler.cursor_effect = if drop_handler.hovered_is_valid { + drop_handler.enter_is_valid = hdrop.is_some(); + drop_handler.cursor_effect = if drop_handler.enter_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE @@ -120,22 +120,31 @@ impl FileDropHandler { pub unsafe extern "system" fn DragOver( this: *mut IDropTarget, _grfKeyState: u32, - _pt: POINTL, + pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { + use crate::event::WindowEvent::DragOver; let drop_handler = Self::from_interface(this); + if drop_handler.enter_is_valid { + let mut pt = POINT { x: pt.x, y: pt.y }; + ScreenToClient(drop_handler.window, &mut pt); + let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); + drop_handler.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(drop_handler.window)), + event: DragOver { position }, + }); + } *pdwEffect = drop_handler.cursor_effect; - S_OK } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { - use crate::event::WindowEvent::HoveredFileCancelled; + use crate::event::WindowEvent::DragLeave; let drop_handler = Self::from_interface(this); - if drop_handler.hovered_is_valid { + if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: HoveredFileCancelled, + event: DragLeave, }); } @@ -149,19 +158,21 @@ impl FileDropHandler { pt: POINTL, _pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DroppedFile; + use crate::event::WindowEvent::DragDrop; let drop_handler = Self::from_interface(this); - let mut pt = POINT { x: pt.x, y: pt.y }; - ScreenToClient(drop_handler.window, &mut pt); - let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); - let hdrop = Self::iterate_filenames(pDataObj, |path| { + if drop_handler.enter_is_valid { + let mut pt = POINT { x: pt.x, y: pt.y }; + ScreenToClient(drop_handler.window, &mut pt); + let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); + let mut paths = Vec::new(); + let hdrop = Self::iterate_filenames(pDataObj, |path| paths.push(path)); drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: DroppedFile { path, position }, + event: DragDrop { paths, position }, }); - }); - if let Some(hdrop) = hdrop { - DragFinish(hdrop); + if let Some(hdrop) = hdrop { + DragFinish(hdrop); + } } S_OK @@ -171,9 +182,9 @@ impl FileDropHandler { &mut *(this as *mut _) } - unsafe fn iterate_filenames(data_obj: *const IDataObject, callback: F) -> Option + unsafe fn iterate_filenames(data_obj: *const IDataObject, mut callback: F) -> Option where - F: Fn(PathBuf), + F: FnMut(PathBuf), { let drop_format = FORMATETC { cfFormat: CF_HDROP, From 15662145fcddf658372e254a9f994513a1248a75 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 8 Feb 2023 16:33:52 +0200 Subject: [PATCH 11/16] macos implementation --- src/platform_impl/macos/window_delegate.rs | 43 +++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index c22e50ea93..27fcf4905c 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -178,10 +178,33 @@ declare_class!( let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); - filenames.into_iter().for_each(|file| { - let path = PathBuf::from(file.to_string()); - self.queue_event(WindowEvent::HoveredFile { path, position }); - }); + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); + + self.queue_event(WindowEvent::DragEnter { paths, position }); + + true + } + + #[sel(wantsPeriodicDraggingUpdates)] + fn wants_periodic_dragging_updates(&self) -> bool { + trace_scope!("wantsPeriodicDraggingUpdates:"); + true + } + + /// Invoked periodically as the image is held within the destination area, allowing modification of the dragging operation or mouse-pointer position. + #[sel(draggingUpdated:)] + fn dragging_updated(&self, sender: *mut Object) -> bool { + trace_scope!("draggingUpdated:"); + let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let y = self.window.frame().size.height - dl.y; + + let scale_factor = self.window.scale_factor(); + let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); + + self.queue_event(WindowEvent::DragOver { position }); true } @@ -210,10 +233,12 @@ declare_class!( let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); - filenames.into_iter().for_each(|file| { - let path = PathBuf::from(file.to_string()); - self.queue_event(WindowEvent::DroppedFile { path, position }); - }); + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); + + self.queue_event(WindowEvent::DragDrop { paths, position }); true } @@ -228,7 +253,7 @@ declare_class!( #[sel(draggingExited:)] fn dragging_exited(&self, _: Option<&Object>) { trace_scope!("draggingExited:"); - self.queue_event(WindowEvent::HoveredFileCancelled); + self.queue_event(WindowEvent::DragLeave); } /// Invoked when before enter fullscreen From d23a3de426e1ccba6bb9383b3a0fad26cc37dc2d Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 8 Feb 2023 16:36:33 +0200 Subject: [PATCH 12/16] x11 implementation --- .../linux/x11/event_processor.rs | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index c23ffc24f0..cb06823d6a 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -215,12 +215,11 @@ impl EventProcessor { } } else if client_msg.message_type == self.dnd.atoms.position { // This event occurs every time the mouse moves while a file's being dragged - // over our window. We emit HoveredFile in response; while the macOS backend + // over our window. We emit DragEnter in response; while the macOS backend // does that upon a drag entering, XDND doesn't have access to the actual drop // data until this event. For parity with other platforms, we only emit - // `HoveredFile` the first time, though if winit's API is later extended to - // supply position updates with `HoveredFile` or another event, implementing - // that here would be trivial. + // `DragEnter` the first time, then we emit `DragOver` for later events to + // match other backends. let source_window = client_msg.data.get_long(0) as c_ulong; @@ -249,16 +248,14 @@ impl EventProcessor { if accepted { self.dnd.source_window = Some(source_window); unsafe { - if self.dnd.result.is_none() { - let time = if version >= 1 { - client_msg.data.get_long(3) as c_ulong - } else { - // In version 0, time isn't specified - ffi::CurrentTime - }; - // This results in the `SelectionNotify` event below - self.dnd.convert_selection(window, time); - } + let time = if version >= 1 { + client_msg.data.get_long(3) as c_ulong + } else { + // In version 0, time isn't specified + ffi::CurrentTime + }; + // This results in the `SelectionNotify` event below + self.dnd.convert_selection(window, time); self.dnd .send_status(window, source_window, DndState::Accepted) .expect("Failed to send `XdndStatus` message."); @@ -288,15 +285,13 @@ impl EventProcessor { let position = PhysicalPosition::new(coords.x_rel as f64, coords.y_rel as f64); - for path in path_list { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::DroppedFile { - path: path.clone(), - position, - }, - }); - } + callback(Event::WindowEvent { + window_id, + event: WindowEvent::DragDrop { + paths: path_list.iter().map(Into::into).collect(), + position, + }, + }); } (source_window, DndState::Accepted) } else { @@ -315,7 +310,7 @@ impl EventProcessor { self.dnd.reset(); callback(Event::WindowEvent { window_id, - event: WindowEvent::HoveredFileCancelled, + event: WindowEvent::DragLeave, }); } } @@ -348,20 +343,28 @@ impl EventProcessor { let position = PhysicalPosition::new(coords.x_rel as f64, coords.y_rel as f64); - for path in path_list { + if self.dnd.result.is_none() { callback(Event::WindowEvent { window_id, - event: WindowEvent::HoveredFile { - path: path.clone(), + event: WindowEvent::DragEnter { + paths: path_list.iter().map(Into::into).collect(), position, }, }); + } else { + callback(Event::WindowEvent { + window_id, + event: WindowEvent::DragOver { position }, + }); } } + result = Some(parse_result); } - self.dnd.result = result; + if self.dnd.result.is_none() { + self.dnd.result = result; + } } } From 5e33749ce91e6742cb2270fea4f2760284d85349 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Sat, 11 Feb 2023 13:29:11 +0200 Subject: [PATCH 13/16] emit paths on `DragOver` --- examples/dnd.rs | 2 +- src/event.rs | 6 ++-- src/platform_impl/linux/x11/dnd.rs | 4 +++ .../linux/x11/event_processor.rs | 16 +++++----- src/platform_impl/macos/window_delegate.rs | 31 ++++++++++++------- src/platform_impl/windows/drop_handler.rs | 15 +++++++-- 6 files changed, 49 insertions(+), 25 deletions(-) diff --git a/examples/dnd.rs b/examples/dnd.rs index 4d6cdd92e3..47a667b68e 100644 --- a/examples/dnd.rs +++ b/examples/dnd.rs @@ -7,7 +7,7 @@ fn main() { let event_loop = EventLoop::new(); let _window = WindowBuilder::new() - .with_title("An iconic window!") + .with_title("Drop files on me!!") .build(&event_loop) .unwrap(); diff --git a/src/event.rs b/src/event.rs index 9c97a50631..5d2478a69a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -347,6 +347,7 @@ pub enum WindowEvent<'a> { position: PhysicalPosition, }, DragOver { + paths: Vec, position: PhysicalPosition, }, DragDrop { @@ -562,7 +563,8 @@ impl Clone for WindowEvent<'static> { paths: paths.clone(), position: position.clone(), }, - DragOver { position } => DragOver { + DragOver { paths, position } => DragOver { + paths: paths.clone(), position: position.clone(), }, DragDrop { paths, position } => DragDrop { @@ -681,7 +683,7 @@ impl<'a> WindowEvent<'a> { CloseRequested => Some(CloseRequested), Destroyed => Some(Destroyed), DragEnter { paths, position } => Some(DragEnter { paths, position }), - DragOver { position } => Some(DragOver { position }), + DragOver { paths, position } => Some(DragOver { paths, position }), DragDrop { paths, position } => Some(DragDrop { paths, position }), DragLeave => Some(DragLeave), ReceivedCharacter(c) => Some(ReceivedCharacter(c)), diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index de16665c98..f815d6b2d3 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -96,6 +96,8 @@ pub(crate) struct Dnd { pub position: (c_long, c_long), // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option, DndDataParseError>>, + // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) + pub has_entered: bool, } impl Dnd { @@ -109,6 +111,7 @@ impl Dnd { source_window: None, position: (0, 0), result: None, + has_entered: false, }) } @@ -117,6 +120,7 @@ impl Dnd { self.type_list = None; self.source_window = None; self.result = None; + self.has_entered = false; } pub unsafe fn send_status( diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index cb06823d6a..543c082442 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -343,18 +343,18 @@ impl EventProcessor { let position = PhysicalPosition::new(coords.x_rel as f64, coords.y_rel as f64); - if self.dnd.result.is_none() { + let paths = path_list.iter().map(Into::into).collect(); + + if self.dnd.has_entered { callback(Event::WindowEvent { window_id, - event: WindowEvent::DragEnter { - paths: path_list.iter().map(Into::into).collect(), - position, - }, + event: WindowEvent::DragOver { paths, position }, }); } else { + self.dnd.has_entered = true; callback(Event::WindowEvent { window_id, - event: WindowEvent::DragOver { position }, + event: WindowEvent::DragEnter { paths, position }, }); } } @@ -362,9 +362,7 @@ impl EventProcessor { result = Some(parse_result); } - if self.dnd.result.is_none() { - self.dnd.result = result; - } + self.dnd.result = result; } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 27fcf4905c..1ad8d99c75 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -171,6 +171,10 @@ declare_class!( let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; let y = self.window.frame().size.height - dl.y; @@ -178,11 +182,6 @@ declare_class!( let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); - let paths = filenames - .into_iter() - .map(|file| PathBuf::from(file.to_string())) - .collect(); - self.queue_event(WindowEvent::DragEnter { paths, position }); true @@ -198,13 +197,24 @@ declare_class!( #[sel(draggingUpdated:)] fn dragging_updated(&self, sender: *mut Object) -> bool { trace_scope!("draggingUpdated:"); + + use std::path::PathBuf; + + let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; + let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); + let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); + let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; let y = self.window.frame().size.height - dl.y; let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); - self.queue_event(WindowEvent::DragOver { position }); + self.queue_event(WindowEvent::DragOver { paths, position }); true } @@ -226,6 +236,10 @@ declare_class!( let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; let y = self.window.frame().size.height - dl.y; @@ -233,11 +247,6 @@ declare_class!( let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); - let paths = filenames - .into_iter() - .map(|file| PathBuf::from(file.to_string())) - .collect(); - self.queue_event(WindowEvent::DragDrop { paths, position }); true diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 1adef6b280..b91a39f6bb 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -37,6 +37,7 @@ pub struct FileDropHandlerData { send_event: Box)>, cursor_effect: u32, enter_is_valid: bool, /* If the currently hovered item is not valid there must not be any `DragLeave` emitted */ + paths: Option>, } pub struct FileDropHandler { @@ -55,6 +56,7 @@ impl FileDropHandler { send_event, cursor_effect: DROPEFFECT_NONE, enter_is_valid: false, + paths: None, }); FileDropHandler { data: Box::into_raw(data), @@ -104,8 +106,12 @@ impl FileDropHandler { let hdrop = Self::iterate_filenames(pDataObj, |path| paths.push(path)); drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: DragEnter { paths, position }, + event: DragEnter { + paths: paths.clone(), + position, + }, }); + drop_handler.paths = Some(paths); drop_handler.enter_is_valid = hdrop.is_some(); drop_handler.cursor_effect = if drop_handler.enter_is_valid { DROPEFFECT_COPY @@ -131,7 +137,10 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: DragOver { position }, + event: DragOver { + position, + paths: drop_handler.paths.clone().unwrap(), + }, }); } *pdwEffect = drop_handler.cursor_effect; @@ -147,6 +156,7 @@ impl FileDropHandler { event: DragLeave, }); } + drop_handler.paths = None; S_OK } @@ -174,6 +184,7 @@ impl FileDropHandler { DragFinish(hdrop); } } + drop_handler.paths = None; S_OK } From bc8e24f68ac59b2ba7df4d647cde7a3aab3450cd Mon Sep 17 00:00:00 2001 From: amrbashir Date: Sat, 11 Feb 2023 13:31:20 +0200 Subject: [PATCH 14/16] clippy --- src/event.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/event.rs b/src/event.rs index 5d2478a69a..cb2c8e528e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -561,15 +561,15 @@ impl Clone for WindowEvent<'static> { Destroyed => Destroyed, DragEnter { paths, position } => DragEnter { paths: paths.clone(), - position: position.clone(), + position: *position, }, DragOver { paths, position } => DragOver { paths: paths.clone(), - position: position.clone(), + position: *position, }, DragDrop { paths, position } => DragDrop { paths: paths.clone(), - position: position.clone(), + position: *position, }, DragLeave => DragLeave, ReceivedCharacter(c) => ReceivedCharacter(*c), From 33a30e32998c7ceb53fb5984f86063c85f2a94f6 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 20 Feb 2023 14:54:02 +0200 Subject: [PATCH 15/16] remove paths from `DragOver`, add docs --- src/event.rs | 27 ++++++++++--------- .../linux/x11/event_processor.rs | 5 ++-- src/platform_impl/macos/window_delegate.rs | 12 +-------- src/platform_impl/windows/drop_handler.rs | 10 +------ 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/event.rs b/src/event.rs index cb2c8e528e..9cc774e6a7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -342,18 +342,26 @@ pub enum WindowEvent<'a> { /// The window has been destroyed. Destroyed, + /// A drag operation has entered the window. DragEnter { + /// List of paths that are being dragged onto the window. paths: Vec, + /// Position of the drag operation. position: PhysicalPosition, }, + /// A drag operation is moving over the window. DragOver { - paths: Vec, + /// Position of the drag operation. position: PhysicalPosition, }, + /// The drag operation has dropped file(s) on the window. DragDrop { + /// List of paths that are being dragged onto the window. paths: Vec, + /// Position of the drag operation. position: PhysicalPosition, }, + /// The drag operation has been cancelled or left the window. DragLeave, /// The window received a unicode character. @@ -412,14 +420,10 @@ pub enum WindowEvent<'a> { }, /// The cursor has entered the window. - CursorEntered { - device_id: DeviceId, - }, + CursorEntered { device_id: DeviceId }, /// The cursor has left the window. - CursorLeft { - device_id: DeviceId, - }, + CursorLeft { device_id: DeviceId }, /// A mouse wheel movement or touchpad scroll occurred. MouseWheel { @@ -470,9 +474,7 @@ pub enum WindowEvent<'a> { /// ## Platform-specific /// /// - Only available on **macOS 10.8** and later. - SmartMagnify { - device_id: DeviceId, - }, + SmartMagnify { device_id: DeviceId }, /// Touchpad rotation event with two-finger rotation gesture. /// @@ -563,8 +565,7 @@ impl Clone for WindowEvent<'static> { paths: paths.clone(), position: *position, }, - DragOver { paths, position } => DragOver { - paths: paths.clone(), + DragOver { position } => DragOver { position: *position, }, DragDrop { paths, position } => DragDrop { @@ -683,7 +684,7 @@ impl<'a> WindowEvent<'a> { CloseRequested => Some(CloseRequested), Destroyed => Some(Destroyed), DragEnter { paths, position } => Some(DragEnter { paths, position }), - DragOver { paths, position } => Some(DragOver { paths, position }), + DragOver { position } => Some(DragOver { position }), DragDrop { paths, position } => Some(DragDrop { paths, position }), DragLeave => Some(DragLeave), ReceivedCharacter(c) => Some(ReceivedCharacter(c)), diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index e543e59d94..c09e42708f 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -343,14 +343,13 @@ impl EventProcessor { let position = PhysicalPosition::new(coords.x_rel as f64, coords.y_rel as f64); - let paths = path_list.iter().map(Into::into).collect(); - if self.dnd.has_entered { callback(Event::WindowEvent { window_id, - event: WindowEvent::DragOver { paths, position }, + event: WindowEvent::DragOver { position }, }); } else { + let paths = path_list.iter().map(Into::into).collect(); self.dnd.has_entered = true; callback(Event::WindowEvent { window_id, diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 3d81aab0aa..98a8543e44 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -198,23 +198,13 @@ declare_class!( fn dragging_updated(&self, sender: *mut Object) -> bool { trace_scope!("draggingUpdated:"); - use std::path::PathBuf; - - let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); - let filenames: Id, Shared> = unsafe { Id::cast(filenames) }; - let paths = filenames - .into_iter() - .map(|file| PathBuf::from(file.to_string())) - .collect(); - let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; let y = self.window.frame().size.height - dl.y; let scale_factor = self.window.scale_factor(); let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); - self.queue_event(WindowEvent::DragOver { paths, position }); + self.queue_event(WindowEvent::DragOver { position }); true } diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index b91a39f6bb..cc35e1abac 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -37,7 +37,6 @@ pub struct FileDropHandlerData { send_event: Box)>, cursor_effect: u32, enter_is_valid: bool, /* If the currently hovered item is not valid there must not be any `DragLeave` emitted */ - paths: Option>, } pub struct FileDropHandler { @@ -56,7 +55,6 @@ impl FileDropHandler { send_event, cursor_effect: DROPEFFECT_NONE, enter_is_valid: false, - paths: None, }); FileDropHandler { data: Box::into_raw(data), @@ -111,7 +109,6 @@ impl FileDropHandler { position, }, }); - drop_handler.paths = Some(paths); drop_handler.enter_is_valid = hdrop.is_some(); drop_handler.cursor_effect = if drop_handler.enter_is_valid { DROPEFFECT_COPY @@ -137,10 +134,7 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: DragOver { - position, - paths: drop_handler.paths.clone().unwrap(), - }, + event: DragOver { position }, }); } *pdwEffect = drop_handler.cursor_effect; @@ -156,7 +150,6 @@ impl FileDropHandler { event: DragLeave, }); } - drop_handler.paths = None; S_OK } @@ -184,7 +177,6 @@ impl FileDropHandler { DragFinish(hdrop); } } - drop_handler.paths = None; S_OK } From 96593ab07da56564958520c0b003d328706bde8e Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 20 Feb 2023 15:08:37 +0200 Subject: [PATCH 16/16] clippy --- src/platform_impl/windows/drop_handler.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index cc35e1abac..1adef6b280 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -104,10 +104,7 @@ impl FileDropHandler { let hdrop = Self::iterate_filenames(pDataObj, |path| paths.push(path)); drop_handler.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(drop_handler.window)), - event: DragEnter { - paths: paths.clone(), - position, - }, + event: DragEnter { paths, position }, }); drop_handler.enter_is_valid = hdrop.is_some(); drop_handler.cursor_effect = if drop_handler.enter_is_valid {