Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 53 additions & 22 deletions crates/bevy_picking/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ pub struct DragEnd {
pub distance: Vec2,
}

/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target).
/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target)
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragEnter {
Expand Down Expand Up @@ -535,31 +535,33 @@ pub fn pointer_events(
}
}

// If the entity is hovered...
// Iterate all currently hovered entities for each pointer
for (pointer_id, hovered_entity, hit) in hover_map
.iter()
.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
{
// ...but was not hovered last frame...
if !previous_hover_map
.get(&pointer_id)
.iter()
.any(|e| e.contains_key(&hovered_entity))
{
let Some(location) = pointer_location(pointer_id) else {
debug!(
"Unable to get location for pointer {:?} during pointer over",
pointer_id
);
continue;
};

// Possibly send DragEnter events
for button in PointerButton::iter() {
let state = pointer_state.get_mut(pointer_id, button);
// Continue if the pointer does not have a valid location.
let Some(location) = pointer_location(pointer_id) else {
debug!(
"Unable to get location for pointer {:?} during pointer over",
pointer_id
);
continue;
};

// For each button update its `dragging_over` state and possibly emit DragEnter events.
for button in PointerButton::iter() {
let state = pointer_state.get_mut(pointer_id, button);

// Only update the `dragging_over` state if there is at least one entity being dragged.
// Only emit DragEnter events for this `hovered_entity`, if it had no previous `dragging_over` state.
if !state.dragging.is_empty()
&& state
.dragging_over
.insert(hovered_entity, hit.clone())
.is_none()
{
for drag_target in state.dragging.keys() {
state.dragging_over.insert(hovered_entity, hit.clone());
let drag_enter_event = Pointer::new(
pointer_id,
location.clone(),
Expand All @@ -574,8 +576,14 @@ pub fn pointer_events(
message_writers.drag_enter_events.write(drag_enter_event);
}
}
}

// Always send Over events
// Emit an Over event if the `hovered_entity` was not hovered by the same pointer the previous frame.
if !previous_hover_map
.get(&pointer_id)
.iter()
.any(|e| e.contains_key(&hovered_entity))
{
let over_event = Pointer::new(
pointer_id,
location.clone(),
Expand Down Expand Up @@ -738,8 +746,32 @@ pub fn pointer_events(
},
*press_target,
);

commands.trigger(drag_start_event.clone());
message_writers.drag_start_events.write(drag_start_event);

// Insert dragging over state and emit DragEnter for hovered entities.
for (hovered_entity, hit) in hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
.filter(|(hovered_entity, _)| *hovered_entity != *press_target)
{
// Inserting the `dragging_over` state here ensures the `DragEnter` event won't be dispatched twice.
state.dragging_over.insert(hovered_entity, hit.clone());
let drag_enter_event = Pointer::new(
pointer_id,
location.clone(),
DragEnter {
button,
dragged: *press_target,
hit: hit.clone(),
},
hovered_entity,
);
commands.trigger(drag_enter_event.clone());
message_writers.drag_enter_events.write(drag_enter_event);
}
}

// Emit Drag events to the entities we are dragging
Expand Down Expand Up @@ -771,7 +803,6 @@ pub fn pointer_events(
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
.filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
{
*state.dragging_over.get_mut(&hovered_entity).unwrap() = hit.clone();
Copy link
Contributor

Choose a reason for hiding this comment

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

So this was the line that caused the panic. Is it valid to delete this line? Is it because it’s redundant now with the logic you put in?

Copy link
Contributor Author

@ickshonpe ickshonpe Dec 1, 2025

Choose a reason for hiding this comment

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

Yep, this line was a fix for another bug: #21849. It's redundant with this PR as the dragging_over states are already updated before the PointerInputs are processed.

let drag_over_event = Pointer::new(
pointer_id,
location.clone(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "DragEnter now fires on drag starts"
pull_requests: [21999]
---

`DragEnter` now also fires when a drag starts over an already hovered entity.