bevy_picking/
events.rs

1//! This module defines a stateful set of interaction events driven by the `PointerInput` stream
2//! and the hover state of each Pointer.
3//!
4//! # Usage
5//!
6//! To receive events from this module, you must use an [`Observer`] or [`MessageReader`] with [`Pointer<E>`] events.
7//! The simplest example, registering a callback when an entity is hovered over by a pointer, looks like this:
8//!
9//! ```rust
10//! # use bevy_ecs::prelude::*;
11//! # use bevy_picking::prelude::*;
12//! # let mut world = World::default();
13//! world.spawn_empty()
14//!     .observe(|event: On<Pointer<Over>>| {
15//!         println!("I am being hovered over");
16//!     });
17//! ```
18//!
19//! Observers give us three important properties:
20//! 1. They allow for attaching event handlers to specific entities,
21//! 2. they allow events to bubble up the entity hierarchy,
22//! 3. and they allow events of different types to be called in a specific order.
23//!
24//! The order in which interaction events are received is extremely important, and you can read more
25//! about it on the docs for the dispatcher system: [`pointer_events`]. This system runs in
26//! [`PreUpdate`](bevy_app::PreUpdate) in [`PickingSystems::Hover`](crate::PickingSystems::Hover). All pointer-event
27//! observers resolve during the sync point between [`pointer_events`] and
28//! [`update_interactions`](crate::hover::update_interactions).
29//!
30//! # Events Types
31//!
32//! The events this module defines fall into a few broad categories:
33//! + Hovering and movement: [`Over`], [`Move`], and [`Out`].
34//! + Clicking and pressing: [`Press`], [`Release`], and [`Click`].
35//! + Dragging and dropping: [`DragStart`], [`Drag`], [`DragEnd`], [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
36//!
37//! When received by an observer, these events will always be wrapped by the [`Pointer`] type, which contains
38//! general metadata about the pointer event.
39
40use core::{fmt::Debug, time::Duration};
41
42use bevy_camera::NormalizedRenderTarget;
43use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal};
44use bevy_input::mouse::MouseScrollUnit;
45use bevy_math::Vec2;
46use bevy_platform::collections::HashMap;
47use bevy_platform::time::Instant;
48use bevy_reflect::prelude::*;
49use bevy_window::Window;
50use tracing::debug;
51
52use crate::{
53    backend::{prelude::PointerLocation, HitData},
54    hover::{HoverMap, PreviousHoverMap},
55    pointer::{Location, PointerAction, PointerButton, PointerId, PointerInput, PointerMap},
56};
57
58/// Stores the common data needed for all pointer events.
59///
60/// The documentation for the [`pointer_events`] explains the events this module exposes and
61/// the order in which they fire.
62#[derive(Message, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)]
63#[entity_event(propagate = PointerTraversal, auto_propagate)]
64#[reflect(Component, Debug, Clone)]
65pub struct Pointer<E: Debug + Clone + Reflect> {
66    /// The entity this pointer event happened for.
67    pub entity: Entity,
68    /// The pointer that triggered this event
69    pub pointer_id: PointerId,
70    /// The location of the pointer during this event
71    pub pointer_location: Location,
72    /// Additional event-specific data. [`DragDrop`] for example, has an additional field to describe
73    /// the `Entity` that is being dropped on the target.
74    pub event: E,
75}
76
77/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.
78///
79/// This will always traverse to the parent, if the entity being visited has one. Otherwise, it
80/// propagates to the pointer's window and stops there.
81#[derive(QueryData)]
82pub struct PointerTraversal {
83    child_of: Option<&'static ChildOf>,
84    window: Option<&'static Window>,
85}
86
87impl<E> Traversal<Pointer<E>> for PointerTraversal
88where
89    E: Debug + Clone + Reflect,
90{
91    fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer<E>) -> Option<Entity> {
92        let PointerTraversalItem { child_of, window } = item;
93
94        // Send event to parent, if it has one.
95        if let Some(child_of) = child_of {
96            return Some(child_of.parent());
97        };
98
99        // Otherwise, send it to the window entity (unless this is a window entity).
100        if window.is_none()
101            && let NormalizedRenderTarget::Window(window_ref) = pointer.pointer_location.target
102        {
103            return Some(window_ref.entity());
104        }
105
106        None
107    }
108}
109
110impl<E: Debug + Clone + Reflect> core::fmt::Display for Pointer<E> {
111    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112        f.write_fmt(format_args!(
113            "{:?}, {:.1?}, {:.1?}",
114            self.pointer_id, self.pointer_location.position, self.event
115        ))
116    }
117}
118
119impl<E: Debug + Clone + Reflect> core::ops::Deref for Pointer<E> {
120    type Target = E;
121
122    fn deref(&self) -> &Self::Target {
123        &self.event
124    }
125}
126
127impl<E: Debug + Clone + Reflect> Pointer<E> {
128    /// Construct a new `Pointer<E>` event.
129    pub fn new(id: PointerId, location: Location, event: E, entity: Entity) -> Self {
130        Self {
131            pointer_id: id,
132            pointer_location: location,
133            event,
134            entity,
135        }
136    }
137}
138
139/// Fires when a pointer is canceled, and its current interaction state is dropped.
140#[derive(Clone, PartialEq, Debug, Reflect)]
141#[reflect(Clone, PartialEq)]
142pub struct Cancel {
143    /// Information about the picking intersection.
144    pub hit: HitData,
145}
146
147/// Fires when a pointer crosses into the bounds of the [target entity](EntityEvent::event_target).
148#[derive(Clone, PartialEq, Debug, Reflect)]
149#[reflect(Clone, PartialEq)]
150pub struct Over {
151    /// Information about the picking intersection.
152    pub hit: HitData,
153}
154
155/// Fires when a pointer crosses out of the bounds of the [target entity](EntityEvent::event_target).
156#[derive(Clone, PartialEq, Debug, Reflect)]
157#[reflect(Clone, PartialEq)]
158pub struct Out {
159    /// Information about the latest prior picking intersection.
160    pub hit: HitData,
161}
162
163/// Fires when a pointer button is pressed over the [target entity](EntityEvent::event_target).
164#[derive(Clone, PartialEq, Debug, Reflect)]
165#[reflect(Clone, PartialEq)]
166pub struct Press {
167    /// Pointer button pressed to trigger this event.
168    pub button: PointerButton,
169    /// Information about the picking intersection.
170    pub hit: HitData,
171}
172
173/// Fires when a pointer button is released over the [target entity](EntityEvent::event_target).
174#[derive(Clone, PartialEq, Debug, Reflect)]
175#[reflect(Clone, PartialEq)]
176pub struct Release {
177    /// Pointer button lifted to trigger this event.
178    pub button: PointerButton,
179    /// Information about the picking intersection.
180    pub hit: HitData,
181}
182
183/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same
184/// [target entity](EntityEvent::event_target) for both events.
185#[derive(Clone, PartialEq, Debug, Reflect)]
186#[reflect(Clone, PartialEq)]
187pub struct Click {
188    /// Pointer button pressed and lifted to trigger this event.
189    pub button: PointerButton,
190    /// Information about the picking intersection.
191    pub hit: HitData,
192    /// Duration between the pointer pressed and lifted for this click
193    pub duration: Duration,
194}
195
196/// Fires while a pointer is moving over the [target entity](EntityEvent::event_target).
197#[derive(Clone, PartialEq, Debug, Reflect)]
198#[reflect(Clone, PartialEq)]
199pub struct Move {
200    /// Information about the picking intersection.
201    pub hit: HitData,
202    /// The change in position since the last move event.
203    ///
204    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
205    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
206    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
207    /// world-space.
208    pub delta: Vec2,
209}
210
211/// Fires when the [target entity](EntityEvent::event_target) receives a pointer pressed event followed by a pointer move event.
212#[derive(Clone, PartialEq, Debug, Reflect)]
213#[reflect(Clone, PartialEq)]
214pub struct DragStart {
215    /// Pointer button pressed and moved to trigger this event.
216    pub button: PointerButton,
217    /// Information about the picking intersection.
218    pub hit: HitData,
219}
220
221/// Fires while the [target entity](EntityEvent::event_target) is being dragged.
222#[derive(Clone, PartialEq, Debug, Reflect)]
223#[reflect(Clone, PartialEq)]
224pub struct Drag {
225    /// Pointer button pressed and moved to trigger this event.
226    pub button: PointerButton,
227    /// The total distance vector of a drag, measured from drag start to the current position.
228    ///
229    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
230    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
231    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
232    /// world-space.
233    pub distance: Vec2,
234    /// The change in position since the last drag event.
235    ///
236    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
237    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
238    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
239    /// world-space.
240    pub delta: Vec2,
241}
242
243/// Fires when a pointer is dragging the [target entity](EntityEvent::event_target) and a pointer released event is received.
244#[derive(Clone, PartialEq, Debug, Reflect)]
245#[reflect(Clone, PartialEq)]
246pub struct DragEnd {
247    /// Pointer button pressed, moved, and released to trigger this event.
248    pub button: PointerButton,
249    /// The vector of drag movement measured from start to final pointer position.
250    ///
251    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
252    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
253    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
254    /// world-space.
255    pub distance: Vec2,
256}
257
258/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target)
259#[derive(Clone, PartialEq, Debug, Reflect)]
260#[reflect(Clone, PartialEq)]
261pub struct DragEnter {
262    /// Pointer button pressed to enter drag.
263    pub button: PointerButton,
264    /// The entity that was being dragged when the pointer entered the [target entity](EntityEvent::event_target).
265    pub dragged: Entity,
266    /// Information about the picking intersection.
267    pub hit: HitData,
268}
269
270/// Fires while the `dragged` entity is being dragged over the [target entity](EntityEvent::event_target).
271#[derive(Clone, PartialEq, Debug, Reflect)]
272#[reflect(Clone, PartialEq)]
273pub struct DragOver {
274    /// Pointer button pressed while dragging over.
275    pub button: PointerButton,
276    /// The entity that was being dragged when the pointer was over the [target entity](EntityEvent::event_target).
277    pub dragged: Entity,
278    /// Information about the picking intersection.
279    pub hit: HitData,
280}
281
282/// Fires when a pointer dragging the `dragged` entity leaves the [target entity](EntityEvent::event_target).
283#[derive(Clone, PartialEq, Debug, Reflect)]
284#[reflect(Clone, PartialEq)]
285pub struct DragLeave {
286    /// Pointer button pressed while leaving drag.
287    pub button: PointerButton,
288    /// The entity that was being dragged when the pointer left the [target entity](EntityEvent::event_target).
289    pub dragged: Entity,
290    /// Information about the latest prior picking intersection.
291    pub hit: HitData,
292}
293
294/// Fires when a pointer drops the `dropped` entity onto the [target entity](EntityEvent::event_target).
295#[derive(Clone, PartialEq, Debug, Reflect)]
296#[reflect(Clone, PartialEq)]
297pub struct DragDrop {
298    /// Pointer button released to drop.
299    pub button: PointerButton,
300    /// The entity that was dropped onto the [target entity](EntityEvent::event_target).
301    pub dropped: Entity,
302    /// Information about the picking intersection.
303    pub hit: HitData,
304}
305
306/// Dragging state.
307#[derive(Clone, PartialEq, Debug, Reflect)]
308#[reflect(Clone, PartialEq)]
309pub struct DragEntry {
310    /// The position of the pointer at drag start.
311    ///
312    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
313    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
314    /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
315    /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
316    /// convert from screen-space to world-space.
317    pub start_pos: Vec2,
318    /// The latest position of the pointer during this drag, used to compute deltas.
319    ///
320    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
321    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
322    /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
323    /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
324    /// convert from screen-space to world-space.
325    pub latest_pos: Vec2,
326}
327
328/// Fires while a pointer is scrolling over the [target entity](EntityEvent::event_target).
329#[derive(Clone, PartialEq, Debug, Reflect)]
330#[reflect(Clone, PartialEq)]
331pub struct Scroll {
332    /// The mouse scroll unit.
333    pub unit: MouseScrollUnit,
334    /// The horizontal scroll value.
335    pub x: f32,
336    /// The vertical scroll value.
337    pub y: f32,
338    /// Information about the picking intersection.
339    pub hit: HitData,
340}
341
342/// An entry in the cache that drives the `pointer_events` system, storing additional data
343/// about pointer button presses.
344#[derive(Debug, Clone, Default)]
345pub struct PointerButtonState {
346    /// Stores the press location and start time for each button currently being pressed by the pointer.
347    pub pressing: HashMap<Entity, (Location, Instant, HitData)>,
348    /// Stores the starting and current locations for each entity currently being dragged by the pointer.
349    pub dragging: HashMap<Entity, DragEntry>,
350    /// Stores the hit data for each entity currently being dragged over by the pointer.
351    pub dragging_over: HashMap<Entity, HitData>,
352}
353
354impl PointerButtonState {
355    /// Clears all press and drag data tracked for this button on its pointer.
356    pub fn clear(&mut self) {
357        self.pressing.clear();
358        self.dragging.clear();
359        self.dragging_over.clear();
360    }
361}
362
363/// State for all pointers.
364#[derive(Debug, Clone, Default, Resource)]
365pub struct PointerState {
366    /// Pressing and dragging state, organized by pointer and button.
367    pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,
368}
369
370impl PointerState {
371    /// Retrieves the current state for a specific pointer and button, if it has been created.
372    pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {
373        self.pointer_buttons.get(&(pointer_id, button))
374    }
375
376    /// Provides write access to the state of a pointer and button, creating it if it does not yet exist.
377    pub fn get_mut(
378        &mut self,
379        pointer_id: PointerId,
380        button: PointerButton,
381    ) -> &mut PointerButtonState {
382        self.pointer_buttons
383            .entry((pointer_id, button))
384            .or_default()
385    }
386
387    /// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.
388    pub fn clear(&mut self, pointer_id: PointerId) {
389        for button in PointerButton::iter() {
390            if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {
391                state.clear();
392            }
393        }
394    }
395}
396
397/// A helper system param for accessing the picking event writers.
398#[derive(SystemParam)]
399pub struct PickingMessageWriters<'w> {
400    cancel_events: MessageWriter<'w, Pointer<Cancel>>,
401    click_events: MessageWriter<'w, Pointer<Click>>,
402    pressed_events: MessageWriter<'w, Pointer<Press>>,
403    drag_drop_events: MessageWriter<'w, Pointer<DragDrop>>,
404    drag_end_events: MessageWriter<'w, Pointer<DragEnd>>,
405    drag_enter_events: MessageWriter<'w, Pointer<DragEnter>>,
406    drag_events: MessageWriter<'w, Pointer<Drag>>,
407    drag_leave_events: MessageWriter<'w, Pointer<DragLeave>>,
408    drag_over_events: MessageWriter<'w, Pointer<DragOver>>,
409    drag_start_events: MessageWriter<'w, Pointer<DragStart>>,
410    scroll_events: MessageWriter<'w, Pointer<Scroll>>,
411    move_events: MessageWriter<'w, Pointer<Move>>,
412    out_events: MessageWriter<'w, Pointer<Out>>,
413    over_events: MessageWriter<'w, Pointer<Over>>,
414    released_events: MessageWriter<'w, Pointer<Release>>,
415}
416
417/// Dispatches interaction events to the target entities.
418///
419/// Within a single frame, events are dispatched in the following order:
420/// + [`Out`] → [`DragLeave`].
421/// + [`DragEnter`] → [`Over`].
422/// + Any number of any of the following:
423///   + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].
424///   + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].
425///   + For each pointer cancellation: [`Cancel`].
426///
427/// Additionally, across multiple frames, the following are also strictly
428/// ordered by the interaction state machine:
429/// + When a pointer moves over the target:
430///   [`Over`], [`Move`], [`Out`].
431/// + When a pointer presses buttons on the target:
432///   [`Press`], [`Click`], [`Release`].
433/// + When a pointer drags the target:
434///   [`DragStart`], [`Drag`], [`DragEnd`].
435/// + When a pointer drags something over the target:
436///   [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
437/// + When a pointer is canceled:
438///   No other events will follow the [`Cancel`] event for that pointer.
439///
440/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].
441/// The rest rely on additional data from the [`PointerInput`] event stream. To
442/// receive these events for a custom pointer, you must add [`PointerInput`]
443/// events.
444///
445/// When the pointer goes from hovering entity A to entity B, entity A will
446/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever
447/// receive both an [`Over`] and an [`Out`] event during the same frame.
448///
449/// When we account for event bubbling, this is no longer true. When the hovering focus shifts
450/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.
451/// In the context of UI, this is especially problematic. Additional hierarchy-aware
452/// events will be added in a future release.
453///
454/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,
455/// rather than the current frame. This is because touch pointers hover nothing
456/// on the frame they are released. The end effect is that these two events can
457/// be received sequentially after an [`Out`] event (but always on the same frame
458/// as the [`Out`] event).
459///
460/// Note: Though it is common for the [`PointerInput`] stream may contain
461/// multiple pointer movements and presses each frame, the hover state is
462/// determined only by the pointer's *final position*. Since the hover state
463/// ultimately determines which entities receive events, this may mean that an
464/// entity can receive events from before or after it was actually hovered.
465pub fn pointer_events(
466    // Input
467    mut input_events: MessageReader<PointerInput>,
468    // ECS State
469    pointers: Query<&PointerLocation>,
470    pointer_map: Res<PointerMap>,
471    hover_map: Res<HoverMap>,
472    previous_hover_map: Res<PreviousHoverMap>,
473    mut pointer_state: ResMut<PointerState>,
474    // Output
475    mut commands: Commands,
476    mut message_writers: PickingMessageWriters,
477) {
478    // Setup utilities
479    let now = Instant::now();
480    let pointer_location = |pointer_id: PointerId| {
481        pointer_map
482            .get_entity(pointer_id)
483            .and_then(|entity| pointers.get(entity).ok())
484            .and_then(|pointer| pointer.location.clone())
485    };
486
487    // If the entity was hovered by a specific pointer last frame...
488    for (pointer_id, hovered_entity, hit) in previous_hover_map
489        .iter()
490        .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
491    {
492        // ...but is now not being hovered by that same pointer...
493        if !hover_map
494            .get(&pointer_id)
495            .iter()
496            .any(|e| e.contains_key(&hovered_entity))
497        {
498            let Some(location) = pointer_location(pointer_id) else {
499                debug!(
500                    "Unable to get location for pointer {:?} during pointer out",
501                    pointer_id
502                );
503                continue;
504            };
505
506            // Always send Out events
507            let out_event = Pointer::new(
508                pointer_id,
509                location.clone(),
510                Out { hit: hit.clone() },
511                hovered_entity,
512            );
513            commands.trigger(out_event.clone());
514            message_writers.out_events.write(out_event);
515
516            // Possibly send DragLeave events
517            for button in PointerButton::iter() {
518                let state = pointer_state.get_mut(pointer_id, button);
519                state.dragging_over.remove(&hovered_entity);
520                for drag_target in state.dragging.keys() {
521                    let drag_leave_event = Pointer::new(
522                        pointer_id,
523                        location.clone(),
524                        DragLeave {
525                            button,
526                            dragged: *drag_target,
527                            hit: hit.clone(),
528                        },
529                        hovered_entity,
530                    );
531                    commands.trigger(drag_leave_event.clone());
532                    message_writers.drag_leave_events.write(drag_leave_event);
533                }
534            }
535        }
536    }
537
538    // Iterate all currently hovered entities for each pointer
539    for (pointer_id, hovered_entity, hit) in hover_map
540        .iter()
541        .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
542    {
543        // Continue if the pointer does not have a valid location.
544        let Some(location) = pointer_location(pointer_id) else {
545            debug!(
546                "Unable to get location for pointer {:?} during pointer over",
547                pointer_id
548            );
549            continue;
550        };
551
552        // For each button update its `dragging_over` state and possibly emit DragEnter events.
553        for button in PointerButton::iter() {
554            let state = pointer_state.get_mut(pointer_id, button);
555
556            // Only update the `dragging_over` state if there is at least one entity being dragged.
557            // Only emit DragEnter events for this `hovered_entity`, if it had no previous `dragging_over` state.
558            if !state.dragging.is_empty()
559                && state
560                    .dragging_over
561                    .insert(hovered_entity, hit.clone())
562                    .is_none()
563            {
564                for drag_target in state.dragging.keys() {
565                    let drag_enter_event = Pointer::new(
566                        pointer_id,
567                        location.clone(),
568                        DragEnter {
569                            button,
570                            dragged: *drag_target,
571                            hit: hit.clone(),
572                        },
573                        hovered_entity,
574                    );
575                    commands.trigger(drag_enter_event.clone());
576                    message_writers.drag_enter_events.write(drag_enter_event);
577                }
578            }
579        }
580
581        // Emit an Over event if the `hovered_entity` was not hovered by the same pointer the previous frame.
582        if !previous_hover_map
583            .get(&pointer_id)
584            .iter()
585            .any(|e| e.contains_key(&hovered_entity))
586        {
587            let over_event = Pointer::new(
588                pointer_id,
589                location.clone(),
590                Over { hit: hit.clone() },
591                hovered_entity,
592            );
593            commands.trigger(over_event.clone());
594            message_writers.over_events.write(over_event);
595        }
596    }
597
598    // Dispatch input events...
599    for PointerInput {
600        pointer_id,
601        location,
602        action,
603    } in input_events.read().cloned()
604    {
605        match action {
606            PointerAction::Press(button) => {
607                let state = pointer_state.get_mut(pointer_id, button);
608
609                // If it's a press, emit a Pressed event and mark the hovered entities as pressed
610                for (hovered_entity, hit) in hover_map
611                    .get(&pointer_id)
612                    .iter()
613                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
614                {
615                    let pressed_event = Pointer::new(
616                        pointer_id,
617                        location.clone(),
618                        Press {
619                            button,
620                            hit: hit.clone(),
621                        },
622                        hovered_entity,
623                    );
624                    commands.trigger(pressed_event.clone());
625                    message_writers.pressed_events.write(pressed_event);
626                    // Also insert the press into the state
627                    state
628                        .pressing
629                        .insert(hovered_entity, (location.clone(), now, hit));
630                }
631            }
632            PointerAction::Release(button) => {
633                let state = pointer_state.get_mut(pointer_id, button);
634
635                // Emit Click and Release events on all the previously hovered entities.
636                for (hovered_entity, hit) in previous_hover_map
637                    .get(&pointer_id)
638                    .iter()
639                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
640                {
641                    // If this pointer previously pressed the hovered entity, emit a Click event
642                    if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {
643                        let click_event = Pointer::new(
644                            pointer_id,
645                            location.clone(),
646                            Click {
647                                button,
648                                hit: hit.clone(),
649                                duration: now - *press_instant,
650                            },
651                            hovered_entity,
652                        );
653                        commands.trigger(click_event.clone());
654                        message_writers.click_events.write(click_event);
655                    }
656                    // Always send the Release event
657                    let released_event = Pointer::new(
658                        pointer_id,
659                        location.clone(),
660                        Release {
661                            button,
662                            hit: hit.clone(),
663                        },
664                        hovered_entity,
665                    );
666                    commands.trigger(released_event.clone());
667                    message_writers.released_events.write(released_event);
668                }
669
670                // Then emit the drop events.
671                for (drag_target, drag) in state.dragging.drain() {
672                    // Emit DragDrop
673                    for (dragged_over, hit) in state.dragging_over.iter() {
674                        let drag_drop_event = Pointer::new(
675                            pointer_id,
676                            location.clone(),
677                            DragDrop {
678                                button,
679                                dropped: drag_target,
680                                hit: hit.clone(),
681                            },
682                            *dragged_over,
683                        );
684                        commands.trigger(drag_drop_event.clone());
685                        message_writers.drag_drop_events.write(drag_drop_event);
686                    }
687                    // Emit DragEnd
688                    let drag_end_event = Pointer::new(
689                        pointer_id,
690                        location.clone(),
691                        DragEnd {
692                            button,
693                            distance: drag.latest_pos - drag.start_pos,
694                        },
695                        drag_target,
696                    );
697                    commands.trigger(drag_end_event.clone());
698                    message_writers.drag_end_events.write(drag_end_event);
699                    // Emit DragLeave
700                    for (dragged_over, hit) in state.dragging_over.iter() {
701                        let drag_leave_event = Pointer::new(
702                            pointer_id,
703                            location.clone(),
704                            DragLeave {
705                                button,
706                                dragged: drag_target,
707                                hit: hit.clone(),
708                            },
709                            *dragged_over,
710                        );
711                        commands.trigger(drag_leave_event.clone());
712                        message_writers.drag_leave_events.write(drag_leave_event);
713                    }
714                }
715
716                // Finally, we can clear the state of everything relating to presses or drags.
717                state.clear();
718            }
719            // Moved
720            PointerAction::Move { delta } => {
721                if delta == Vec2::ZERO {
722                    continue; // If delta is zero, the following events will not be triggered.
723                }
724                // Triggers during movement even if not over an entity
725                for button in PointerButton::iter() {
726                    let state = pointer_state.get_mut(pointer_id, button);
727
728                    // Emit DragEntry and DragStart the first time we move while pressing an entity
729                    for (press_target, (location, _, hit)) in state.pressing.iter() {
730                        if state.dragging.contains_key(press_target) {
731                            continue; // This entity is already logged as being dragged
732                        }
733                        state.dragging.insert(
734                            *press_target,
735                            DragEntry {
736                                start_pos: location.position,
737                                latest_pos: location.position,
738                            },
739                        );
740                        let drag_start_event = Pointer::new(
741                            pointer_id,
742                            location.clone(),
743                            DragStart {
744                                button,
745                                hit: hit.clone(),
746                            },
747                            *press_target,
748                        );
749
750                        commands.trigger(drag_start_event.clone());
751                        message_writers.drag_start_events.write(drag_start_event);
752
753                        // Insert dragging over state and emit DragEnter for hovered entities.
754                        for (hovered_entity, hit) in hover_map
755                            .get(&pointer_id)
756                            .iter()
757                            .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
758                            .filter(|(hovered_entity, _)| *hovered_entity != *press_target)
759                        {
760                            // Inserting the `dragging_over` state here ensures the `DragEnter` event won't be dispatched twice.
761                            state.dragging_over.insert(hovered_entity, hit.clone());
762                            let drag_enter_event = Pointer::new(
763                                pointer_id,
764                                location.clone(),
765                                DragEnter {
766                                    button,
767                                    dragged: *press_target,
768                                    hit: hit.clone(),
769                                },
770                                hovered_entity,
771                            );
772                            commands.trigger(drag_enter_event.clone());
773                            message_writers.drag_enter_events.write(drag_enter_event);
774                        }
775                    }
776
777                    // Emit Drag events to the entities we are dragging
778                    for (drag_target, drag) in state.dragging.iter_mut() {
779                        let delta = location.position - drag.latest_pos;
780                        if delta == Vec2::ZERO {
781                            continue; // No need to emit a Drag event if there is no movement
782                        }
783                        let drag_event = Pointer::new(
784                            pointer_id,
785                            location.clone(),
786                            Drag {
787                                button,
788                                distance: location.position - drag.start_pos,
789                                delta,
790                            },
791                            *drag_target,
792                        );
793                        commands.trigger(drag_event.clone());
794                        message_writers.drag_events.write(drag_event);
795
796                        // Update drag position
797                        drag.latest_pos = location.position;
798
799                        // Emit corresponding DragOver to the hovered entities
800                        for (hovered_entity, hit) in hover_map
801                            .get(&pointer_id)
802                            .iter()
803                            .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
804                            .filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
805                        {
806                            let drag_over_event = Pointer::new(
807                                pointer_id,
808                                location.clone(),
809                                DragOver {
810                                    button,
811                                    dragged: *drag_target,
812                                    hit: hit.clone(),
813                                },
814                                hovered_entity,
815                            );
816                            commands.trigger(drag_over_event.clone());
817                            message_writers.drag_over_events.write(drag_over_event);
818                        }
819                    }
820                }
821
822                for (hovered_entity, hit) in hover_map
823                    .get(&pointer_id)
824                    .iter()
825                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
826                {
827                    // Emit Move events to the entities we are hovering
828                    let move_event = Pointer::new(
829                        pointer_id,
830                        location.clone(),
831                        Move {
832                            hit: hit.clone(),
833                            delta,
834                        },
835                        hovered_entity,
836                    );
837                    commands.trigger(move_event.clone());
838                    message_writers.move_events.write(move_event);
839                }
840            }
841            PointerAction::Scroll { x, y, unit } => {
842                for (hovered_entity, hit) in hover_map
843                    .get(&pointer_id)
844                    .iter()
845                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
846                {
847                    // Emit Scroll events to the entities we are hovering
848                    let scroll_event = Pointer::new(
849                        pointer_id,
850                        location.clone(),
851                        Scroll {
852                            unit,
853                            x,
854                            y,
855                            hit: hit.clone(),
856                        },
857                        hovered_entity,
858                    );
859                    commands.trigger(scroll_event.clone());
860                    message_writers.scroll_events.write(scroll_event);
861                }
862            }
863            // Canceled
864            PointerAction::Cancel => {
865                // Emit a Cancel to the hovered entity.
866                for (hovered_entity, hit) in hover_map
867                    .get(&pointer_id)
868                    .iter()
869                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
870                {
871                    let cancel_event =
872                        Pointer::new(pointer_id, location.clone(), Cancel { hit }, hovered_entity);
873                    commands.trigger(cancel_event.clone());
874                    message_writers.cancel_events.write(cancel_event);
875                }
876                // Clear the state for the canceled pointer
877                pointer_state.clear(pointer_id);
878            }
879        }
880    }
881}