Skip to content

OS drag-and-drop file events on SDL - #922

Open
xsawyerx wants to merge 2 commits into
david-vanderson:mainfrom
xsawyerx:sawyer/drag-and-drop
Open

OS drag-and-drop file events on SDL#922
xsawyerx wants to merge 2 commits into
david-vanderson:mainfrom
xsawyerx:sawyer/drag-and-drop

Conversation

@xsawyerx

@xsawyerx xsawyerx commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

[I put all of this in the commit message too.]

SDL backend receives SDL_EVENT_DROP_FILE but it's logged and discarded.

  • added Event.Drop in EventTypes, like the .mouse, .key.
  • it's of Action type.
  • .enter = drag entered the window
  • .motion = drag moved while hovering
  • .leave = drag left or drop finished
  • .content = content was dropped (one event per content)
  • p (of type dvui.Point.Physical) is set for .motion and .file

The content can be a lot of stuff. SDL supports both file (SDL_EVENT_DROP_FILE) and text (SDL_EVENT_DROP_TEXT), but other systems might support much more. Across macOS NSPasteboard, Windows IDataObject, and GTK/Qt, there might be file URLs, plain text, RTF, HTML, images, URLs, app-defined custom types. So best to just separate it to Content.

For now, Content only has the two types that SDL has. I'm not sure if in the future it would be better to have backend-specific values or not.

Added Window.addEventDrop(action, p).

Tried to provide implementations for both SDL2 and SDL3. In SDL3, we need to translate the positions from window coordinates to physical pixels with the same scale factor as mouse motion.

SDL2 has no hover tracking, so it just uses .file.

Can use it with:

for (dvui.events()) |*e| switch (e.evt) {
    .drop => |d| switch (d.action) {
        .enter, .motion => highlight = true,
        .leave => highlight = false,
        .file => |path| openOrAppendImport(path), // copy to keep it
    },
    else => {},
};

@david-vanderson

Copy link
Copy Markdown
Owner

Thank you! I agree with the intention, we need to support this.

I'm trying to figure out if we can reuse the cross-widget dragging facilities for this. That could unify the event handling code for drags no matter if they originated from inside dvui or outside.

Thinking out loud:

That would look like dvui having a well-known drag name, like "dvui.content". Does SDL let us know the type file/text during the drag, or only at the release?

  • .enter would mean we start the "dvui.content" drag
  • .motion becomes a normal mouse motion event (is this duplicated by a normal SDL mouse event?)
  • .leave ends the drag
  • .content becomes a mouse release (the normal way a drag end is processed)
    • do we get all the .content sdl events on the same frame?
    • we could stuff them all together into a single "content" list so the widget only needs to process a single mouse release

I plan to explore this approach on this branch if that's ok. Any obvious problems with this?

@xsawyerx

xsawyerx commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

I'm trying to figure out if we can reuse the cross-widget dragging facilities for this. That could unify the event handling code for drags no matter if they originated from inside dvui or outside.

I think this is the right approach too.

Does SDL let us know the type file/text during the drag, or only at the release?

At release.

But it seems macOS draggingEntered (its equivalent of .enter) provides the content (as DraggingPasteboard).

Means populating the .content is needed whenever we receive it, whether .enter or .leave.

* `.enter` would mean we start the "dvui.content" drag

Yes.

* `.motion` becomes a normal mouse motion event

Yes

(is this duplicated by a normal SDL mouse event?)

In short, probably not. I wouldn't do that. I dug into this a bit and gave up after getting into the internals of X11 and Wayland. X11 sends the dragging through XdndPosition, Wayland through data_device_handle_motion - they're both DnD protocols and not just mouse clicks. I didn't check macOS or Windows.

So, I think it's best to just use DROP_POSITION.

* `.leave` ends the drag

Yes.

* `.content` becomes a mouse release (the normal way a drag end is processed)

I don't think you can view .content as mouse release. Firstly, because of how it handles events during dragging (the DnD stuff, being a different event channel) and secondly, because I don't think you're actually getting a mouse-up. The mouse-down started in another app, so would it create a mouse-up for something it couldn't connect to a mouse-up?

I'd say it's too risky to make these assumptions. (And I'd say I'm too lazy to thoroughly review X11/Wayland, macOS, and Windows code to determine this 😆)

I admit I don't know if the DnD channels vs. others actually matters for dvui, so take it with a grain of salt.

  * do we get all the `.content` sdl events on the same frame?

Yeah. And multi-files is just DROP_BEGIN to x amount of DROP_FILE to DROP_COMPLETE.

  * we could stuff them all together into a single "content" list so the widget only needs to process a single mouse release

Could do that, but doesn't dvui prefer one event per input? That would also cut out state and allow users to run some code for each file drop separately (if users want/need).

I plan to explore this approach on this branch if that's ok. Any obvious problems with this?

By all means! I'm already using it, but I'd trust your code over mine. :)

If you need me to test (Linux, macOS), let me know.

p5p added 2 commits August 12, 2026 14:38
SDL backend receives `SDL_EVENT_DROP_FILE` but it's logged and
discarded.

- added `Event.Drop` in `EventTypes`, like the `.mouse`, `.key`.
- it's of `Action` type.
 - `.enter` = drag entered the window
 - `.motion` = drag moved while hovering
 - `.leave` = drag left or drop finished
 - `.content` = content was dropped (one event per content)
- `p` (of type `dvui.Point.Physical`) is set for `.motion` and `.file`

The content can be a lot of stuff. SDL supports both file
(`SDL_EVENT_DROP_FILE`) and text (`SDL_EVENT_DROP_TEXT`), but other
systems might support much more. Across macOS NSPasteboard, Windows
IDataObject, and GTK/Qt, there might be file URLs, plain text, RTF,
HTML, images, URLs, app-defined custom types. So best to just separate
it to Content.

For now, Content only has the two types that SDL has. I'm not sure if in
the future it would be better to have backend-specific values or not.

Added `Window.addEventDrop(action, p)`.

Tried to provide implementations for both SDL2 and SDL3. In SDL3, we
need to translate the positions from window coordinates to physical
pixels with the same scale factor as mouse motion.

SDL2 has no hover tracking, so it just uses `.file`.

Can use it with:

    for (dvui.events()) |*e| switch (e.evt) {
        .drop => |d| switch (d.action) {
            .enter, .motion => highlight = true,
            .leave => highlight = false,
            .file => |path| openOrAppendImport(path), // copy to keep it
        },
        else => {},
    };
@xsawyerx
xsawyerx force-pushed the sawyer/drag-and-drop branch from 6d30400 to d4f6b40 Compare August 12, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants