Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework DnD redux #4079

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

valadaptive
Copy link

@valadaptive valadaptive commented Jan 8, 2025

This picks up from #2615, rebasing it atop the latest winit version and fixing a couple coordinate-space bugs. Part of #720.

Resolves #1550. Closes #2615.

  • Tested on all platforms changed
    • Windows (low- and high-DPI)
    • macOS (low- and high-DPI)
    • Linux X11 (low- and high-DPI)
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

@valadaptive valadaptive mentioned this pull request Jan 8, 2025
8 tasks
@valadaptive valadaptive force-pushed the dnd-cursor-location-rebase branch from 008f86a to aa18119 Compare January 8, 2025 19:40
@valadaptive valadaptive changed the title DnD changes so far (rebased) Rework DnD redux Jan 8, 2025
@valadaptive valadaptive force-pushed the dnd-cursor-location-rebase branch from aa18119 to 4f0819f Compare January 8, 2025 19:42
@valadaptive valadaptive marked this pull request as ready for review January 8, 2025 19:50
@valadaptive valadaptive force-pushed the dnd-cursor-location-rebase branch 3 times, most recently from b6eba6b to fe7132e Compare January 9, 2025 19:26
Copy link
Member

@kchibisov kchibisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will leave macOS re-review up to @madsmtm .

src/event.rs Outdated
},
/// 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a position: Option<PhysicalPosition<f64>> to follow the pointer api.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added position.

Comment on lines 168 to 185

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you write them the way e.g. Pointer event above written?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed them to DragEntered, DragDropped, DragLeft, and DragMoved.

Comment on lines 473 to 476
// 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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can keep the link, but what you wrote is already written in the next paragraph.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the redundant comment

Comment on lines 479 to 482
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe store translated position directly? I don't think there's a reason to use root coords?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to store the translated position.

@@ -502,21 +504,19 @@ impl EventProcessor {
}

self.dnd.source_window = Some(source_window);
if self.dnd.result.is_none() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this got removed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.dnd.result.is_none() is only true for the first DragEnter event. Keeping this check means no DragOver (aka DragMoved) events will be fired.

Comment on lines 136 to 142
if drop_handler.enter_is_valid {
drop_handler.send_event(Event::WindowEvent {
window_id: WindowId::from_raw(drop_handler.window as usize),
event: HoveredFileCancelled,
event: DragLeave,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's wrong, if you had Enter you should have Leave, and you send Enter regardless. Sending Enter and then not Leave looks wrong.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above; changed it so nothing is emitted if !enter_is_valid.

window_id: WindowId::from_raw(drop_handler.window as usize),
event: DragEnter { paths, position },
});
drop_handler.enter_is_valid = hdrop.is_some();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to utilize it, we should use it for sedening the Enter as well?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it so no events are emitted if !enter_is_valid.

X11 has the opposite issue: if there are no valid files being dragged, it won't emit any drag start/position events but will emit DragLeave. That's a pre-existing issue (in the current master branch, it will emit HoveredFileCancelled without any prior HoveredFile events), but I'll go ahead and fix that too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we stash whether the enter was emitted for X11?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; it's already stored as self.dnd.has_entered (I'll push those changes)

src/event.rs Outdated
Comment on lines 178 to 202
/// A drag operation has entered the window.
DragEnter {
/// List of paths that are being dragged onto the window.
paths: Vec<PathBuf>,
/// 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<f64>,
},
/// 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).
position: PhysicalPosition<f64>,
},
/// The drag operation has dropped file(s) on the window.
DragDrop {
/// List of paths that are being dragged onto the window.
paths: Vec<PathBuf>,
/// 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<f64>,
},
/// 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you reword things to how they are in pointer events?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed the events and reworded the documentation a bit; see above

src/event.rs Outdated
position: PhysicalPosition<f64>,
},
/// The drag operation has dropped file(s) on the window.
DragDrop {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DragDrop {
DragMoved {

so it looks like pointer.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@valadaptive valadaptive requested a review from kchibisov January 10, 2025 16:31
@@ -426,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<dyn NSDraggingInfo>>) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For @madsmtm: is this the right way to represent a sender param that may be null? The Swift docs note that for this specific callback, this parameter is nullable.

(As a side node, kinda alarming how the Objective-C docs just don't mention its nullability at all, as far as I can tell. Any idea what's going on there?)

amrbashir and others added 16 commits January 10, 2025 12:26
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
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.
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::<c_short> * 2 or something like that, because the specification
gives us the constant 16.
ProtocolObject is new in objc2, and lets us use the generated AppKit
bindings instead of roughing it with `msg_send!`.
Not yet implemented on macOS
Keep everything in past-tense
Make it clearer that drag events are for dragged *files*, and remove the
outdated bit about spurious DragLeft events.
@valadaptive valadaptive force-pushed the dnd-cursor-location-rebase branch from 18849af to 1084930 Compare January 10, 2025 17:28
@valadaptive
Copy link
Author

Made the requested review changes.

Needed to display anything on Wayland. DnD still doesn't work on Wayland
but the example should at least run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Drag&Drop API / HoveredFile, DroppedFile
3 participants