From 2594b83e7f7a5818fabec10ae8622e3dcdd9cfa4 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 8 Jan 2025 10:19:50 -0500 Subject: [PATCH 01/24] feat: DnD changes so far (rebased) (Original work at #2615; rebased by @valadaptive) --- examples/window.rs | 7 +- src/changelog/unreleased.md | 2 + src/event.rs | 56 +++++---- .../apple/appkit/window_delegate.rs | 54 ++++++-- src/platform_impl/linux/x11/dnd.rs | 43 ++++--- .../linux/x11/event_processor.rs | 119 ++++++++++++------ src/platform_impl/linux/x11/util/geometry.rs | 17 ++- src/platform_impl/linux/x11/window.rs | 2 +- src/platform_impl/windows/definitions.rs | 6 +- src/platform_impl/windows/drop_handler.rs | 90 +++++++------ 10 files changed, 259 insertions(+), 137 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index 3d87e2f8d9..5457272170 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -541,11 +541,12 @@ impl ApplicationHandler for Application { info!("Smart zoom"); }, WindowEvent::TouchpadPressure { .. } - | WindowEvent::HoveredFileCancelled + | WindowEvent::DragLeave | WindowEvent::KeyboardInput { .. } | WindowEvent::PointerEntered { .. } - | WindowEvent::DroppedFile(_) - | WindowEvent::HoveredFile(_) + | WindowEvent::DragEnter { .. } + | WindowEvent::DragOver { .. } + | WindowEvent::DragDrop { .. } | WindowEvent::Destroyed | WindowEvent::Moved(_) => (), } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 6a9c4b08dc..abd0e97381 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -73,6 +73,7 @@ changelog entry. - Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`. - `ActivationToken::from_raw` and `ActivationToken::into_raw`. - On X11, add a workaround for disabling IME on GNOME. +- Add `WindowEvent::DragEnter`, `WindowEvent::DragOver`, `WindowEvent::DragDrop` and `WindowEvent::DragLeave` events. ### Changed @@ -197,6 +198,7 @@ changelog entry. `ButtonSource` as part of the new pointer event overhaul. - Remove `Force::altitude_angle`. - Removed `Window::inner_position`, use the new `Window::surface_position` instead. +- Removed `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled` events. ### Fixed diff --git a/src/event.rs b/src/event.rs index e7af7d65cd..a02fa69a5e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -175,28 +175,27 @@ pub enum WindowEvent { /// The window has been destroyed. Destroyed, - /// 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), - - /// 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. - /// - /// The support for this is known to be incomplete, see [#720] for more - /// information. - /// - /// [#720]: https://github.com/rust-windowing/winit/issues/720 - DroppedFile(PathBuf), - - /// A file was hovered, but has exited the window. - /// - /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were - /// hovered. - HoveredFileCancelled, + /// 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 { + /// 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 gained or lost focus. /// @@ -1221,9 +1220,16 @@ mod tests { with_window_event(Focused(true)); with_window_event(Moved((0, 0).into())); with_window_event(SurfaceResized((0, 0).into())); - with_window_event(DroppedFile("x.txt".into())); - with_window_event(HoveredFile("x.txt".into())); - with_window_event(HoveredFileCancelled); + with_window_event(DragEnter { + paths: vec!["x.txt".into()], + position: (0, 0).into(), + }); + with_window_event(DragOver { position: (0, 0).into() }); + with_window_event(DragDrop { + paths: vec!["x.txt".into()], + position: (0, 0).into(), + }); + with_window_event(DragLeave); with_window_event(Ime(Enabled)); with_window_event(PointerMoved { device_id: None, diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 8bb04f4337..5c067bf9a5 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -383,11 +383,40 @@ declare_class!( let pb: Retained = unsafe { msg_send_id![sender, draggingPasteboard] }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Retained> = unsafe { Retained::cast(filenames) }; + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); - filenames.into_iter().for_each(|file| { - let path = PathBuf::from(file.to_string()); - self.queue_event(WindowEvent::HoveredFile(path)); - }); + 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::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 } @@ -409,11 +438,18 @@ declare_class!( let pb: Retained = unsafe { msg_send_id![sender, draggingPasteboard] }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Retained> = unsafe { Retained::cast(filenames) }; + let paths = filenames + .into_iter() + .map(|file| PathBuf::from(file.to_string())) + .collect(); - filenames.into_iter().for_each(|file| { - let path = PathBuf::from(file.to_string()); - self.queue_event(WindowEvent::DroppedFile(path)); - }); + 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::DragDrop { paths, position }); true } @@ -428,7 +464,7 @@ declare_class!( #[method(draggingExited:)] fn dragging_exited(&self, _sender: Option<&NSObject>) { trace_scope!("draggingExited:"); - self.queue_event(WindowEvent::HoveredFileCancelled); + self.queue_event(WindowEvent::DragLeave); } } diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 691e40af9b..2c6d929831 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -45,13 +45,25 @@ pub struct Dnd { pub type_list: Option>, // Populated by XdndPosition event handler pub source_window: Option, + // Populated by XdndPosition event handler + 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 { pub fn new(xconn: Arc) -> Result { - Ok(Dnd { xconn, version: None, type_list: None, source_window: None, result: None }) + Ok(Dnd { + xconn, + version: None, + type_list: None, + source_window: None, + position: (0, 0), + result: None, + has_entered: false, + }) } pub fn reset(&mut self) { @@ -59,6 +71,7 @@ impl Dnd { self.type_list = None; self.source_window = None; self.result = None; + self.has_entered = false; } pub unsafe fn send_status( @@ -73,13 +86,13 @@ impl Dnd { DndState::Rejected => (0, atoms[DndNone]), }; self.xconn - .send_client_msg(target_window, target_window, atoms[XdndStatus] as _, None, [ - this_window, - accepted, - 0, - 0, - action as _, - ])? + .send_client_msg( + target_window, + target_window, + atoms[XdndStatus] as _, + None, + [this_window, accepted, 0, 0, action as _], + )? .ignore_error(); Ok(()) @@ -97,13 +110,13 @@ impl Dnd { DndState::Rejected => (0, atoms[DndNone]), }; self.xconn - .send_client_msg(target_window, target_window, atoms[XdndFinished] as _, None, [ - this_window, - accepted, - action as _, - 0, - 0, - ])? + .send_client_msg( + target_window, + target_window, + atoms[XdndFinished] as _, + None, + [this_window, accepted, action as _, 0, 0], + )? .ignore_error(); Ok(()) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index dbe82935da..775e9bbe80 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,7 +1,7 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; use std::os::raw::{c_char, c_int, c_long, c_ulong}; -use std::slice; +use std::{mem, slice}; use std::sync::{Arc, Mutex}; use x11_dl::xinput2::{ @@ -474,10 +474,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 = xev.data.get_long(2); - // let shift = mem::size_of::() * 8; - // let x = packed_coordinates >> shift; - // let y = packed_coordinates & !(x << shift); + let packed_coordinates = xev.data.get_long(2); + let shift = 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); @@ -502,21 +503,19 @@ impl EventProcessor { } self.dnd.source_window = Some(source_window); - if self.dnd.result.is_none() { - let time = if version >= 1 { - xev.data.get_long(3) as xproto::Timestamp - } else { - // In version 0, time isn't specified - x11rb::CURRENT_TIME - }; + let time = if version >= 1 { + xev.data.get_long(3) as xproto::Timestamp + } else { + // In version 0, time isn't specified + x11rb::CURRENT_TIME + }; - // Log this timestamp. - self.target.xconn.set_timestamp(time); + // Log this timestamp. + self.target.xconn.set_timestamp(time); - // This results in the `SelectionNotify` event below - unsafe { - self.dnd.convert_selection(window, time); - } + // This results in the `SelectionNotify` event below + unsafe { + self.dnd.convert_selection(window, time); } unsafe { @@ -530,13 +529,29 @@ impl EventProcessor { if xev.message_type == atoms[XdndDrop] as c_ulong { let (source_window, state) = if let Some(source_window) = self.dnd.source_window { if let Some(Ok(ref path_list)) = self.dnd.result { - for path in path_list { - let event = Event::WindowEvent { + let coords = self + .target + .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.dst_x as f64, coords.dst_y as f64); + + callback( + &self.target, + Event::WindowEvent { window_id, - event: WindowEvent::DroppedFile(path.clone()), - }; - callback(&self.target, event); - } + event: WindowEvent::DragDrop { + paths: path_list.iter().map(Into::into).collect(), + position, + }, + }, + ); } (source_window, DndState::Accepted) } else { @@ -558,7 +573,7 @@ impl EventProcessor { if xev.message_type == atoms[XdndLeave] as c_ulong { self.dnd.reset(); - let event = Event::WindowEvent { window_id, event: WindowEvent::HoveredFileCancelled }; + let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeave }; callback(&self.target, event); } } @@ -583,15 +598,41 @@ impl EventProcessor { self.dnd.result = None; 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 { - for path in path_list { - let event = Event::WindowEvent { - window_id, - event: WindowEvent::HoveredFile(path.clone()), - }; - callback(&self.target, event); + let source_window = self.dnd.source_window.unwrap_or(self.target.root); + + let coords = self + .target + .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.dst_x as f64, coords.dst_y as f64); + + if self.dnd.has_entered { + callback( + &self.target, + Event::WindowEvent { window_id, event: WindowEvent::DragOver { position } }, + ); + } else { + let paths = path_list.iter().map(Into::into).collect(); + self.dnd.has_entered = true; + callback( + &self.target, + Event::WindowEvent { + window_id, + event: WindowEvent::DragEnter { paths, position }, + }, + ); } } + self.dnd.result = Some(parse_result); } } @@ -717,11 +758,11 @@ impl EventProcessor { let surface_size = Arc::new(Mutex::new(new_surface_size)); callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_scale_factor, + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor: new_scale_factor, surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&surface_size)), - }, + }, }); let new_surface_size = *surface_size.lock().unwrap(); @@ -773,8 +814,8 @@ impl EventProcessor { if resized { callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::SurfaceResized(new_surface_size.into()), + window_id, + event: WindowEvent::SurfaceResized(new_surface_size.into()), }); } } @@ -1549,8 +1590,8 @@ impl EventProcessor { let physical_key = xkb::raw_keycode_to_physicalkey(keycode); callback(&self.target, Event::DeviceEvent { - device_id, - event: DeviceEvent::Key(RawKeyEvent { physical_key, state }), + device_id, + event: DeviceEvent::Key(RawKeyEvent { physical_key, state }), }); } diff --git a/src/platform_impl/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs index c935d162f7..ab13b0410e 100644 --- a/src/platform_impl/linux/x11/util/geometry.rs +++ b/src/platform_impl/linux/x11/util/geometry.rs @@ -95,7 +95,7 @@ impl FrameExtentsHeuristic { impl XConnection { // This is adequate for inner_position - pub fn translate_coords( + pub fn translate_coords_root( &self, window: xproto::Window, root: xproto::Window, @@ -103,6 +103,19 @@ impl XConnection { self.xcb_connection().translate_coordinates(window, root, 0, 0)?.reply().map_err(Into::into) } + pub fn translate_coords( + &self, + src_w: xproto::Window, + dst_w: xproto::Window, + src_x: i16, + src_y: i16, + ) -> Result { + self.xcb_connection() + .translate_coordinates(src_w, dst_w, src_x, src_y)? + .reply() + .map_err(Into::into) + } + // This is adequate for surface_size pub fn get_geometry( &self, @@ -189,7 +202,7 @@ impl XConnection { // that, fullscreen windows often aren't nested. let (inner_y_rel_root, child) = { let coords = self - .translate_coords(window, root) + .translate_coords_root(window, root) .expect("Failed to translate window coordinates"); (coords.dst_y, coords.child) }; diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 9f412356ac..4fbc1df3d6 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1520,7 +1520,7 @@ 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) + .translate_coords_root(self.xwindow, self.root) .map(|coords| (coords.dst_x.into(), coords.dst_y.into())) .unwrap() } diff --git a/src/platform_impl/windows/definitions.rs b/src/platform_impl/windows/definitions.rs index c015ffc4e3..62c0cb0625 100644 --- a/src/platform_impl/windows/definitions.rs +++ b/src/platform_impl/windows/definitions.rs @@ -72,13 +72,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, @@ -86,7 +86,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 99c413bfaa..a074be4f38 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -24,8 +24,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 { @@ -41,7 +40,7 @@ impl FileDropHandler { window, send_event, cursor_effect: DROPEFFECT_NONE, - hovered_is_valid: false, + enter_is_valid: false, }); FileDropHandler { data: Box::into_raw(data) } } @@ -77,22 +76,23 @@ impl FileDropHandler { this: *mut IDropTarget, pDataObj: *const IDataObject, _grfKeyState: u32, - _pt: *const POINTL, + pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::HoveredFile; + use crate::event::WindowEvent::DragEnter; let drop_handler = unsafe { Self::from_interface(this) }; - let hdrop = unsafe { - Self::iterate_filenames(pDataObj, |filename| { - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: HoveredFile(filename), - }); - }) - }; - drop_handler.hovered_is_valid = hdrop.is_some(); + 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: DragEnter { paths, position }, + }); + drop_handler.enter_is_valid = hdrop.is_some(); drop_handler.cursor_effect = - if drop_handler.hovered_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; + if drop_handler.enter_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; unsafe { *pdwEffect = drop_handler.cursor_effect; } @@ -103,24 +103,31 @@ 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 = unsafe { Self::from_interface(this) }; - unsafe { - *pdwEffect = drop_handler.cursor_effect; + 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; - let drop_handler = unsafe { Self::from_interface(this) }; - if drop_handler.hovered_is_valid { + use crate::event::WindowEvent::DragLeave; + let drop_handler = Self::from_interface(this); + if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: HoveredFileCancelled, + window_id: RootWindowId(WindowId(drop_handler.window)), + event: DragLeave, }); } @@ -131,21 +138,24 @@ 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 = unsafe { Self::from_interface(this) }; - let hdrop = unsafe { - Self::iterate_filenames(pDataObj, |filename| { - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: DroppedFile(filename), - }); - }) - }; - if let Some(hdrop) = hdrop { - unsafe { DragFinish(hdrop) }; + use crate::event::WindowEvent::DragDrop; + 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); + 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: DragDrop { paths, position }, + }); + if let Some(hdrop) = hdrop { + DragFinish(hdrop); + } } S_OK @@ -155,9 +165,9 @@ impl FileDropHandler { unsafe { &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 1069dd899474035cf4dbd82f0e5134522b9375a1 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 12:20:51 -0500 Subject: [PATCH 02/24] fix: translate xdnd coordinates to root window Fixes GTK drag-and-drop coords being offset by (-100, -100). The relevant spec says: > data.l[2] contains the coordinates of the mouse position relative > to the root window. https://www.freedesktop.org/wiki/Specifications/XDND/#xdndposition --- src/platform_impl/linux/x11/event_processor.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 775e9bbe80..d4892315b9 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -533,7 +533,7 @@ impl EventProcessor { .target .xconn .translate_coords( - source_window, + self.target.root, window, self.dnd.position.0 as _, self.dnd.position.1 as _, @@ -600,13 +600,11 @@ impl EventProcessor { 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(self.target.root); - let coords = self .target .xconn .translate_coords( - source_window, + self.target.root, window, self.dnd.position.0 as _, self.dnd.position.1 as _, From b3510c2c20284af99a05065b3cf635b2a589c3d6 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 12:40:14 -0500 Subject: [PATCH 03/24] feat: DnD example/test --- examples/dnd.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 examples/dnd.rs diff --git a/examples/dnd.rs b/examples/dnd.rs new file mode 100644 index 0000000000..60893687ab --- /dev/null +++ b/examples/dnd.rs @@ -0,0 +1,59 @@ +use std::error::Error; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::window::{ + Window, WindowAttributes, WindowId, +}; + +#[path = "util/tracing.rs"] +mod tracing; + +fn main() -> Result<(), Box> { + tracing::init(); + + let event_loop = EventLoop::new()?; + + let app = Application::new(); + Ok(event_loop.run_app(app)?) +} + +/// Application state and event handling. +struct Application { + window: Option> +} + +impl Application { + fn new() -> Self { + Self { + window: None + } + } +} + +impl ApplicationHandler for Application { + fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { + let window_attributes = WindowAttributes::default().with_title("Drag and drop files on me!"); + self.window = Some(event_loop.create_window(window_attributes).unwrap()); + } + + fn window_event( + &mut self, + event_loop: &dyn ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + match event { + | WindowEvent::DragLeave + | WindowEvent::DragEnter { .. } + | WindowEvent::DragOver { .. } + | WindowEvent::DragDrop { .. } => { + println!("{:?}", event); + }, + WindowEvent::CloseRequested => { + event_loop.exit(); + }, + _ => {}, + } + } +} From d100b868dfde735d4e852afccb0ca4fbab85c641 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 13:19:05 -0500 Subject: [PATCH 04/24] fix: make DnD compile and function on macOS We also need to use `convertPoint_fromView` for coordinate conversion-- attempting to do it manually resulted in (0, 0) being the top-left of the window *including* decorations. --- .../apple/appkit/window_delegate.rs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 5c067bf9a5..7a41ec026c 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex}; use core_graphics::display::CGDisplay; use objc2::rc::{autoreleasepool, Retained}; use objc2::runtime::{AnyObject, ProtocolObject}; -use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass}; +use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass}; use objc2_app_kit::{ NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization, NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType, @@ -389,32 +389,29 @@ declare_class!( .collect(); let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; - let y = self.window.frame().size.height - dl.y; + let dl = self.view().convertPoint_fromView(dl, None); + let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); - let scale_factor = self.window.scale_factor(); - let position = LogicalPosition::::from((dl.x, y)).to_physical(scale_factor); self.queue_event(WindowEvent::DragEnter { paths, position }); true } - #[sel(wantsPeriodicDraggingUpdates)] + #[method(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 { + #[method(draggingUpdated:)] + fn dragging_updated(&self, sender: &NSObject) -> 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); + let dl = self.view().convertPoint_fromView(dl, None); + let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); self.queue_event(WindowEvent::DragOver { position }); @@ -444,10 +441,8 @@ declare_class!( .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); + let dl = self.view().convertPoint_fromView(dl, None); + let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); self.queue_event(WindowEvent::DragDrop { paths, position }); From 9bf7a4ec829ddc3559963e5be182368868886396 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 13:31:42 -0500 Subject: [PATCH 05/24] fix: make DnD compile and function on Windows --- src/platform_impl/windows/drop_handler.rs | 42 +++++++++++++++-------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index a074be4f38..3152139556 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -6,11 +6,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use tracing::debug; use windows_sys::core::{IUnknown, GUID, HRESULT}; -use windows_sys::Win32::Foundation::{DV_E_FORMATETC, HWND, POINTL, S_OK}; +use windows_sys::Win32::Foundation::{DV_E_FORMATETC, HWND, POINT, POINTL, S_OK}; +use windows_sys::Win32::Graphics::Gdi::ScreenToClient; use windows_sys::Win32::System::Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYMED_HGLOBAL}; use windows_sys::Win32::System::Ole::{CF_HDROP, DROPEFFECT_COPY, DROPEFFECT_NONE}; use windows_sys::Win32::UI::Shell::{DragFinish, DragQueryFileW, HDROP}; +use crate::dpi::PhysicalPosition; use crate::event::Event; use crate::platform_impl::platform::definitions::{ IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl, @@ -82,12 +84,14 @@ impl FileDropHandler { use crate::event::WindowEvent::DragEnter; let drop_handler = unsafe { Self::from_interface(this) }; let mut pt = POINT { x: pt.x, y: pt.y }; - ScreenToClient(drop_handler.window, &mut pt); + unsafe { + 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)); + let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; drop_handler.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(drop_handler.window)), + window_id: WindowId::from_raw(drop_handler.window as usize), event: DragEnter { paths, position }, }); drop_handler.enter_is_valid = hdrop.is_some(); @@ -107,26 +111,30 @@ impl FileDropHandler { pdwEffect: *mut u32, ) -> HRESULT { use crate::event::WindowEvent::DragOver; - let drop_handler = Self::from_interface(this); + let drop_handler = unsafe { 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); + unsafe { + 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)), + window_id: WindowId::from_raw(drop_handler.window as usize), event: DragOver { position }, }); } - *pdwEffect = drop_handler.cursor_effect; + unsafe { + *pdwEffect = drop_handler.cursor_effect; + } S_OK } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { use crate::event::WindowEvent::DragLeave; - let drop_handler = Self::from_interface(this); + let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(drop_handler.window)), + window_id: WindowId::from_raw(drop_handler.window as usize), event: DragLeave, }); } @@ -142,19 +150,23 @@ impl FileDropHandler { _pdwEffect: *mut u32, ) -> HRESULT { use crate::event::WindowEvent::DragDrop; - let drop_handler = Self::from_interface(this); + let drop_handler = unsafe { 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); + unsafe { + 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)); + let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; drop_handler.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(drop_handler.window)), + window_id: WindowId::from_raw(drop_handler.window as usize), event: DragDrop { paths, position }, }); if let Some(hdrop) = hdrop { - DragFinish(hdrop); + unsafe { + DragFinish(hdrop); + } } } From 7c293f5a047c65660dc5a14bb227720d139110d7 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 13:38:53 -0500 Subject: [PATCH 06/24] chore: format according to nightly rules --- examples/dnd.rs | 16 +++--- src/platform_impl/linux/x11/dnd.rs | 28 +++++----- .../linux/x11/event_processor.rs | 52 ++++++++----------- src/platform_impl/windows/drop_handler.rs | 3 +- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/examples/dnd.rs b/examples/dnd.rs index 60893687ab..5a659937c6 100644 --- a/examples/dnd.rs +++ b/examples/dnd.rs @@ -1,10 +1,9 @@ use std::error::Error; + use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::window::{ - Window, WindowAttributes, WindowId, -}; +use winit::window::{Window, WindowAttributes, WindowId}; #[path = "util/tracing.rs"] mod tracing; @@ -20,20 +19,19 @@ fn main() -> Result<(), Box> { /// Application state and event handling. struct Application { - window: Option> + window: Option>, } impl Application { fn new() -> Self { - Self { - window: None - } + Self { window: None } } } impl ApplicationHandler for Application { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let window_attributes = WindowAttributes::default().with_title("Drag and drop files on me!"); + let window_attributes = + WindowAttributes::default().with_title("Drag and drop files on me!"); self.window = Some(event_loop.create_window(window_attributes).unwrap()); } @@ -44,7 +42,7 @@ impl ApplicationHandler for Application { event: WindowEvent, ) { match event { - | WindowEvent::DragLeave + WindowEvent::DragLeave | WindowEvent::DragEnter { .. } | WindowEvent::DragOver { .. } | WindowEvent::DragDrop { .. } => { diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 2c6d929831..7f33ec5e65 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -86,13 +86,13 @@ impl Dnd { DndState::Rejected => (0, atoms[DndNone]), }; self.xconn - .send_client_msg( - target_window, - target_window, - atoms[XdndStatus] as _, - None, - [this_window, accepted, 0, 0, action as _], - )? + .send_client_msg(target_window, target_window, atoms[XdndStatus] as _, None, [ + this_window, + accepted, + 0, + 0, + action as _, + ])? .ignore_error(); Ok(()) @@ -110,13 +110,13 @@ impl Dnd { DndState::Rejected => (0, atoms[DndNone]), }; self.xconn - .send_client_msg( - target_window, - target_window, - atoms[XdndFinished] as _, - None, - [this_window, accepted, action as _, 0, 0], - )? + .send_client_msg(target_window, target_window, atoms[XdndFinished] as _, None, [ + this_window, + accepted, + action as _, + 0, + 0, + ])? .ignore_error(); Ok(()) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index d4892315b9..3335b38875 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,8 +1,8 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; use std::os::raw::{c_char, c_int, c_long, c_ulong}; -use std::{mem, slice}; use std::sync::{Arc, Mutex}; +use std::{mem, slice}; use x11_dl::xinput2::{ self, XIDeviceEvent, XIEnterEvent, XIFocusInEvent, XIFocusOutEvent, XIHierarchyEvent, @@ -542,16 +542,13 @@ impl EventProcessor { let position = PhysicalPosition::new(coords.dst_x as f64, coords.dst_y as f64); - callback( - &self.target, - Event::WindowEvent { - window_id, - event: WindowEvent::DragDrop { - paths: path_list.iter().map(Into::into).collect(), - position, - }, + callback(&self.target, Event::WindowEvent { + window_id, + event: WindowEvent::DragDrop { + paths: path_list.iter().map(Into::into).collect(), + position, }, - ); + }); } (source_window, DndState::Accepted) } else { @@ -614,20 +611,17 @@ impl EventProcessor { let position = PhysicalPosition::new(coords.dst_x as f64, coords.dst_y as f64); if self.dnd.has_entered { - callback( - &self.target, - Event::WindowEvent { window_id, event: WindowEvent::DragOver { position } }, - ); + callback(&self.target, Event::WindowEvent { + window_id, + event: WindowEvent::DragOver { position }, + }); } else { let paths = path_list.iter().map(Into::into).collect(); self.dnd.has_entered = true; - callback( - &self.target, - Event::WindowEvent { - window_id, - event: WindowEvent::DragEnter { paths, position }, - }, - ); + callback(&self.target, Event::WindowEvent { + window_id, + event: WindowEvent::DragEnter { paths, position }, + }); } } @@ -756,11 +750,11 @@ impl EventProcessor { let surface_size = Arc::new(Mutex::new(new_surface_size)); callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_scale_factor, + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor: new_scale_factor, surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&surface_size)), - }, + }, }); let new_surface_size = *surface_size.lock().unwrap(); @@ -812,8 +806,8 @@ impl EventProcessor { if resized { callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::SurfaceResized(new_surface_size.into()), + window_id, + event: WindowEvent::SurfaceResized(new_surface_size.into()), }); } } @@ -1588,8 +1582,8 @@ impl EventProcessor { let physical_key = xkb::raw_keycode_to_physicalkey(keycode); callback(&self.target, Event::DeviceEvent { - device_id, - event: DeviceEvent::Key(RawKeyEvent { physical_key, state }), + device_id, + event: DeviceEvent::Key(RawKeyEvent { physical_key, state }), }); } diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 3152139556..a97a6703bf 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -26,7 +26,8 @@ pub struct FileDropHandlerData { window: HWND, 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 */ + enter_is_valid: bool, /* If the currently hovered item is not valid there must not be any + * `DragLeave` emitted */ } pub struct FileDropHandler { From 35a972f91b8a46d006821ce1108e102f7258ed23 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 13:44:37 -0500 Subject: [PATCH 07/24] docs: expand drag event documentation --- src/event.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/event.rs b/src/event.rs index a02fa69a5e..da238d959c 100644 --- a/src/event.rs +++ b/src/event.rs @@ -179,22 +179,26 @@ pub enum WindowEvent { DragEnter { /// List of paths that are being dragged onto the window. paths: Vec, - /// Position of the drag operation. + /// Position of the drag operation. May be negative on some platforms if something is + /// dragged over a window's decorations (title bar, frame, etc). position: PhysicalPosition, }, /// A drag operation is moving over the window. DragOver { - /// Position of the drag operation. + /// Position of the drag operation. May be negative on some platforms if something is + /// dragged over a window's decorations (title bar, frame, etc). 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 of the drag operation. May be negative on some platforms if something is + /// dragged over a window's decorations (title bar, frame, etc). position: PhysicalPosition, }, - /// The drag operation has been cancelled or left the window. + /// The drag operation has been cancelled or left the window. On some platforms, this may occur + /// even if no other drag events have occurred. DragLeave, /// The window gained or lost focus. From 36438b18fb41ebf26b60a9c0184aff98f5f1fbdc Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 14:40:33 -0500 Subject: [PATCH 08/24] docs(changelog): better document DnD changes --- src/changelog/unreleased.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index abd0e97381..95b9bc64c1 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -73,7 +73,6 @@ changelog entry. - Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`. - `ActivationToken::from_raw` and `ActivationToken::into_raw`. - On X11, add a workaround for disabling IME on GNOME. -- Add `WindowEvent::DragEnter`, `WindowEvent::DragOver`, `WindowEvent::DragDrop` and `WindowEvent::DragLeave` events. ### Changed @@ -166,6 +165,25 @@ changelog entry. - Rename `VideoModeHandle` to `VideoMode`, now it only stores plain data. - Make `Fullscreen::Exclusive` contain `(MonitorHandle, VideoMode)`. - On Wayland, no longer send an explicit clearing `Ime::Preedit` just prior to a new `Ime::Preedit`. +- Reworked the file drag-and-drop API. + + The `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled` + events have been removed, and replaced with `WindowEvent::DragEnter`, `WindowEvent::DragOver`, + `WindowEvent::DragDrop` and `WindowEvent::DragLeave`. + + The old drag-and-drop events were emitted once per file. This occurred when files were *first* + hovered over the window, dropped, or left the window. The new drag-and-drop events are emitted + once per set of files dragged, and include a list of all dragged files. They also include the + pointer position. + + The rough correspondence is: + - `WindowEvent::HoveredFile` -> `WindowEvent::DragEnter` + - `WindowEvent::DroppedFile` -> `WindowEvent::DragDrop` + - `WindowEvent::HoveredFileCancelled` -> `WindowEvent::DragLeave` + + The `WindowEvent::DragOver` event is entirely new, and is emitted whenever the pointer moves + whilst files are being dragged over the window. It doesn't contain any file paths, just the + pointer position. ### Removed @@ -198,7 +216,6 @@ changelog entry. `ButtonSource` as part of the new pointer event overhaul. - Remove `Force::altitude_angle`. - Removed `Window::inner_position`, use the new `Window::surface_position` instead. -- Removed `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled` events. ### Fixed From c5e3575aaadebc976e4010665a52089c75759125 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 15:05:22 -0500 Subject: [PATCH 09/24] x11: store dnd.position as pair of i16 It's what translate_coords takes anyway, so the extra precision is misleading if we're going to cast it to i16 everywhere it's used. We can also simplify the "unpacking" from the XdndPosition message--we can and should use the value of 16 as the shift instead of size_of:: * 2 or something like that, because the specification gives us the constant 16. --- src/platform_impl/linux/x11/dnd.rs | 2 +- .../linux/x11/event_processor.rs | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 7f33ec5e65..247a0cd4b9 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -46,7 +46,7 @@ pub struct Dnd { // Populated by XdndPosition event handler pub source_window: Option, // Populated by XdndPosition event handler - pub position: (c_long, c_long), + pub position: (i16, i16), // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option, DndDataParseError>>, // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 3335b38875..ed65ade69a 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,8 +1,8 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; use std::os::raw::{c_char, c_int, c_long, c_ulong}; +use std::slice; use std::sync::{Arc, Mutex}; -use std::{mem, slice}; use x11_dl::xinput2::{ self, XIDeviceEvent, XIEnterEvent, XIFocusInEvent, XIFocusOutEvent, XIHierarchyEvent, @@ -470,14 +470,15 @@ impl EventProcessor { let source_window = xev.data.get_long(0) as xproto::Window; - // Equivalent to `(x << shift) | y` - // where `shift = mem::size_of::() * 8` + // https://www.freedesktop.org/wiki/Specifications/XDND/#xdndposition + // "data.l[2] contains the coordinates of the mouse position relative to the root + // window." + // "data.l[2] = (x << 16) | y" // Note that coordinates are in "desktop space", not "window space" // (in X11 parlance, they're root window coordinates) let packed_coordinates = xev.data.get_long(2); - let shift = mem::size_of::() * 8; - let x = packed_coordinates >> shift; - let y = packed_coordinates & !(x << shift); + let x = (packed_coordinates >> 16) as i16; + let y = (packed_coordinates & 0xffff) as i16; self.dnd.position = (x, y); // By our own state flow, `version` should never be `None` at this point. @@ -535,8 +536,8 @@ impl EventProcessor { .translate_coords( self.target.root, window, - self.dnd.position.0 as _, - self.dnd.position.1 as _, + self.dnd.position.0, + self.dnd.position.1, ) .expect("Failed to translate window coordinates"); @@ -603,8 +604,8 @@ impl EventProcessor { .translate_coords( self.target.root, window, - self.dnd.position.0 as _, - self.dnd.position.1 as _, + self.dnd.position.0, + self.dnd.position.1, ) .expect("Failed to translate window coordinates"); From a2b150bdb11ca65922ea8c46f7e146207fb0a7c1 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 8 Jan 2025 16:36:40 -0500 Subject: [PATCH 10/24] appkit: use ProtocolObject ProtocolObject is new in objc2, and lets us use the generated AppKit bindings instead of roughing it with `msg_send!`. --- .../apple/appkit/window_delegate.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 7a41ec026c..8edc24e6e6 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -9,11 +9,11 @@ use std::sync::{Arc, Mutex}; use core_graphics::display::CGDisplay; use objc2::rc::{autoreleasepool, Retained}; use objc2::runtime::{AnyObject, ProtocolObject}; -use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass}; +use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass}; use objc2_app_kit::{ NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization, NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType, - NSColor, NSDraggingDestination, NSFilenamesPboardType, NSPasteboard, + NSColor, NSDraggingDestination, NSDraggingInfo, NSFilenamesPboardType, NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSViewFrameDidChangeNotification, NSWindow, NSWindowButton, NSWindowDelegate, NSWindowFullScreenButton, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, @@ -375,12 +375,12 @@ declare_class!( unsafe impl NSDraggingDestination for WindowDelegate { /// Invoked when the dragged image enters destination bounds or frame #[method(draggingEntered:)] - fn dragging_entered(&self, sender: &NSObject) -> bool { + fn dragging_entered(&self, info: &ProtocolObject) -> bool { trace_scope!("draggingEntered:"); use std::path::PathBuf; - let pb: Retained = unsafe { msg_send_id![sender, draggingPasteboard] }; + let pb = unsafe { info.draggingPasteboard() }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Retained> = unsafe { Retained::cast(filenames) }; let paths = filenames @@ -388,7 +388,7 @@ declare_class!( .map(|file| PathBuf::from(file.to_string())) .collect(); - let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let dl = unsafe { info.draggingLocation() }; let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); @@ -406,10 +406,10 @@ declare_class!( /// Invoked periodically as the image is held within the destination area, allowing modification of the dragging operation or mouse-pointer position. #[method(draggingUpdated:)] - fn dragging_updated(&self, sender: &NSObject) -> bool { + fn dragging_updated(&self, info: &ProtocolObject) -> bool { trace_scope!("draggingUpdated:"); - let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let dl = unsafe { info.draggingLocation() }; let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); @@ -427,12 +427,12 @@ declare_class!( /// Invoked after the released image has been removed from the screen #[method(performDragOperation:)] - fn perform_drag_operation(&self, sender: &NSObject) -> bool { + fn perform_drag_operation(&self, info: &ProtocolObject) -> bool { trace_scope!("performDragOperation:"); use std::path::PathBuf; - let pb: Retained = unsafe { msg_send_id![sender, draggingPasteboard] }; + let pb = unsafe { info.draggingPasteboard() }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Retained> = unsafe { Retained::cast(filenames) }; let paths = filenames @@ -440,7 +440,7 @@ declare_class!( .map(|file| PathBuf::from(file.to_string())) .collect(); - let dl: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let dl = unsafe { info.draggingLocation() }; let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); From 5bbbeddcf68dfab4b1330b93d09718812549f211 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 10:48:45 -0500 Subject: [PATCH 11/24] windows: only emit DragEnter if enter_is_valid --- src/platform_impl/windows/drop_handler.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index a97a6703bf..515929c674 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -91,11 +91,13 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); let mut paths = Vec::new(); let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragEnter { paths, position }, - }); drop_handler.enter_is_valid = hdrop.is_some(); + if drop_handler.enter_is_valid { + drop_handler.send_event(Event::WindowEvent { + window_id: WindowId::from_raw(drop_handler.window as usize), + event: DragEnter { paths, position }, + }); + } drop_handler.cursor_effect = if drop_handler.enter_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; unsafe { From 3d3f6fbc59eb364557997f81bef704ccae703166 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 10:57:19 -0500 Subject: [PATCH 12/24] x11: store translated DnD coords --- src/platform_impl/linux/x11/dnd.rs | 5 ++- .../linux/x11/event_processor.rs | 43 +++++-------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 247a0cd4b9..c5b79cfff4 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use std::str::Utf8Error; use std::sync::Arc; +use dpi::PhysicalPosition; use percent_encoding::percent_decode; use x11rb::protocol::xproto::{self, ConnectionExt}; @@ -46,7 +47,7 @@ pub struct Dnd { // Populated by XdndPosition event handler pub source_window: Option, // Populated by XdndPosition event handler - pub position: (i16, i16), + pub position: PhysicalPosition, // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option, DndDataParseError>>, // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) @@ -60,7 +61,7 @@ impl Dnd { version: None, type_list: None, source_window: None, - position: (0, 0), + position: PhysicalPosition::default(), result: None, has_entered: false, }) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index ed65ade69a..b7a1a634a4 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -471,15 +471,18 @@ impl EventProcessor { let source_window = xev.data.get_long(0) as xproto::Window; // https://www.freedesktop.org/wiki/Specifications/XDND/#xdndposition - // "data.l[2] contains the coordinates of the mouse position relative to the root - // window." - // "data.l[2] = (x << 16) | y" // Note that coordinates are in "desktop space", not "window space" // (in X11 parlance, they're root window coordinates) let packed_coordinates = xev.data.get_long(2); let x = (packed_coordinates >> 16) as i16; let y = (packed_coordinates & 0xffff) as i16; - self.dnd.position = (x, y); + + let coords = self + .target + .xconn + .translate_coords(self.target.root, window, x, y) + .expect("Failed to translate window coordinates"); + self.dnd.position = PhysicalPosition::new(coords.dst_x as f64, coords.dst_y as f64); // By our own state flow, `version` should never be `None` at this point. let version = self.dnd.version.unwrap_or(5); @@ -530,24 +533,11 @@ impl EventProcessor { if xev.message_type == atoms[XdndDrop] as c_ulong { 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 = self - .target - .xconn - .translate_coords( - self.target.root, - window, - self.dnd.position.0, - self.dnd.position.1, - ) - .expect("Failed to translate window coordinates"); - - let position = PhysicalPosition::new(coords.dst_x as f64, coords.dst_y as f64); - callback(&self.target, Event::WindowEvent { window_id, event: WindowEvent::DragDrop { paths: path_list.iter().map(Into::into).collect(), - position, + position: self.dnd.position, }, }); } @@ -598,30 +588,17 @@ impl EventProcessor { let parse_result = self.dnd.parse_data(&mut data); if let Ok(ref path_list) = parse_result { - let coords = self - .target - .xconn - .translate_coords( - self.target.root, - window, - self.dnd.position.0, - self.dnd.position.1, - ) - .expect("Failed to translate window coordinates"); - - let position = PhysicalPosition::new(coords.dst_x as f64, coords.dst_y as f64); - if self.dnd.has_entered { callback(&self.target, Event::WindowEvent { window_id, - event: WindowEvent::DragOver { position }, + event: WindowEvent::DragOver { position: self.dnd.position }, }); } else { let paths = path_list.iter().map(Into::into).collect(); self.dnd.has_entered = true; callback(&self.target, Event::WindowEvent { window_id, - event: WindowEvent::DragEnter { paths, position }, + event: WindowEvent::DragEnter { paths, position: self.dnd.position }, }); } } From bafb09037daeaa0eff432aac64df9ebe8f733ce3 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 10:58:38 -0500 Subject: [PATCH 13/24] x11: don't emit DragLeave without DragEnter --- src/platform_impl/linux/x11/event_processor.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index b7a1a634a4..6e4c1e26c0 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -560,9 +560,11 @@ impl EventProcessor { } if xev.message_type == atoms[XdndLeave] as c_ulong { + if self.dnd.has_entered { + let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeave }; + callback(&self.target, event); + } self.dnd.reset(); - let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeave }; - callback(&self.target, event); } } From 2873e88321d50bac715f4584b66e1234e394bd09 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 11:09:45 -0500 Subject: [PATCH 14/24] refactor: reword drag events to match pointer ones --- examples/dnd.rs | 6 ++-- examples/window.rs | 6 ++-- src/changelog/unreleased.md | 10 +++---- src/event.rs | 29 ++++++++++--------- .../apple/appkit/window_delegate.rs | 6 ++-- .../linux/x11/event_processor.rs | 6 ++-- src/platform_impl/windows/drop_handler.rs | 14 ++++----- 7 files changed, 40 insertions(+), 37 deletions(-) diff --git a/examples/dnd.rs b/examples/dnd.rs index 5a659937c6..8a79231d6c 100644 --- a/examples/dnd.rs +++ b/examples/dnd.rs @@ -42,9 +42,9 @@ impl ApplicationHandler for Application { event: WindowEvent, ) { match event { - WindowEvent::DragLeave - | WindowEvent::DragEnter { .. } - | WindowEvent::DragOver { .. } + WindowEvent::DragLeft + | WindowEvent::DragEntered { .. } + | WindowEvent::DragMoved { .. } | WindowEvent::DragDrop { .. } => { println!("{:?}", event); }, diff --git a/examples/window.rs b/examples/window.rs index 5457272170..cab9426875 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -541,11 +541,11 @@ impl ApplicationHandler for Application { info!("Smart zoom"); }, WindowEvent::TouchpadPressure { .. } - | WindowEvent::DragLeave + | WindowEvent::DragLeft | WindowEvent::KeyboardInput { .. } | WindowEvent::PointerEntered { .. } - | WindowEvent::DragEnter { .. } - | WindowEvent::DragOver { .. } + | WindowEvent::DragEntered { .. } + | WindowEvent::DragMoved { .. } | WindowEvent::DragDrop { .. } | WindowEvent::Destroyed | WindowEvent::Moved(_) => (), diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 95b9bc64c1..026a55f79f 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -168,8 +168,8 @@ changelog entry. - Reworked the file drag-and-drop API. The `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled` - events have been removed, and replaced with `WindowEvent::DragEnter`, `WindowEvent::DragOver`, - `WindowEvent::DragDrop` and `WindowEvent::DragLeave`. + events have been removed, and replaced with `WindowEvent::DragEntered`, `WindowEvent::DragMoved`, + `WindowEvent::DragDrop` and `WindowEvent::DragLeft`. The old drag-and-drop events were emitted once per file. This occurred when files were *first* hovered over the window, dropped, or left the window. The new drag-and-drop events are emitted @@ -177,11 +177,11 @@ changelog entry. pointer position. The rough correspondence is: - - `WindowEvent::HoveredFile` -> `WindowEvent::DragEnter` + - `WindowEvent::HoveredFile` -> `WindowEvent::DragEntered` - `WindowEvent::DroppedFile` -> `WindowEvent::DragDrop` - - `WindowEvent::HoveredFileCancelled` -> `WindowEvent::DragLeave` + - `WindowEvent::HoveredFileCancelled` -> `WindowEvent::DragLeft` - The `WindowEvent::DragOver` event is entirely new, and is emitted whenever the pointer moves + The `WindowEvent::DragMoved` event is entirely new, and is emitted whenever the pointer moves whilst files are being dragged over the window. It doesn't contain any file paths, just the pointer position. diff --git a/src/event.rs b/src/event.rs index da238d959c..f913407d60 100644 --- a/src/event.rs +++ b/src/event.rs @@ -176,30 +176,33 @@ pub enum WindowEvent { Destroyed, /// A drag operation has entered the window. - DragEnter { + DragEntered { /// List of paths that are being dragged onto the window. paths: Vec, - /// Position of the drag operation. May be negative on some platforms if something is - /// dragged over a window's decorations (title bar, frame, etc). + /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be + /// negative on some platforms if something is dragged over a window's decorations (title + /// bar, frame, etc). position: PhysicalPosition, }, - /// A drag operation is moving over the window. - DragOver { - /// Position of the drag operation. May be negative on some platforms if something is - /// dragged over a window's decorations (title bar, frame, etc). + /// A drag operation has moved over the window. + DragMoved { + /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be + /// negative on some platforms if something is dragged over a window's decorations (title + /// bar, frame, etc). 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. May be negative on some platforms if something is - /// dragged over a window's decorations (title bar, frame, etc). + /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be + /// negative on some platforms if something is dragged over a window's decorations (title + /// bar, frame, etc). position: PhysicalPosition, }, /// The drag operation has been cancelled or left the window. On some platforms, this may occur /// even if no other drag events have occurred. - DragLeave, + DragLeft, /// The window gained or lost focus. /// @@ -1224,16 +1227,16 @@ mod tests { with_window_event(Focused(true)); with_window_event(Moved((0, 0).into())); with_window_event(SurfaceResized((0, 0).into())); - with_window_event(DragEnter { + with_window_event(DragEntered { paths: vec!["x.txt".into()], position: (0, 0).into(), }); - with_window_event(DragOver { position: (0, 0).into() }); + with_window_event(DragMoved { position: (0, 0).into() }); with_window_event(DragDrop { paths: vec!["x.txt".into()], position: (0, 0).into(), }); - with_window_event(DragLeave); + with_window_event(DragLeft); with_window_event(Ime(Enabled)); with_window_event(PointerMoved { device_id: None, diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 8edc24e6e6..7339911c30 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -393,7 +393,7 @@ declare_class!( let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); - self.queue_event(WindowEvent::DragEnter { paths, position }); + self.queue_event(WindowEvent::DragEntered { paths, position }); true } @@ -413,7 +413,7 @@ declare_class!( let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); - self.queue_event(WindowEvent::DragOver { position }); + self.queue_event(WindowEvent::DragMoved { position }); true } @@ -459,7 +459,7 @@ declare_class!( #[method(draggingExited:)] fn dragging_exited(&self, _sender: Option<&NSObject>) { trace_scope!("draggingExited:"); - self.queue_event(WindowEvent::DragLeave); + self.queue_event(WindowEvent::DragLeft); } } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 6e4c1e26c0..5de5b8e923 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -561,7 +561,7 @@ impl EventProcessor { if xev.message_type == atoms[XdndLeave] as c_ulong { if self.dnd.has_entered { - let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeave }; + let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeft }; callback(&self.target, event); } self.dnd.reset(); @@ -593,14 +593,14 @@ impl EventProcessor { if self.dnd.has_entered { callback(&self.target, Event::WindowEvent { window_id, - event: WindowEvent::DragOver { position: self.dnd.position }, + event: WindowEvent::DragMoved { position: self.dnd.position }, }); } else { let paths = path_list.iter().map(Into::into).collect(); self.dnd.has_entered = true; callback(&self.target, Event::WindowEvent { window_id, - event: WindowEvent::DragEnter { paths, position: self.dnd.position }, + event: WindowEvent::DragEntered { paths, position: self.dnd.position }, }); } } diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 515929c674..1c772099f8 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -27,7 +27,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 */ + * `DragLeft` emitted */ } pub struct FileDropHandler { @@ -82,7 +82,7 @@ impl FileDropHandler { pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DragEnter; + use crate::event::WindowEvent::DragEntered; let drop_handler = unsafe { Self::from_interface(this) }; let mut pt = POINT { x: pt.x, y: pt.y }; unsafe { @@ -95,7 +95,7 @@ impl FileDropHandler { if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragEnter { paths, position }, + event: DragEntered { paths, position }, }); } drop_handler.cursor_effect = @@ -113,7 +113,7 @@ impl FileDropHandler { pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DragOver; + use crate::event::WindowEvent::DragMoved; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { let mut pt = POINT { x: pt.x, y: pt.y }; @@ -123,7 +123,7 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragOver { position }, + event: DragMoved { position }, }); } unsafe { @@ -133,12 +133,12 @@ impl FileDropHandler { } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { - use crate::event::WindowEvent::DragLeave; + use crate::event::WindowEvent::DragLeft; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragLeave, + event: DragLeft, }); } From 991b4c83f44389c44ae6e68453854b673c48781f Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 11:17:02 -0500 Subject: [PATCH 15/24] feat: add position to DragLeft Not yet implemented on macOS --- examples/dnd.rs | 2 +- examples/window.rs | 2 +- src/event.rs | 13 +++++++++++-- src/platform_impl/apple/appkit/window_delegate.rs | 2 +- src/platform_impl/linux/x11/event_processor.rs | 5 ++++- src/platform_impl/windows/drop_handler.rs | 2 +- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/examples/dnd.rs b/examples/dnd.rs index 8a79231d6c..0a610d2bb5 100644 --- a/examples/dnd.rs +++ b/examples/dnd.rs @@ -42,7 +42,7 @@ impl ApplicationHandler for Application { event: WindowEvent, ) { match event { - WindowEvent::DragLeft + WindowEvent::DragLeft { .. } | WindowEvent::DragEntered { .. } | WindowEvent::DragMoved { .. } | WindowEvent::DragDrop { .. } => { diff --git a/examples/window.rs b/examples/window.rs index cab9426875..e8f3077330 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -541,7 +541,7 @@ impl ApplicationHandler for Application { info!("Smart zoom"); }, WindowEvent::TouchpadPressure { .. } - | WindowEvent::DragLeft + | WindowEvent::DragLeft { .. } | WindowEvent::KeyboardInput { .. } | WindowEvent::PointerEntered { .. } | WindowEvent::DragEntered { .. } diff --git a/src/event.rs b/src/event.rs index f913407d60..30d3eb5b4d 100644 --- a/src/event.rs +++ b/src/event.rs @@ -202,7 +202,16 @@ pub enum WindowEvent { }, /// The drag operation has been cancelled or left the window. On some platforms, this may occur /// even if no other drag events have occurred. - DragLeft, + DragLeft { + /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be + /// negative on some platforms if something is dragged over a window's decorations (title + /// bar, frame, etc). + /// + /// ## Platform-specific + /// + /// - **Windows:** Always emits [`None`]. + position: Option>, + }, /// The window gained or lost focus. /// @@ -1236,7 +1245,7 @@ mod tests { paths: vec!["x.txt".into()], position: (0, 0).into(), }); - with_window_event(DragLeft); + with_window_event(DragLeft { position: Some((0, 0).into()) }); with_window_event(Ime(Enabled)); with_window_event(PointerMoved { device_id: None, diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 7339911c30..40cc13ac8a 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -459,7 +459,7 @@ declare_class!( #[method(draggingExited:)] fn dragging_exited(&self, _sender: Option<&NSObject>) { trace_scope!("draggingExited:"); - self.queue_event(WindowEvent::DragLeft); + self.queue_event(WindowEvent::DragLeft { position: None } ); } } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 5de5b8e923..87986bb57f 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -561,7 +561,10 @@ impl EventProcessor { if xev.message_type == atoms[XdndLeave] as c_ulong { if self.dnd.has_entered { - let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeft }; + let event = Event::WindowEvent { + window_id, + event: WindowEvent::DragLeft { position: Some(self.dnd.position) }, + }; callback(&self.target, event); } self.dnd.reset(); diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 1c772099f8..1ffc802507 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -138,7 +138,7 @@ impl FileDropHandler { if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragLeft, + event: DragLeft { position: None }, }); } From dcfb62d2999b5a28b2877516a4e908db7aefbac7 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 11:21:19 -0500 Subject: [PATCH 16/24] macos: implement position on DragLeft The Swift docs say the dragging info may be nullable here specifically: https://developer.apple.com/documentation/appkit/nsdraggingdestination/draggingexited(_:) --- src/platform_impl/apple/appkit/window_delegate.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 40cc13ac8a..85fdaae36c 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -457,9 +457,16 @@ declare_class!( /// Invoked when the dragging operation is cancelled #[method(draggingExited:)] - fn dragging_exited(&self, _sender: Option<&NSObject>) { + fn dragging_exited(&self, info: Option<&ProtocolObject>) { trace_scope!("draggingExited:"); - self.queue_event(WindowEvent::DragLeft { position: None } ); + + let position = info.map(|info| { + let dl = unsafe { info.draggingLocation() }; + let dl = self.view().convertPoint_fromView(dl, None); + LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()) + }); + + self.queue_event(WindowEvent::DragLeft { position } ); } } From 6b4b84baef3e85bc500c31e9161c29b6e3e0641a Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 11:26:52 -0500 Subject: [PATCH 17/24] refactor: DragDrop -> DragDropped Keep everything in past-tense --- examples/dnd.rs | 2 +- examples/window.rs | 2 +- src/changelog/unreleased.md | 4 ++-- src/event.rs | 4 ++-- src/platform_impl/apple/appkit/window_delegate.rs | 2 +- src/platform_impl/linux/x11/event_processor.rs | 2 +- src/platform_impl/windows/drop_handler.rs | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/dnd.rs b/examples/dnd.rs index 0a610d2bb5..1de137f7b3 100644 --- a/examples/dnd.rs +++ b/examples/dnd.rs @@ -45,7 +45,7 @@ impl ApplicationHandler for Application { WindowEvent::DragLeft { .. } | WindowEvent::DragEntered { .. } | WindowEvent::DragMoved { .. } - | WindowEvent::DragDrop { .. } => { + | WindowEvent::DragDropped { .. } => { println!("{:?}", event); }, WindowEvent::CloseRequested => { diff --git a/examples/window.rs b/examples/window.rs index e8f3077330..c0587fab93 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -546,7 +546,7 @@ impl ApplicationHandler for Application { | WindowEvent::PointerEntered { .. } | WindowEvent::DragEntered { .. } | WindowEvent::DragMoved { .. } - | WindowEvent::DragDrop { .. } + | WindowEvent::DragDropped { .. } | WindowEvent::Destroyed | WindowEvent::Moved(_) => (), } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 026a55f79f..cf9c9288a4 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -169,7 +169,7 @@ changelog entry. The `WindowEvent::DroppedFile`, `WindowEvent::HoveredFile` and `WindowEvent::HoveredFileCancelled` events have been removed, and replaced with `WindowEvent::DragEntered`, `WindowEvent::DragMoved`, - `WindowEvent::DragDrop` and `WindowEvent::DragLeft`. + `WindowEvent::DragDropped` and `WindowEvent::DragLeft`. The old drag-and-drop events were emitted once per file. This occurred when files were *first* hovered over the window, dropped, or left the window. The new drag-and-drop events are emitted @@ -178,7 +178,7 @@ changelog entry. The rough correspondence is: - `WindowEvent::HoveredFile` -> `WindowEvent::DragEntered` - - `WindowEvent::DroppedFile` -> `WindowEvent::DragDrop` + - `WindowEvent::DroppedFile` -> `WindowEvent::DragDropped` - `WindowEvent::HoveredFileCancelled` -> `WindowEvent::DragLeft` The `WindowEvent::DragMoved` event is entirely new, and is emitted whenever the pointer moves diff --git a/src/event.rs b/src/event.rs index 30d3eb5b4d..dc22f08816 100644 --- a/src/event.rs +++ b/src/event.rs @@ -192,7 +192,7 @@ pub enum WindowEvent { position: PhysicalPosition, }, /// The drag operation has dropped file(s) on the window. - DragDrop { + DragDropped { /// List of paths that are being dragged onto the window. paths: Vec, /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be @@ -1241,7 +1241,7 @@ mod tests { position: (0, 0).into(), }); with_window_event(DragMoved { position: (0, 0).into() }); - with_window_event(DragDrop { + with_window_event(DragDropped { paths: vec!["x.txt".into()], position: (0, 0).into(), }); diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 85fdaae36c..3865e69f79 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -444,7 +444,7 @@ declare_class!( let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); - self.queue_event(WindowEvent::DragDrop { paths, position }); + self.queue_event(WindowEvent::DragDropped { paths, position }); true } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 87986bb57f..4c8f249f7f 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -535,7 +535,7 @@ impl EventProcessor { if let Some(Ok(ref path_list)) = self.dnd.result { callback(&self.target, Event::WindowEvent { window_id, - event: WindowEvent::DragDrop { + event: WindowEvent::DragDropped { paths: path_list.iter().map(Into::into).collect(), position: self.dnd.position, }, diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 1ffc802507..af454c9da6 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -152,7 +152,7 @@ impl FileDropHandler { pt: POINTL, _pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DragDrop; + use crate::event::WindowEvent::DragDropped; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { let mut pt = POINT { x: pt.x, y: pt.y }; @@ -164,7 +164,7 @@ impl FileDropHandler { let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragDrop { paths, position }, + event: DragDropped { paths, position }, }); if let Some(hdrop) = hdrop { unsafe { From a4be71eabd1f6153beebb56cb066c1b6a2564db6 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 10 Jan 2025 11:28:40 -0500 Subject: [PATCH 18/24] docs: clarify drag events Make it clearer that drag events are for dragged *files*, and remove the outdated bit about spurious DragLeft events. --- src/event.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/event.rs b/src/event.rs index dc22f08816..0f9571c8d8 100644 --- a/src/event.rs +++ b/src/event.rs @@ -175,7 +175,7 @@ pub enum WindowEvent { /// The window has been destroyed. Destroyed, - /// A drag operation has entered the window. + /// A file drag operation has entered the window. DragEntered { /// List of paths that are being dragged onto the window. paths: Vec, @@ -184,14 +184,14 @@ pub enum WindowEvent { /// bar, frame, etc). position: PhysicalPosition, }, - /// A drag operation has moved over the window. + /// A file drag operation has moved over the window. DragMoved { /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be /// negative on some platforms if something is dragged over a window's decorations (title /// bar, frame, etc). position: PhysicalPosition, }, - /// The drag operation has dropped file(s) on the window. + /// The file drag operation has dropped file(s) on the window. DragDropped { /// List of paths that are being dragged onto the window. paths: Vec, @@ -200,8 +200,7 @@ pub enum WindowEvent { /// bar, frame, etc). position: PhysicalPosition, }, - /// The drag operation has been cancelled or left the window. On some platforms, this may occur - /// even if no other drag events have occurred. + /// The file drag operation has been cancelled or left the window. DragLeft { /// (x,y) coordinates in pixels relative to the top-left corner of the window. May be /// negative on some platforms if something is dragged over a window's decorations (title From df8e5ce2417130bf48fe3ff55d200b5f4e1ca7cc Mon Sep 17 00:00:00 2001 From: valadaptive Date: Mon, 13 Jan 2025 15:29:11 -0500 Subject: [PATCH 19/24] examples/dnd: handle RedrawRequested event Needed to display anything on Wayland. DnD still doesn't work on Wayland but the example should at least run. --- examples/dnd.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/dnd.rs b/examples/dnd.rs index 1de137f7b3..a05e12d57c 100644 --- a/examples/dnd.rs +++ b/examples/dnd.rs @@ -5,6 +5,8 @@ use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::window::{Window, WindowAttributes, WindowId}; +#[path = "util/fill.rs"] +mod fill; #[path = "util/tracing.rs"] mod tracing; @@ -48,6 +50,11 @@ impl ApplicationHandler for Application { | WindowEvent::DragDropped { .. } => { println!("{:?}", event); }, + WindowEvent::RedrawRequested => { + let window = self.window.as_ref().unwrap(); + window.pre_present_notify(); + fill::fill_window(window.as_ref()); + }, WindowEvent::CloseRequested => { event_loop.exit(); }, From f55a5347ceda2f51827cabf95b5582919c164334 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 17 Jan 2025 12:34:15 -0500 Subject: [PATCH 20/24] x11: DnD code style tweaks --- src/platform_impl/linux/x11/dnd.rs | 6 +-- .../linux/x11/event_processor.rs | 39 ++++++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index c5b79cfff4..f5988aaad6 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -51,7 +51,7 @@ pub struct Dnd { // 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, + pub dragging: bool, } impl Dnd { @@ -63,7 +63,7 @@ impl Dnd { source_window: None, position: PhysicalPosition::default(), result: None, - has_entered: false, + dragging: false, }) } @@ -72,7 +72,7 @@ impl Dnd { self.type_list = None; self.source_window = None; self.result = None; - self.has_entered = false; + self.dragging = 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 4c8f249f7f..61365490ca 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -507,11 +507,11 @@ impl EventProcessor { } self.dnd.source_window = Some(source_window); - let time = if version >= 1 { - xev.data.get_long(3) as xproto::Timestamp - } else { + let time = if version == 0 { // In version 0, time isn't specified x11rb::CURRENT_TIME + } else { + xev.data.get_long(3) as xproto::Timestamp }; // Log this timestamp. @@ -533,13 +533,12 @@ impl EventProcessor { if xev.message_type == atoms[XdndDrop] as c_ulong { let (source_window, state) = if let Some(source_window) = self.dnd.source_window { if let Some(Ok(ref path_list)) = self.dnd.result { - callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::DragDropped { - paths: path_list.iter().map(Into::into).collect(), - position: self.dnd.position, - }, - }); + let event = WindowEvent::DragDropped { + paths: path_list.iter().map(Into::into).collect(), + position: self.dnd.position, + }; + + callback(&self.target, Event::WindowEvent { window_id, event }); } (source_window, DndState::Accepted) } else { @@ -560,7 +559,7 @@ impl EventProcessor { } if xev.message_type == atoms[XdndLeave] as c_ulong { - if self.dnd.has_entered { + if self.dnd.dragging { let event = Event::WindowEvent { window_id, event: WindowEvent::DragLeft { position: Some(self.dnd.position) }, @@ -593,19 +592,15 @@ impl EventProcessor { let parse_result = self.dnd.parse_data(&mut data); if let Ok(ref path_list) = parse_result { - if self.dnd.has_entered { - callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::DragMoved { position: self.dnd.position }, - }); + let event = if self.dnd.dragging { + WindowEvent::DragMoved { position: self.dnd.position } } else { let paths = path_list.iter().map(Into::into).collect(); - self.dnd.has_entered = true; - callback(&self.target, Event::WindowEvent { - window_id, - event: WindowEvent::DragEntered { paths, position: self.dnd.position }, - }); - } + self.dnd.dragging = true; + WindowEvent::DragEntered { paths, position: self.dnd.position } + }; + + callback(&self.target, Event::WindowEvent { window_id, event }); } self.dnd.result = Some(parse_result); From 2f589b51b54e07ed5e850eabc406e6b69f7a2168 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 17 Jan 2025 12:45:18 -0500 Subject: [PATCH 21/24] windows: import DnD events at top of file --- src/platform_impl/windows/drop_handler.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index af454c9da6..2ce204d2cb 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -13,7 +13,7 @@ use windows_sys::Win32::System::Ole::{CF_HDROP, DROPEFFECT_COPY, DROPEFFECT_NONE use windows_sys::Win32::UI::Shell::{DragFinish, DragQueryFileW, HDROP}; use crate::dpi::PhysicalPosition; -use crate::event::Event; +use crate::event::{Event, WindowEvent}; use crate::platform_impl::platform::definitions::{ IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl, }; @@ -82,7 +82,6 @@ impl FileDropHandler { pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DragEntered; let drop_handler = unsafe { Self::from_interface(this) }; let mut pt = POINT { x: pt.x, y: pt.y }; unsafe { @@ -95,7 +94,7 @@ impl FileDropHandler { if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragEntered { paths, position }, + event: WindowEvent::DragEntered { paths, position }, }); } drop_handler.cursor_effect = @@ -113,7 +112,6 @@ impl FileDropHandler { pt: POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DragMoved; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { let mut pt = POINT { x: pt.x, y: pt.y }; @@ -123,7 +121,7 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragMoved { position }, + event: WindowEvent::DragMoved { position }, }); } unsafe { @@ -133,12 +131,11 @@ impl FileDropHandler { } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { - use crate::event::WindowEvent::DragLeft; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragLeft { position: None }, + event: WindowEvent::DragLeft { position: None }, }); } @@ -152,7 +149,6 @@ impl FileDropHandler { pt: POINTL, _pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DragDropped; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.enter_is_valid { let mut pt = POINT { x: pt.x, y: pt.y }; @@ -164,7 +160,7 @@ impl FileDropHandler { let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), - event: DragDropped { paths, position }, + event: WindowEvent::DragDropped { paths, position }, }); if let Some(hdrop) = hdrop { unsafe { From 59dd7ede074e0e4893d96f0cf4f7f9bd12d7923e Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 17 Jan 2025 13:22:04 -0500 Subject: [PATCH 22/24] windows: drop_handler.enter_is_valid -> valid --- src/platform_impl/windows/drop_handler.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 2ce204d2cb..e67942a3f0 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -26,8 +26,8 @@ pub struct FileDropHandlerData { window: HWND, send_event: Box, cursor_effect: u32, - enter_is_valid: bool, /* If the currently hovered item is not valid there must not be any - * `DragLeft` emitted */ + valid: bool, /* If the currently hovered item is not valid there must not be any + * `DragLeft` emitted */ } pub struct FileDropHandler { @@ -43,7 +43,7 @@ impl FileDropHandler { window, send_event, cursor_effect: DROPEFFECT_NONE, - enter_is_valid: false, + valid: false, }); FileDropHandler { data: Box::into_raw(data) } } @@ -90,15 +90,15 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); let mut paths = Vec::new(); let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; - drop_handler.enter_is_valid = hdrop.is_some(); - if drop_handler.enter_is_valid { + drop_handler.valid = hdrop.is_some(); + if drop_handler.valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), event: WindowEvent::DragEntered { paths, position }, }); } drop_handler.cursor_effect = - if drop_handler.enter_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; + if drop_handler.valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; unsafe { *pdwEffect = drop_handler.cursor_effect; } @@ -113,7 +113,7 @@ impl FileDropHandler { pdwEffect: *mut u32, ) -> HRESULT { let drop_handler = unsafe { Self::from_interface(this) }; - if drop_handler.enter_is_valid { + if drop_handler.valid { let mut pt = POINT { x: pt.x, y: pt.y }; unsafe { ScreenToClient(drop_handler.window, &mut pt); @@ -132,7 +132,7 @@ impl FileDropHandler { pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { let drop_handler = unsafe { Self::from_interface(this) }; - if drop_handler.enter_is_valid { + if drop_handler.valid { drop_handler.send_event(Event::WindowEvent { window_id: WindowId::from_raw(drop_handler.window as usize), event: WindowEvent::DragLeft { position: None }, @@ -150,7 +150,7 @@ impl FileDropHandler { _pdwEffect: *mut u32, ) -> HRESULT { let drop_handler = unsafe { Self::from_interface(this) }; - if drop_handler.enter_is_valid { + if drop_handler.valid { let mut pt = POINT { x: pt.x, y: pt.y }; unsafe { ScreenToClient(drop_handler.window, &mut pt); From 2153865ccb6a199f5bc01be56e5f04449f2455ae Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 17 Jan 2025 17:18:19 -0500 Subject: [PATCH 23/24] windows: in DnD, always set pdwEffect It appears other apps (like Chromium) set pdwEffect on Drop too: https://github.com/chromium/chromium/blob/61a391b86bd946d6e1105412539e77ba9fb2a6b3/ui/base/dragdrop/drop_target_win.cc --- src/platform_impl/windows/drop_handler.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index e67942a3f0..3f378f6ebc 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -127,6 +127,7 @@ impl FileDropHandler { unsafe { *pdwEffect = drop_handler.cursor_effect; } + S_OK } @@ -147,7 +148,7 @@ impl FileDropHandler { pDataObj: *const IDataObject, _grfKeyState: u32, pt: POINTL, - _pdwEffect: *mut u32, + pdwEffect: *mut u32, ) -> HRESULT { let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.valid { @@ -168,6 +169,9 @@ impl FileDropHandler { } } } + unsafe { + *pdwEffect = drop_handler.cursor_effect; + } S_OK } From 731a0ce24f5c246aecb130fc86e1bc0ebedddfef Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 28 Jan 2025 20:57:19 +0100 Subject: [PATCH 24/24] Don't rename protocol parameter --- .../apple/appkit/window_delegate.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 3865e69f79..926ff03018 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -375,12 +375,12 @@ declare_class!( unsafe impl NSDraggingDestination for WindowDelegate { /// Invoked when the dragged image enters destination bounds or frame #[method(draggingEntered:)] - fn dragging_entered(&self, info: &ProtocolObject) -> bool { + fn dragging_entered(&self, sender: &ProtocolObject) -> bool { trace_scope!("draggingEntered:"); use std::path::PathBuf; - let pb = unsafe { info.draggingPasteboard() }; + let pb = unsafe { sender.draggingPasteboard() }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Retained> = unsafe { Retained::cast(filenames) }; let paths = filenames @@ -388,7 +388,7 @@ declare_class!( .map(|file| PathBuf::from(file.to_string())) .collect(); - let dl = unsafe { info.draggingLocation() }; + let dl = unsafe { sender.draggingLocation() }; let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); @@ -406,10 +406,10 @@ declare_class!( /// Invoked periodically as the image is held within the destination area, allowing modification of the dragging operation or mouse-pointer position. #[method(draggingUpdated:)] - fn dragging_updated(&self, info: &ProtocolObject) -> bool { + fn dragging_updated(&self, sender: &ProtocolObject) -> bool { trace_scope!("draggingUpdated:"); - let dl = unsafe { info.draggingLocation() }; + let dl = unsafe { sender.draggingLocation() }; let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor()); @@ -427,12 +427,12 @@ declare_class!( /// Invoked after the released image has been removed from the screen #[method(performDragOperation:)] - fn perform_drag_operation(&self, info: &ProtocolObject) -> bool { + fn perform_drag_operation(&self, sender: &ProtocolObject) -> bool { trace_scope!("performDragOperation:"); use std::path::PathBuf; - let pb = unsafe { info.draggingPasteboard() }; + let pb = unsafe { sender.draggingPasteboard() }; let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Retained> = unsafe { Retained::cast(filenames) }; let paths = filenames @@ -440,7 +440,7 @@ declare_class!( .map(|file| PathBuf::from(file.to_string())) .collect(); - let dl = unsafe { info.draggingLocation() }; + let dl = unsafe { sender.draggingLocation() }; let dl = self.view().convertPoint_fromView(dl, None); let position = LogicalPosition::::from((dl.x, dl.y)).to_physical(self.scale_factor());