Skip to content

DropArea: route Drop only to a previously-accepting target#11716

Closed
tronical wants to merge 2 commits into
masterfrom
simon/dnd-no-drop-after-false-can-drop
Closed

DropArea: route Drop only to a previously-accepting target#11716
tronical wants to merge 2 commits into
masterfrom
simon/dnd-no-drop-after-false-can-drop

Conversation

@tronical
Copy link
Copy Markdown
Member

dropped used to fire even after can-drop returned false. Fix at the
routing layer: track on MouseInputState.drop_target_stack the item
stack of the DropArea that accepted the most recent DragMove. On
Release, only dispatch a Drop if the release point lies inside that
DropArea, and route it via the existing grab mechanism so non-accepting
DropAreas never see it. The full ancestry chain is stored (not just the
target's weak ref) so nested DropAreas with inner-reject + outer-accept
skip past the inner one that hit-test would reach first.

Outside that case the release is converted to MouseEvent::Exit so the
drag tears down cleanly without the underlying Release registering as a
spurious click/hover on the hit-tested item.

Adds a rejection-path scenario to dragarea_droparea.slint.

@tronical tronical requested a review from ogoffart May 13, 2026 13:56
Comment thread internal/core/input.rs Outdated
Comment on lines +997 to +1000
/// The item stack of the DropArea that accepted the most recent DragMove, if any.
/// On Release we use this to route the resulting Drop event only to that DropArea,
/// matching how OS DnD pipelines deliver drops only to targets that previously accepted.
pub(crate) drop_target_stack: Option<Vec<(ItemWeak, InputEventFilterResult)>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is it a stack?
I'd say only one should accept it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ahh, that was when I first tried to cover this with nested touch areas. But indeed, we only need the last anyway.

The Drop arm fired `dropped` unconditionally, even when `can-drop` had
returned false on the most recent DragMove. Track on
`MouseInputState.drop_target` whichever DropArea accepted that DragMove,
and on Release only convert to `MouseEvent::Drop` if such a target was
recorded. Otherwise tear the drag down via `MouseEvent::Exit` so neither
the Drop nor the underlying Release reaches a non-accepting target.

Adds a rejection scenario to dragarea_droparea.slint.
@tronical tronical force-pushed the simon/dnd-no-drop-after-false-can-drop branch from 45fd8e8 to 504cb30 Compare May 13, 2026 14:25
Copy link
Copy Markdown
Member

@ogoffart ogoffart left a comment

Choose a reason for hiding this comment

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

We need to consider that the position of the release might not be the same position as the move.
I don't know if any of our backend do that. But this is not an invariant in our platform API that there must always be move event at the same position before the release event.

For that reason, i think it is the responsibility of the user's drop implementation to always check the validity of the drop before and not assume can-drop always returned true.

We could change that by always calling the can-drop before drop in the DropArea event handler.

Comment thread internal/core/window.rs
mouse_input_state.drag_data = None;
if mouse_input_state.drop_target.take().is_some() {
drop_event.position = crate::lengths::logical_position_to_api(*position);
event = MouseEvent::Drop(drop_event);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note that you can still end up deliverying the event to a different item if the position changed from one item to another.

@tronical
Copy link
Copy Markdown
Member Author

Agreed. This is then covered by #11720 where can-drop() as well as drop() can return None and reject the drop.

@tronical tronical closed this May 15, 2026
@tronical tronical deleted the simon/dnd-no-drop-after-false-can-drop branch May 15, 2026 12:07
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.

2 participants