Skip to main content

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`], [`Enter`], [`Move`], [`Leave`], 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};
41use std::collections::HashSet;
42
43use bevy_camera::NormalizedRenderTarget;
44use bevy_derive::{Deref, DerefMut};
45use bevy_ecs::{
46    entity::{EntityHashMap, EntityHashSet},
47    prelude::*,
48    query::QueryData,
49    system::SystemParam,
50    traversal::Traversal,
51};
52use bevy_input::{mouse::MouseScrollUnit, touch::TouchPhase};
53use bevy_math::Vec2;
54use bevy_platform::collections::HashMap;
55use bevy_platform::time::Instant;
56use bevy_reflect::prelude::*;
57use bevy_window::Window;
58use tracing::debug;
59
60use crate::{
61    backend::{prelude::PointerLocation, HitData},
62    hover::{get_hovered_entities, is_directly_hovered, HoverMap, PreviousHoverMap},
63    pointer::{Location, PointerAction, PointerButton, PointerId, PointerInput, PointerMap},
64    PickingSettings,
65};
66
67/// Stores the common data needed for all pointer events.
68///
69/// The documentation for the [`pointer_events`] explains the events this module exposes and
70/// the order in which they fire.
71#[derive(Message, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)]
72#[entity_event(propagate = PointerTraversal, auto_propagate)]
73#[reflect(Component, Debug, Clone)]
74pub struct Pointer<E: Debug + Clone + Reflect> {
75    /// The entity this pointer event happened for.
76    pub entity: Entity,
77    /// The pointer that triggered this event
78    pub pointer_id: PointerId,
79    /// The location of the pointer during this event
80    pub pointer_location: Location,
81    /// Additional event-specific data. [`DragDrop`] for example, has an additional field to describe
82    /// the `Entity` that is being dropped on the target.
83    pub event: E,
84    /// Whether to propagate the event via `PointerTraversal`
85    /// For [`Enter`] and [`Leave`] events, this is set to false.
86    pub(crate) propagate: bool,
87}
88
89/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.
90///
91/// Unless shortcircuited out by the [`Pointer`] event itself, this will always traverse to the
92/// parent if the entity being visited has one. Otherwise, it propagates to the pointer's
93/// window and stops there.
94#[derive(QueryData)]
95pub struct PointerTraversal {
96    child_of: Option<&'static ChildOf>,
97    window: Option<&'static Window>,
98}
99
100impl<E> Traversal<Pointer<E>> for PointerTraversal
101where
102    E: Debug + Clone + Reflect,
103{
104    fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer<E>) -> Option<Entity> {
105        if !pointer.propagate {
106            return None;
107        }
108
109        let PointerTraversalItem { child_of, window } = item;
110
111        // Send event to parent, if it has one.
112        if let Some(child_of) = child_of {
113            return Some(child_of.parent());
114        };
115
116        // Otherwise, send it to the window entity (unless this is a window entity).
117        if window.is_none()
118            && let NormalizedRenderTarget::Window(window_ref) = pointer.pointer_location.target
119        {
120            return Some(window_ref.entity());
121        }
122
123        None
124    }
125}
126
127impl<E: Debug + Clone + Reflect> core::fmt::Display for Pointer<E> {
128    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
129        f.write_fmt(format_args!(
130            "{:?}, {:.1?}, {:.1?}",
131            self.pointer_id, self.pointer_location.position, self.event
132        ))
133    }
134}
135
136impl<E: Debug + Clone + Reflect> core::ops::Deref for Pointer<E> {
137    type Target = E;
138
139    fn deref(&self) -> &Self::Target {
140        &self.event
141    }
142}
143
144impl<E: Debug + Clone + Reflect> Pointer<E> {
145    /// Construct a new `Pointer<E>` event that propagates
146    pub fn new(id: PointerId, location: Location, event: E, entity: Entity) -> Self {
147        Self::new_inner(id, location, event, entity, true)
148    }
149
150    /// Construct a new `Pointer<E>` event that does not propagate
151    pub fn new_without_propagate(
152        id: PointerId,
153        location: Location,
154        event: E,
155        entity: Entity,
156    ) -> Self {
157        Self::new_inner(id, location, event, entity, false)
158    }
159
160    fn new_inner(
161        id: PointerId,
162        location: Location,
163        event: E,
164        entity: Entity,
165        propagate: bool,
166    ) -> Self {
167        Self {
168            pointer_id: id,
169            pointer_location: location,
170            event,
171            entity,
172            propagate,
173        }
174    }
175}
176
177/// Fires when a pointer is canceled, and its current interaction state is dropped.
178#[derive(Clone, PartialEq, Debug, Reflect)]
179#[reflect(Clone, PartialEq)]
180pub struct Cancel {
181    /// Information about the picking intersection.
182    pub hit: HitData,
183}
184
185/// Fires when a pointer crosses into the bounds of a [target entity](EntityEvent::event_target).
186/// Unlike [`Enter`], this event bubbles up to all of the
187/// [target entity's](EntityEvent::event_target) ancestors (traversed via the [`ChildOf`] relationship)
188/// without restriction. Refer to [`pointer_events`] for more information on how these events are triggered.
189/// Refer to [`PointerTraversal`] for how [`Pointer`] events are propagated.
190#[derive(Clone, PartialEq, Debug, Reflect)]
191#[reflect(Clone, PartialEq)]
192pub struct Over {
193    /// Information about the picking intersection.
194    pub hit: HitData,
195}
196
197/// Fires when a pointer crosses into the bounds of a [target entity](EntityEvent::event_target).
198/// Unlike [`Over`], this event bubbles up through a subset of the
199/// [target entity's](EntityEvent::event_target) ancestors
200/// (traversed via the [`ChildOf`] relationship).
201///
202/// ### Event Propagation
203/// An ancestor of a [target entity](EntityEvent::event_target) will receive an [`Enter`] event
204/// when the ancestor does not have a direct relation to any entity hovered by the
205/// pointer in the previous frame. For example, for a given pointer:
206///
207/// If the previously hovered entity C has the following entity ancestry: A -> B -> C
208///
209/// And the currently hovered entity E has the following entity ancestry: A -> D -> E
210///
211/// [`Enter`] events would be sent for both E and its direct ancestor D.
212/// An [`Enter`] event would not be sent for A because it is a shared ancestor of both C and E.
213///
214/// Note: An [`Enter`] event may be fired for an ancestor even if the pointer does not enter
215/// within the ancestor's bounds. More concretely, if a child's bounds extend beyond the parent's
216/// and the pointer enters the child's bounds without crossing into the parent's,
217/// two [`Enter`] events are still emitted for both the child and the parent.
218/// This matches the triggering behavior of `mouseenter` events on the web.
219/// To find out whether a pointer is within the target entity's bounds
220/// immediately upon entering, check the value of [`is_in_bounds`](Enter::is_in_bounds).
221///
222/// Refer to [`pointer_events`] for more information on how these events are triggered.
223#[derive(Clone, PartialEq, Debug, Reflect)]
224#[reflect(Clone, PartialEq)]
225pub struct Enter {
226    /// Information about the picking intersection.
227    pub hit: HitData,
228    /// Whether this pointer directly entered into the target entity's bounds at the
229    /// time of the event.
230    /// This may be false if this entity's child's bounds extended beyond the entity and
231    /// the pointer entered within the child's bounds only.
232    pub is_in_bounds: bool,
233}
234
235/// Fires when a pointer crosses out of the bounds of a [target entity](EntityEvent::event_target).
236/// Unlike [`Leave`], this event bubbles up to all of the
237/// [target entity's](EntityEvent::event_target) ancestors (traversed via the [`ChildOf`] relationship)
238/// without restriction. Refer to [`pointer_events`] for more information on how these events are triggered.
239/// Refer to [`PointerTraversal`] for how [`Pointer`] events are propagated.
240#[derive(Clone, PartialEq, Debug, Reflect)]
241#[reflect(Clone, PartialEq)]
242pub struct Out {
243    /// Information about the latest prior picking intersection.
244    pub hit: HitData,
245}
246
247/// Fires when a pointer crosses out of the bounds of a [target entity](EntityEvent::event_target).
248/// Unlike [`Out`], this event bubbles up through a subset of the
249/// [target entity's](EntityEvent::event_target) ancestors
250/// (traversed via the [`ChildOf`] relationship).
251///
252/// ### Event Propagation
253/// An ancestor of a [target entity](EntityEvent::event_target) will receive a [`Leave`] event
254/// when the ancestor does not have a direct relation to any entity hovered by the
255/// pointer in the current frame. For example, for a given pointer:
256///
257/// If the previously hovered entity C has the following entity ancestry: A -> B -> C
258///
259/// And the currently hovered entity E has the following entity ancestry: A -> D -> E
260///
261/// [`Leave`] events would be sent for both C and its direct ancestor B.
262/// A [`Leave`] event would not be sent for A because it is a shared ancestor of both C and E.
263///
264/// Note: A [`Leave`] event may be fired for an ancestor even if the pointer does not leave
265/// the ancestor's bounds. More concretely, if a child's bounds extend beyond the parent's
266/// and the pointer leaves from within those extended bounds,
267/// two [`Leave`] events are still emitted for both the child and the parent.
268/// This matches the triggering behavior of `mouseleave` events on the web.
269/// To find out whether the pointer was within the target entity's bounds
270/// right before leaving, check the value of [`was_in_bounds`](Leave::was_in_bounds).
271///
272/// Refer to [`pointer_events`] for more information on how these events are triggered.
273#[derive(Clone, PartialEq, Debug, Reflect)]
274#[reflect(Clone, PartialEq)]
275pub struct Leave {
276    /// Information about the latest prior picking intersection.
277    pub hit: HitData,
278    /// Whether this pointer directly exited out of the target entity's bounds
279    /// at the time of the event.
280    /// This may be false if this entity's child's bounds extended beyond the entity and
281    /// the pointer exited out of the child's bounds only.
282    pub was_in_bounds: bool,
283}
284
285/// Fires when a pointer button is pressed over the [target entity](EntityEvent::event_target).
286#[derive(Clone, PartialEq, Debug, Reflect)]
287#[reflect(Clone, PartialEq)]
288pub struct Press {
289    /// Pointer button pressed to trigger this event.
290    pub button: PointerButton,
291    /// Information about the picking intersection.
292    pub hit: HitData,
293    /// Number of consecutive presses, starting at `1`.
294    pub count: u8,
295}
296
297/// Fires when a pointer button is released over the [target entity](EntityEvent::event_target).
298#[derive(Clone, PartialEq, Debug, Reflect)]
299#[reflect(Clone, PartialEq)]
300pub struct Release {
301    /// Pointer button lifted to trigger this event.
302    pub button: PointerButton,
303    /// Information about the picking intersection.
304    pub hit: HitData,
305}
306
307/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same
308/// [target entity](EntityEvent::event_target) for both events.
309#[derive(Clone, PartialEq, Debug, Reflect)]
310#[reflect(Clone, PartialEq)]
311pub struct Click {
312    /// Pointer button pressed and lifted to trigger this event.
313    pub button: PointerButton,
314    /// Information about the picking intersection.
315    pub hit: HitData,
316    /// Duration between the pointer pressed and lifted for this click
317    pub duration: Duration,
318    /// Number of consecutive clicks, starting at `1`.
319    pub count: u8,
320}
321
322/// Fires while a pointer is moving over the [target entity](EntityEvent::event_target).
323#[derive(Clone, PartialEq, Debug, Reflect)]
324#[reflect(Clone, PartialEq)]
325pub struct Move {
326    /// Information about the picking intersection.
327    pub hit: HitData,
328    /// The change in position since the last move event.
329    ///
330    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
331    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
332    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
333    /// world-space.
334    pub delta: Vec2,
335}
336
337/// Fires when the [target entity](EntityEvent::event_target) receives a pointer pressed event followed by a pointer move event.
338#[derive(Clone, PartialEq, Debug, Reflect)]
339#[reflect(Clone, PartialEq)]
340pub struct DragStart {
341    /// Pointer button pressed and moved to trigger this event.
342    pub button: PointerButton,
343    /// Information about the picking intersection.
344    pub hit: HitData,
345}
346
347/// Fires while the [target entity](EntityEvent::event_target) is being dragged.
348#[derive(Clone, PartialEq, Debug, Reflect)]
349#[reflect(Clone, PartialEq)]
350pub struct Drag {
351    /// Pointer button pressed and moved to trigger this event.
352    pub button: PointerButton,
353    /// The total distance vector of a drag, measured from drag start to the current position.
354    ///
355    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
356    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
357    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
358    /// world-space.
359    pub distance: Vec2,
360    /// The change in position since the last drag event.
361    ///
362    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
363    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
364    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
365    /// world-space.
366    pub delta: Vec2,
367}
368
369/// Fires when a pointer is dragging the [target entity](EntityEvent::event_target) and a pointer released event is received.
370#[derive(Clone, PartialEq, Debug, Reflect)]
371#[reflect(Clone, PartialEq)]
372pub struct DragEnd {
373    /// Pointer button pressed, moved, and released to trigger this event.
374    pub button: PointerButton,
375    /// The vector of drag movement measured from start to final pointer position.
376    ///
377    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
378    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
379    /// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to
380    /// world-space.
381    pub distance: Vec2,
382}
383
384/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target)
385#[derive(Clone, PartialEq, Debug, Reflect)]
386#[reflect(Clone, PartialEq)]
387pub struct DragEnter {
388    /// Pointer button pressed to enter drag.
389    pub button: PointerButton,
390    /// The entity that was being dragged when the pointer entered the [target entity](EntityEvent::event_target).
391    pub dragged: Entity,
392    /// Information about the picking intersection.
393    pub hit: HitData,
394}
395
396/// Fires while the `dragged` entity is being dragged over the [target entity](EntityEvent::event_target).
397#[derive(Clone, PartialEq, Debug, Reflect)]
398#[reflect(Clone, PartialEq)]
399pub struct DragOver {
400    /// Pointer button pressed while dragging over.
401    pub button: PointerButton,
402    /// The entity that was being dragged when the pointer was over the [target entity](EntityEvent::event_target).
403    pub dragged: Entity,
404    /// Information about the picking intersection.
405    pub hit: HitData,
406}
407
408/// Fires when a pointer dragging the `dragged` entity leaves the [target entity](EntityEvent::event_target).
409#[derive(Clone, PartialEq, Debug, Reflect)]
410#[reflect(Clone, PartialEq)]
411pub struct DragLeave {
412    /// Pointer button pressed while leaving drag.
413    pub button: PointerButton,
414    /// The entity that was being dragged when the pointer left the [target entity](EntityEvent::event_target).
415    pub dragged: Entity,
416    /// Information about the latest prior picking intersection.
417    pub hit: HitData,
418}
419
420/// Fires when a pointer drops the `dropped` entity onto the [target entity](EntityEvent::event_target).
421#[derive(Clone, PartialEq, Debug, Reflect)]
422#[reflect(Clone, PartialEq)]
423pub struct DragDrop {
424    /// Pointer button released to drop.
425    pub button: PointerButton,
426    /// The entity that was dropped onto the [target entity](EntityEvent::event_target).
427    pub dropped: Entity,
428    /// Information about the picking intersection.
429    pub hit: HitData,
430}
431
432/// Dragging state.
433#[derive(Clone, PartialEq, Debug, Reflect)]
434#[reflect(Clone, PartialEq)]
435pub struct DragEntry {
436    /// The position of the pointer at drag start.
437    ///
438    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
439    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
440    /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
441    /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
442    /// convert from screen-space to world-space.
443    pub start_pos: Vec2,
444    /// The latest position of the pointer during this drag, used to compute deltas.
445    ///
446    /// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to
447    /// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider
448    /// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or
449    /// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to
450    /// convert from screen-space to world-space.
451    pub latest_pos: Vec2,
452}
453
454/// Fires while a pointer is scrolling over the [target entity](EntityEvent::event_target).
455#[derive(Clone, PartialEq, Debug, Reflect)]
456#[reflect(Clone, PartialEq)]
457pub struct Scroll {
458    /// The mouse scroll unit.
459    pub unit: MouseScrollUnit,
460    /// The horizontal scroll value.
461    pub x: f32,
462    /// The vertical scroll value.
463    pub y: f32,
464    /// Information about the picking intersection.
465    pub hit: HitData,
466    /// Touch phase of the input.
467    ///
468    /// When using a mouse, this will always be [`TouchPhase::Moved`].
469    pub phase: TouchPhase,
470}
471
472/// An entry in the cache that drives the `pointer_events` system, storing additional data
473/// about pointer button presses.
474#[derive(Debug, Clone, Default)]
475pub struct PointerButtonState {
476    /// Stores the press location and start time for each button currently being pressed by the pointer.
477    pub pressing: EntityHashMap<(Location, Instant, HitData)>,
478    /// Stores the latest click time and count for each clicked entity.
479    pub clicking: EntityHashMap<(Instant, u8)>,
480    /// Stores the starting and current locations for each entity currently being dragged by the pointer.
481    pub dragging: EntityHashMap<DragEntry>,
482    /// Stores the hit data for each entity currently being dragged over by the pointer.
483    pub dragging_over: EntityHashMap<HitData>,
484}
485
486impl PointerButtonState {
487    /// Clears all press and drag data tracked for this button on its pointer.
488    pub fn clear(&mut self) {
489        self.pressing.clear();
490        self.dragging.clear();
491        self.dragging_over.clear();
492    }
493}
494
495/// A cache map containing the ancestry of hovered entities
496#[derive(Debug, Clone, Default, Deref, DerefMut)]
497pub struct HoveredEntityAncestors(EntityHashMap<EntityHashSet>);
498
499impl HoveredEntityAncestors {
500    /// Clears self and rebuilds a map of every hovered entity to its ancestors.
501    ///
502    /// This map is used to calculate which entities should receive [`Enter`] or [`Leave`] events.
503    pub fn rebuild(
504        &mut self,
505        hover_map: &HoverMap,
506        pointer_state: &PointerState,
507        ancestors_query: &Query<&ChildOf>,
508    ) {
509        self.clear();
510        for hovered_entity in hover_map
511            .iter()
512            .flat_map(|(_, hashmap)| hashmap.iter().map(|data| *data.0))
513        {
514            // If the ancestors were already added into the map, do not re-fetch
515            if self.contains_key(&hovered_entity) {
516                continue;
517            }
518            // If the ancestors were previously fetched, just re-use the entry.
519            if let Some(previous_entry) =
520                pointer_state.hovered_entity_ancestors.get(&hovered_entity)
521            {
522                self.insert(hovered_entity, previous_entry.clone());
523            } else {
524                let mut ancestors = EntityHashSet::new();
525                for member in ancestors_query.iter_ancestors(hovered_entity) {
526                    ancestors.insert(member);
527                }
528                self.insert(hovered_entity, ancestors);
529            }
530        }
531    }
532
533    /// Returns a new combined `HashSet` of ancestors for the provided `hover_entities`
534    pub fn get_ancestors_union(&self, hover_entities: &EntityHashSet) -> EntityHashSet {
535        hover_entities
536            .iter()
537            .flat_map(|entity| self.get(entity))
538            .flat_map(|set| set.iter().copied())
539            .collect::<EntityHashSet>()
540    }
541
542    /// Returns the ancestors for the provided `hover_entity`, if it has been created
543    pub fn get_ancestors(&self, hover_entity: &Entity) -> Option<&EntityHashSet> {
544        self.get(hover_entity)
545    }
546}
547
548/// State for all pointers.
549#[derive(Debug, Clone, Default, Resource)]
550pub struct PointerState {
551    /// Pressing and dragging state, organized by pointer and button.
552    pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,
553    /// A cache map providing the set of an entity's ancestors for a given hovered entity.
554    pub hovered_entity_ancestors: HoveredEntityAncestors,
555}
556
557impl PointerState {
558    /// Retrieves the current state for a specific pointer and button, if it has been created.
559    pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {
560        self.pointer_buttons.get(&(pointer_id, button))
561    }
562
563    /// Provides write access to the state of a pointer and button, creating it if it does not yet exist.
564    pub fn get_mut(
565        &mut self,
566        pointer_id: PointerId,
567        button: PointerButton,
568    ) -> &mut PointerButtonState {
569        self.pointer_buttons
570            .entry((pointer_id, button))
571            .or_default()
572    }
573
574    /// Retrieves the ancestors for a given hovered entity
575    pub fn get_ancestors(&self, hovered_entity: &Entity) -> Option<&EntityHashSet> {
576        self.hovered_entity_ancestors.get_ancestors(hovered_entity)
577    }
578
579    /// Retrieves the union of ancestors for the given hovered entities
580    pub fn get_ancestors_union(&self, hovered_entities: &EntityHashSet) -> EntityHashSet {
581        self.hovered_entity_ancestors
582            .get_ancestors_union(hovered_entities)
583    }
584
585    /// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.
586    pub fn clear(&mut self, pointer_id: PointerId) {
587        for button in PointerButton::iter() {
588            if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {
589                state.clear();
590            }
591        }
592    }
593}
594
595/// A helper system param for accessing the picking event writers.
596#[derive(SystemParam)]
597pub struct PickingMessageWriters<'w> {
598    cancel_events: MessageWriter<'w, Pointer<Cancel>>,
599    click_events: MessageWriter<'w, Pointer<Click>>,
600    pressed_events: MessageWriter<'w, Pointer<Press>>,
601    drag_drop_events: MessageWriter<'w, Pointer<DragDrop>>,
602    drag_end_events: MessageWriter<'w, Pointer<DragEnd>>,
603    drag_enter_events: MessageWriter<'w, Pointer<DragEnter>>,
604    drag_events: MessageWriter<'w, Pointer<Drag>>,
605    drag_leave_events: MessageWriter<'w, Pointer<DragLeave>>,
606    drag_over_events: MessageWriter<'w, Pointer<DragOver>>,
607    drag_start_events: MessageWriter<'w, Pointer<DragStart>>,
608    scroll_events: MessageWriter<'w, Pointer<Scroll>>,
609    move_events: MessageWriter<'w, Pointer<Move>>,
610    out_events: MessageWriter<'w, Pointer<Out>>,
611    over_events: MessageWriter<'w, Pointer<Over>>,
612    leave_events: MessageWriter<'w, Pointer<Leave>>,
613    enter_events: MessageWriter<'w, Pointer<Enter>>,
614    released_events: MessageWriter<'w, Pointer<Release>>,
615}
616
617/// Dispatches interaction events to the target entities.
618///
619/// Within a single frame, events are dispatched in the following order:
620/// + [`Out`] → [`Leave`] → [`DragLeave`].
621/// + [`DragEnter`] → [`Enter`] → [`Over`].
622/// + Any number of any of the following:
623///   + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].
624///   + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].
625///   + For each pointer cancellation: [`Cancel`].
626///
627/// Additionally, across multiple frames, the following are also strictly
628/// ordered by the interaction state machine:
629/// + When a pointer moves over the target:
630///   [`Over`], [`Enter`], [`Move`], [`Leave`], [`Out`].
631/// + When a pointer presses buttons on the target:
632///   [`Press`], [`Click`], [`Release`].
633/// + When a pointer drags the target:
634///   [`DragStart`], [`Drag`], [`DragEnd`].
635/// + When a pointer drags something over the target:
636///   [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
637/// + When a pointer is canceled:
638///   No other events will follow the [`Cancel`] event for that pointer.
639///
640/// Four events -- [`Over`], [`Enter`], [`Leave`] and [`Out`] -- are driven only by the [`HoverMap`].
641/// The rest rely on additional data from the [`PointerInput`] event stream. To
642/// receive these events for a custom pointer, you must add [`PointerInput`]
643/// events.
644///
645/// When the pointer goes from hovering entity A to entity B, entity A will
646/// receive [`Out`] and [`Enter`] and then entity B will receive [`Leave`] and [`Over`].
647/// No entity will ever receive both an [`Over`] and an [`Out`] or
648/// an [`Enter`] and a [`Leave`] event during the same frame.
649///
650/// When we account for event bubbling, the two pairs of events,
651/// [`Out`] [`Over`] and [`Enter`] [`Leave`], behave differently. When the hovering focus shifts
652/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs. In
653/// the case of [`Enter`] → [`Leave`], shared parent entities will not receive [`Enter`]
654/// or [`Leave`].
655///
656/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,
657/// rather than the current frame. This is because touch pointers hover nothing
658/// on the frame they are released. The end effect is that these two events can
659/// be received sequentially after an [`Out`] event (but always on the same frame
660/// as the [`Out`] event).
661///
662/// Note: Though it is common for the [`PointerInput`] stream may contain
663/// multiple pointer movements and presses each frame, the hover state is
664/// determined only by the pointer's *final position*. Since the hover state
665/// ultimately determines which entities receive events, this may mean that an
666/// entity can receive events from before or after it was actually hovered.
667pub fn pointer_events(
668    // Input
669    mut input_events: MessageReader<PointerInput>,
670    // ECS State
671    pointers: Query<&PointerLocation>,
672    ancestors_query: Query<&ChildOf>,
673    pointer_map: Res<PointerMap>,
674    hover_map: Res<HoverMap>,
675    previous_hover_map: Res<PreviousHoverMap>,
676    picking_settings: Res<PickingSettings>,
677    mut pointer_state: ResMut<PointerState>,
678    mut hovered_entity_ancestors: Local<HoveredEntityAncestors>,
679    mut sent_leave: Local<HashSet<(PointerId, Entity)>>,
680    mut sent_enter: Local<HashSet<(PointerId, Entity)>>,
681    // Output
682    mut commands: Commands,
683    mut message_writers: PickingMessageWriters,
684) {
685    // Setup utilities
686    let now = Instant::now();
687    let pointer_location = |pointer_id: PointerId| {
688        pointer_map
689            .get_entity(pointer_id)
690            .and_then(|entity| pointers.get(entity).ok())
691            .and_then(|pointer| pointer.location.clone())
692    };
693    hovered_entity_ancestors.rebuild(&hover_map, &pointer_state, &ancestors_query);
694    sent_leave.clear();
695    sent_enter.clear();
696
697    // If the entity was hovered by a specific pointer last frame...
698    for (pointer_id, hovered_entity, hit) in previous_hover_map
699        .iter()
700        .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
701    {
702        // ...but is now not being hovered by that same pointer...
703        if !hover_map
704            .get(&pointer_id)
705            .iter()
706            .any(|e| e.contains_key(&hovered_entity))
707        {
708            let Some(location) = pointer_location(pointer_id) else {
709                debug!(
710                    "Unable to get location for pointer {:?} during pointer out",
711                    pointer_id
712                );
713                continue;
714            };
715
716            // Always send Out events
717            let out_event = Pointer::new(
718                pointer_id,
719                location.clone(),
720                Out { hit: hit.clone() },
721                hovered_entity,
722            );
723            commands.trigger(out_event.clone());
724            message_writers.out_events.write(out_event);
725
726            // Potentially send `Leave` events to the entity and all of its ancestors
727            let mut entities_to_send_leave =
728                pointer_state.get_ancestors(&hovered_entity).map_or_else(
729                    || {
730                        ancestors_query
731                            .iter_ancestors(hovered_entity)
732                            .collect::<EntityHashSet>()
733                    },
734                    Clone::clone,
735                );
736            entities_to_send_leave.insert(hovered_entity);
737            // Ensure we do not double send to any other entities that have already been sent to during this loop
738            entities_to_send_leave.retain(|entity| !sent_leave.contains(&(pointer_id, *entity)));
739            if !entities_to_send_leave.is_empty() {
740                // Fetch the currently hovered entities and their ancestors
741                let new_hovered_entities = get_hovered_entities(&hover_map, &pointer_id);
742                let new_hovered_ancestors =
743                    hovered_entity_ancestors.get_ancestors_union(&new_hovered_entities);
744                let union = new_hovered_entities
745                    .union(&new_hovered_ancestors)
746                    .copied()
747                    .collect::<EntityHashSet>();
748                // Keep entities and ancestors that are not going to continue to be hovered over
749                entities_to_send_leave.retain(|entity| !union.contains(entity));
750                // Send `Leave` events for those entities.
751                // Note that `Leave` events send without propagation; we manually calculated
752                // which ancestors should receive one.
753                for leave_event in entities_to_send_leave.iter().map(|entity| {
754                    Pointer::new_without_propagate(
755                        pointer_id,
756                        location.clone(),
757                        Leave {
758                            hit: hit.clone(),
759                            was_in_bounds: is_directly_hovered(
760                                &previous_hover_map.0,
761                                &pointer_id,
762                                entity,
763                            ),
764                        },
765                        *entity,
766                    )
767                }) {
768                    let entity = leave_event.entity;
769                    commands.trigger(leave_event.clone());
770                    message_writers.leave_events.write(leave_event);
771                    sent_leave.insert((pointer_id, entity));
772                }
773            }
774
775            // Possibly send DragLeave events
776            for button in PointerButton::iter() {
777                let state = pointer_state.get_mut(pointer_id, button);
778                state.dragging_over.remove(&hovered_entity);
779                for drag_target in state.dragging.keys() {
780                    let drag_leave_event = Pointer::new(
781                        pointer_id,
782                        location.clone(),
783                        DragLeave {
784                            button,
785                            dragged: *drag_target,
786                            hit: hit.clone(),
787                        },
788                        hovered_entity,
789                    );
790                    commands.trigger(drag_leave_event.clone());
791                    message_writers.drag_leave_events.write(drag_leave_event);
792                }
793            }
794        }
795    }
796
797    // Iterate all currently hovered entities for each pointer
798    for (pointer_id, hovered_entity, hit) in hover_map
799        .iter()
800        .flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
801    {
802        // Continue if the pointer does not have a valid location.
803        let Some(location) = pointer_location(pointer_id) else {
804            debug!(
805                "Unable to get location for pointer {:?} during pointer over",
806                pointer_id
807            );
808            continue;
809        };
810
811        // For each button update its `dragging_over` state and possibly emit DragEnter events.
812        for button in PointerButton::iter() {
813            let state = pointer_state.get_mut(pointer_id, button);
814
815            // Only update the `dragging_over` state if there is at least one entity being dragged.
816            // Only emit DragEnter events for this `hovered_entity`, if it had no previous `dragging_over` state.
817            if !state.dragging.is_empty()
818                && state
819                    .dragging_over
820                    .insert(hovered_entity, hit.clone())
821                    .is_none()
822            {
823                for drag_target in state.dragging.keys() {
824                    let drag_enter_event = Pointer::new(
825                        pointer_id,
826                        location.clone(),
827                        DragEnter {
828                            button,
829                            dragged: *drag_target,
830                            hit: hit.clone(),
831                        },
832                        hovered_entity,
833                    );
834                    commands.trigger(drag_enter_event.clone());
835                    message_writers.drag_enter_events.write(drag_enter_event);
836                }
837            }
838        }
839
840        // If the `hovered_entity` was not hovered by the same pointer the previous frame...
841        if !previous_hover_map
842            .get(&pointer_id)
843            .iter()
844            .any(|e| e.contains_key(&hovered_entity))
845        {
846            // Potentially send `Enter` events to the entity and all of its ancestors
847            let mut entities_to_send_enter = hovered_entity_ancestors
848                .get_ancestors(&hovered_entity)
849                .map_or_else(
850                    || {
851                        ancestors_query
852                            .iter_ancestors(hovered_entity)
853                            .collect::<EntityHashSet>()
854                    },
855                    Clone::clone,
856                );
857            entities_to_send_enter.insert(hovered_entity);
858            // Ensure we do not double send to any other entities that have already been sent to during this loop
859            entities_to_send_enter
860                .retain(|entity: &Entity| !sent_enter.contains(&(pointer_id, *entity)));
861            if !entities_to_send_enter.is_empty() {
862                // Fetch the previously hovered entities and their ancestors
863                let prev_hovered_entities = get_hovered_entities(&previous_hover_map, &pointer_id);
864                let prev_hovered_ancestors =
865                    pointer_state.get_ancestors_union(&prev_hovered_entities);
866                let union = prev_hovered_entities
867                    .union(&prev_hovered_ancestors)
868                    .copied()
869                    .collect::<EntityHashSet>();
870                // Keep entities and ancestors that were not hovered over previously
871                entities_to_send_enter.retain(|entity| !union.contains(entity));
872                // Send `Enter` events for those entities.
873                // Note that `Enter` events send without propagation; we manually calculated
874                // which ancestors should receive one.
875                for enter_event in entities_to_send_enter.iter().map(|entity| {
876                    Pointer::new_without_propagate(
877                        pointer_id,
878                        location.clone(),
879                        Enter {
880                            hit: hit.clone(),
881                            is_in_bounds: is_directly_hovered(&hover_map.0, &pointer_id, entity),
882                        },
883                        *entity,
884                    )
885                }) {
886                    let entity = enter_event.entity;
887                    commands.trigger(enter_event.clone());
888                    message_writers.enter_events.write(enter_event);
889                    sent_enter.insert((pointer_id, entity));
890                }
891            }
892
893            // Always send Over events
894            let over_event = Pointer::new(
895                pointer_id,
896                location.clone(),
897                Over { hit: hit.clone() },
898                hovered_entity,
899            );
900            commands.trigger(over_event.clone());
901            message_writers.over_events.write(over_event);
902        }
903    }
904
905    // Update pointer_state with the current hovered entity ancestors
906    // We swap with the Local SystemParam's map, which will be rebuilt
907    // on the next invocation of `pointer_events`
908    core::mem::swap(
909        &mut hovered_entity_ancestors.0,
910        &mut pointer_state.hovered_entity_ancestors,
911    );
912
913    // Dispatch input events...
914    for PointerInput {
915        pointer_id,
916        location,
917        action,
918    } in input_events.read().cloned()
919    {
920        match action {
921            PointerAction::Press(button) => {
922                let state = pointer_state.get_mut(pointer_id, button);
923                state.clicking.retain(|_, (last_click, _)| {
924                    now - *last_click <= picking_settings.multi_click_interval
925                });
926
927                // If it's a press, emit a Pressed event and mark the hovered entities as pressed
928                for (hovered_entity, hit) in hover_map
929                    .get(&pointer_id)
930                    .iter()
931                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
932                {
933                    let count = state
934                        .clicking
935                        .get(&hovered_entity)
936                        .map_or(1, |(_, count)| count.saturating_add(1));
937                    state.clicking.insert(hovered_entity, (now, count));
938                    let pressed_event = Pointer::new(
939                        pointer_id,
940                        location.clone(),
941                        Press {
942                            button,
943                            hit: hit.clone(),
944                            count,
945                        },
946                        hovered_entity,
947                    );
948                    commands.trigger(pressed_event.clone());
949                    message_writers.pressed_events.write(pressed_event);
950                    // Also insert the press into the state
951                    state
952                        .pressing
953                        .insert(hovered_entity, (location.clone(), now, hit));
954                }
955            }
956            PointerAction::Release(button) => {
957                let state = pointer_state.get_mut(pointer_id, button);
958                state.clicking.retain(|_, (last_click, _)| {
959                    now - *last_click <= picking_settings.multi_click_interval
960                });
961
962                // Emit Click and Release events on all the previously hovered entities.
963                for (hovered_entity, hit) in previous_hover_map
964                    .get(&pointer_id)
965                    .iter()
966                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
967                {
968                    // If this pointer previously pressed the hovered entity, emit a Click event
969                    if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {
970                        let count = state
971                            .clicking
972                            .get(&hovered_entity)
973                            .map_or(1, |(_, count)| *count);
974                        state.clicking.insert(hovered_entity, (now, count));
975                        let click_event = Pointer::new(
976                            pointer_id,
977                            location.clone(),
978                            Click {
979                                button,
980                                hit: hit.clone(),
981                                duration: now - *press_instant,
982                                count,
983                            },
984                            hovered_entity,
985                        );
986                        commands.trigger(click_event.clone());
987                        message_writers.click_events.write(click_event);
988                    }
989                    // Always send the Release event
990                    let released_event = Pointer::new(
991                        pointer_id,
992                        location.clone(),
993                        Release {
994                            button,
995                            hit: hit.clone(),
996                        },
997                        hovered_entity,
998                    );
999                    commands.trigger(released_event.clone());
1000                    message_writers.released_events.write(released_event);
1001                }
1002
1003                // Then emit the drop events.
1004                for (drag_target, drag) in state.dragging.drain() {
1005                    // Emit DragDrop
1006                    for (dragged_over, hit) in state.dragging_over.iter() {
1007                        let drag_drop_event = Pointer::new(
1008                            pointer_id,
1009                            location.clone(),
1010                            DragDrop {
1011                                button,
1012                                dropped: drag_target,
1013                                hit: hit.clone(),
1014                            },
1015                            *dragged_over,
1016                        );
1017                        commands.trigger(drag_drop_event.clone());
1018                        message_writers.drag_drop_events.write(drag_drop_event);
1019                    }
1020                    // Emit DragEnd
1021                    let drag_end_event = Pointer::new(
1022                        pointer_id,
1023                        location.clone(),
1024                        DragEnd {
1025                            button,
1026                            distance: drag.latest_pos - drag.start_pos,
1027                        },
1028                        drag_target,
1029                    );
1030                    commands.trigger(drag_end_event.clone());
1031                    message_writers.drag_end_events.write(drag_end_event);
1032                    // Emit DragLeave
1033                    for (dragged_over, hit) in state.dragging_over.iter() {
1034                        let drag_leave_event = Pointer::new(
1035                            pointer_id,
1036                            location.clone(),
1037                            DragLeave {
1038                                button,
1039                                dragged: drag_target,
1040                                hit: hit.clone(),
1041                            },
1042                            *dragged_over,
1043                        );
1044                        commands.trigger(drag_leave_event.clone());
1045                        message_writers.drag_leave_events.write(drag_leave_event);
1046                    }
1047                }
1048
1049                // Finally, we can clear the state of everything relating to presses or drags.
1050                state.clear();
1051            }
1052            // Moved
1053            PointerAction::Move { delta } => {
1054                if delta == Vec2::ZERO {
1055                    continue; // If delta is zero, the following events will not be triggered.
1056                }
1057                // Triggers during movement even if not over an entity
1058                for button in PointerButton::iter() {
1059                    let state = pointer_state.get_mut(pointer_id, button);
1060
1061                    // Emit DragEntry and DragStart the first time we move while pressing an entity
1062                    for (press_target, (location, _, hit)) in state.pressing.iter() {
1063                        if state.dragging.contains_key(press_target) {
1064                            continue; // This entity is already logged as being dragged
1065                        }
1066                        state.dragging.insert(
1067                            *press_target,
1068                            DragEntry {
1069                                start_pos: location.position,
1070                                latest_pos: location.position,
1071                            },
1072                        );
1073                        let drag_start_event = Pointer::new(
1074                            pointer_id,
1075                            location.clone(),
1076                            DragStart {
1077                                button,
1078                                hit: hit.clone(),
1079                            },
1080                            *press_target,
1081                        );
1082
1083                        commands.trigger(drag_start_event.clone());
1084                        message_writers.drag_start_events.write(drag_start_event);
1085
1086                        // Insert dragging over state and emit DragEnter for hovered entities.
1087                        for (hovered_entity, hit) in hover_map
1088                            .get(&pointer_id)
1089                            .iter()
1090                            .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1091                            .filter(|(hovered_entity, _)| *hovered_entity != *press_target)
1092                        {
1093                            // Inserting the `dragging_over` state here ensures the `DragEnter` event won't be dispatched twice.
1094                            state.dragging_over.insert(hovered_entity, hit.clone());
1095                            let drag_enter_event = Pointer::new(
1096                                pointer_id,
1097                                location.clone(),
1098                                DragEnter {
1099                                    button,
1100                                    dragged: *press_target,
1101                                    hit: hit.clone(),
1102                                },
1103                                hovered_entity,
1104                            );
1105                            commands.trigger(drag_enter_event.clone());
1106                            message_writers.drag_enter_events.write(drag_enter_event);
1107                        }
1108                    }
1109
1110                    // Emit Drag events to the entities we are dragging
1111                    for (drag_target, drag) in state.dragging.iter_mut() {
1112                        let delta = location.position - drag.latest_pos;
1113                        if delta == Vec2::ZERO {
1114                            continue; // No need to emit a Drag event if there is no movement
1115                        }
1116                        let drag_event = Pointer::new(
1117                            pointer_id,
1118                            location.clone(),
1119                            Drag {
1120                                button,
1121                                distance: location.position - drag.start_pos,
1122                                delta,
1123                            },
1124                            *drag_target,
1125                        );
1126                        commands.trigger(drag_event.clone());
1127                        message_writers.drag_events.write(drag_event);
1128
1129                        // Update drag position
1130                        drag.latest_pos = location.position;
1131
1132                        // Emit corresponding DragOver to the hovered entities
1133                        for (hovered_entity, hit) in hover_map
1134                            .get(&pointer_id)
1135                            .iter()
1136                            .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1137                            .filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
1138                        {
1139                            let drag_over_event = Pointer::new(
1140                                pointer_id,
1141                                location.clone(),
1142                                DragOver {
1143                                    button,
1144                                    dragged: *drag_target,
1145                                    hit: hit.clone(),
1146                                },
1147                                hovered_entity,
1148                            );
1149                            commands.trigger(drag_over_event.clone());
1150                            message_writers.drag_over_events.write(drag_over_event);
1151                        }
1152                    }
1153                }
1154
1155                for (hovered_entity, hit) in hover_map
1156                    .get(&pointer_id)
1157                    .iter()
1158                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1159                {
1160                    // Emit Move events to the entities we are hovering
1161                    let move_event = Pointer::new(
1162                        pointer_id,
1163                        location.clone(),
1164                        Move {
1165                            hit: hit.clone(),
1166                            delta,
1167                        },
1168                        hovered_entity,
1169                    );
1170                    commands.trigger(move_event.clone());
1171                    message_writers.move_events.write(move_event);
1172                }
1173            }
1174            PointerAction::Scroll { x, y, unit, phase } => {
1175                for (hovered_entity, hit) in hover_map
1176                    .get(&pointer_id)
1177                    .iter()
1178                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
1179                {
1180                    // Emit Scroll events to the entities we are hovering
1181                    let scroll_event = Pointer::new(
1182                        pointer_id,
1183                        location.clone(),
1184                        Scroll {
1185                            unit,
1186                            x,
1187                            y,
1188                            hit: hit.clone(),
1189                            phase,
1190                        },
1191                        hovered_entity,
1192                    );
1193                    commands.trigger(scroll_event.clone());
1194                    message_writers.scroll_events.write(scroll_event);
1195                }
1196            }
1197            // Canceled
1198            PointerAction::Cancel => {
1199                // Emit a Cancel to the hovered entity.
1200                for (hovered_entity, hit) in hover_map
1201                    .get(&pointer_id)
1202                    .iter()
1203                    .flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
1204                {
1205                    let cancel_event =
1206                        Pointer::new(pointer_id, location.clone(), Cancel { hit }, hovered_entity);
1207                    commands.trigger(cancel_event.clone());
1208                    message_writers.cancel_events.write(cancel_event);
1209                }
1210                // Clear the state for the canceled pointer
1211                pointer_state.clear(pointer_id);
1212            }
1213        }
1214    }
1215}
1216
1217#[cfg(test)]
1218mod tests {
1219    use bevy_app::App;
1220    use bevy_camera::{Camera, ManualTextureViewHandle};
1221
1222    use crate::pointer::update_pointer_map;
1223
1224    use super::*;
1225
1226    const POINTER_ID: PointerId = PointerId::Mouse;
1227    const STUB_LOCATION: Location = Location {
1228        target: NormalizedRenderTarget::TextureView(ManualTextureViewHandle(5)),
1229        position: Vec2::new(3., 4.),
1230    };
1231
1232    fn initialize_app_for_test(app: &mut App) {
1233        // Init all the resources and messages necessary to run `pointer_events`
1234        app.init_resource::<HoverMap>()
1235            .init_resource::<PreviousHoverMap>()
1236            .init_resource::<PickingSettings>()
1237            .init_resource::<PointerState>()
1238            .add_message::<PointerInput>()
1239            .add_message::<Pointer<Cancel>>()
1240            .add_message::<Pointer<Click>>()
1241            .add_message::<Pointer<Press>>()
1242            .add_message::<Pointer<DragDrop>>()
1243            .add_message::<Pointer<DragEnd>>()
1244            .add_message::<Pointer<DragEnter>>()
1245            .add_message::<Pointer<Drag>>()
1246            .add_message::<Pointer<DragLeave>>()
1247            .add_message::<Pointer<DragOver>>()
1248            .add_message::<Pointer<DragStart>>()
1249            .add_message::<Pointer<Scroll>>()
1250            .add_message::<Pointer<Move>>()
1251            .add_message::<Pointer<Out>>()
1252            .add_message::<Pointer<Over>>()
1253            .add_message::<Pointer<Leave>>()
1254            .add_message::<Pointer<Enter>>()
1255            .add_message::<Pointer<Release>>();
1256
1257        // Initialize the pointer map resource manually with a stub location for the mouse
1258        app.world_mut()
1259            .spawn((POINTER_ID, PointerLocation::new(STUB_LOCATION)));
1260        app.world_mut().insert_resource(PointerMap::default());
1261        assert!(app
1262            .world_mut()
1263            .run_system_cached(update_pointer_map)
1264            .is_ok());
1265    }
1266
1267    fn update_hover_map_with_hovered_entities(app: &mut App, camera: Entity, entities: &[Entity]) {
1268        let mut hover_map = HoverMap::default();
1269        let mut entity_map = EntityHashMap::with_capacity(entities.len());
1270        for entity in entities {
1271            entity_map.insert(
1272                *entity,
1273                HitData {
1274                    depth: 0.0,
1275                    camera,
1276                    position: None,
1277                    normal: None,
1278                    extra: None,
1279                },
1280            );
1281        }
1282        hover_map.insert(PointerId::Mouse, entity_map);
1283
1284        let previous_hover_map = app.world().resource::<HoverMap>().0.clone();
1285        app.world_mut()
1286            .insert_resource(PreviousHoverMap(previous_hover_map));
1287        app.world_mut().insert_resource(hover_map);
1288    }
1289
1290    #[test]
1291    fn enter_leave_events() {
1292        // the bool distinguishes between different *_in_bounds bool vals
1293        #[derive(Resource, Default)]
1294        struct EnterEventCounts(HashMap<(Entity, bool), usize>);
1295
1296        #[derive(Resource, Default)]
1297        struct LeaveEventCounts(HashMap<(Entity, bool), usize>);
1298
1299        fn observe_enter(event: On<Pointer<Enter>>, mut counts: ResMut<EnterEventCounts>) {
1300            *counts
1301                .0
1302                .entry((event.entity, event.event().is_in_bounds))
1303                .or_insert(0_usize) += 1;
1304        }
1305
1306        fn observe_leave(event: On<Pointer<Leave>>, mut counts: ResMut<LeaveEventCounts>) {
1307            *counts
1308                .0
1309                .entry((event.entity, event.event().was_in_bounds))
1310                .or_insert(0_usize) += 1;
1311        }
1312
1313        fn assert_msg_event_counts(app: &App, enter_count: usize, leave_count: usize) {
1314            let enter_messages = app.world().resource::<Messages<Pointer<Enter>>>();
1315            let leave_messages = app.world().resource::<Messages<Pointer<Leave>>>();
1316            assert_eq!(enter_messages.len(), enter_count);
1317            assert_eq!(leave_messages.len(), leave_count);
1318        }
1319
1320        fn assert_observer_event_counts(
1321            app: &App,
1322            entity: Entity,
1323            enter_in_bounds_counts: usize,
1324            enter_out_of_bounds_counts: usize,
1325            leave_in_bounds_counts: usize,
1326            leave_out_of_bounds_counts: usize,
1327        ) {
1328            assert_eq!(
1329                *app.world()
1330                    .resource::<EnterEventCounts>()
1331                    .0
1332                    .get(&(entity, true))
1333                    .unwrap_or(&0),
1334                enter_in_bounds_counts
1335            );
1336            assert_eq!(
1337                *app.world()
1338                    .resource::<EnterEventCounts>()
1339                    .0
1340                    .get(&(entity, false))
1341                    .unwrap_or(&0),
1342                enter_out_of_bounds_counts
1343            );
1344            assert_eq!(
1345                *app.world()
1346                    .resource::<LeaveEventCounts>()
1347                    .0
1348                    .get(&(entity, true))
1349                    .unwrap_or(&0),
1350                leave_in_bounds_counts
1351            );
1352            assert_eq!(
1353                *app.world()
1354                    .resource::<LeaveEventCounts>()
1355                    .0
1356                    .get(&(entity, false))
1357                    .unwrap_or(&0),
1358                leave_out_of_bounds_counts
1359            );
1360        }
1361
1362        let mut app = App::new();
1363        initialize_app_for_test(&mut app);
1364        app.init_resource::<EnterEventCounts>()
1365            .init_resource::<LeaveEventCounts>();
1366        let enter_messages = app.world().resource::<Messages<Pointer<Enter>>>();
1367        let leave_messages = app.world().resource::<Messages<Pointer<Leave>>>();
1368        assert_eq!(enter_messages.len(), 0);
1369        assert_eq!(leave_messages.len(), 0);
1370        // Setup test entities
1371        let camera = app.world_mut().spawn(Camera::default()).id();
1372        let child_one = app
1373            .world_mut()
1374            .spawn_empty()
1375            .observe(observe_enter)
1376            .observe(observe_leave)
1377            .id();
1378        let child_two = app
1379            .world_mut()
1380            .spawn_empty()
1381            .observe(observe_enter)
1382            .observe(observe_leave)
1383            .id();
1384        let parent = app
1385            .world_mut()
1386            .spawn_empty()
1387            .add_children(&[child_one, child_two])
1388            .observe(observe_enter)
1389            .observe(observe_leave)
1390            .id();
1391
1392        // FIRST: child_one is hovered over
1393        update_hover_map_with_hovered_entities(&mut app, camera, &[child_one]);
1394
1395        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1396
1397        // child_one received an in_bounds `Enter` event
1398        // The parent received an indirect `Enter` event because its child was hovered into
1399        assert_msg_event_counts(&app, 2, 0);
1400        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1401        assert_observer_event_counts(&app, child_one, 1, 0, 0, 0);
1402        assert_observer_event_counts(&app, child_two, 0, 0, 0, 0);
1403        app.world_mut().increment_change_tick();
1404        // ---
1405
1406        // SECOND: child_one is hovered out of, child_two and parent are directly hovered over
1407        update_hover_map_with_hovered_entities(&mut app, camera, &[child_two, parent]);
1408
1409        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1410
1411        // child_one received an in_bounds `Leave` event.
1412        // child_two received an in_bounds `Enter` event.
1413        // The parent did not receive any events because it is a shared ancestor
1414        assert_msg_event_counts(&app, 3, 1);
1415        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1416        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1417        assert_observer_event_counts(&app, child_two, 1, 0, 0, 0);
1418        app.world_mut().increment_change_tick();
1419        // ---
1420
1421        // THIRD: child_two is hovered out of, parent is still hovered
1422        update_hover_map_with_hovered_entities(&mut app, camera, &[parent]);
1423
1424        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1425
1426        // child_two received an in_bounds `Leave` event.
1427        assert_msg_event_counts(&app, 3, 2);
1428        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1429        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1430        assert_observer_event_counts(&app, child_two, 1, 0, 1, 0);
1431        app.world_mut().increment_change_tick();
1432        // ---
1433
1434        // FOURTH: child_two is hovered back into, parent is no longer directly hovered
1435        update_hover_map_with_hovered_entities(&mut app, camera, &[child_two]);
1436
1437        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1438
1439        // child_two received an in_bounds `Enter` event
1440        // The parent did not receive an `Leave` event because its child is still hovered
1441        assert_msg_event_counts(&app, 4, 2);
1442        assert_observer_event_counts(&app, parent, 0, 1, 0, 0);
1443        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1444        assert_observer_event_counts(&app, child_two, 2, 0, 1, 0);
1445        app.world_mut().increment_change_tick();
1446        // ---
1447
1448        // FIFTH: child_two is hovered out of
1449        update_hover_map_with_hovered_entities(&mut app, camera, &[]);
1450
1451        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1452
1453        // child_two received one in_bounds `Leave` event
1454        // The parent received one indirect `Leave` event because the pointer is no longer hovering
1455        // any of its children
1456        assert_msg_event_counts(&app, 4, 4);
1457        assert_observer_event_counts(&app, parent, 0, 1, 0, 1);
1458        assert_observer_event_counts(&app, child_one, 1, 0, 1, 0);
1459        assert_observer_event_counts(&app, child_two, 2, 0, 2, 0);
1460        app.world_mut().increment_change_tick();
1461        // ---
1462
1463        // FINAL: parent and child_one are directly hovered into
1464        update_hover_map_with_hovered_entities(&mut app, camera, &[parent, child_one]);
1465
1466        assert!(app.world_mut().run_system_cached(pointer_events).is_ok());
1467
1468        // The parent received one in_bounds `Enter` event
1469        // child_one received one in_bounds `Enter` event
1470        assert_msg_event_counts(&app, 6, 4);
1471        assert_observer_event_counts(&app, parent, 1, 1, 0, 1);
1472        assert_observer_event_counts(&app, child_one, 2, 0, 1, 0);
1473        assert_observer_event_counts(&app, child_two, 2, 0, 2, 0);
1474        app.world_mut().increment_change_tick();
1475        // ---
1476    }
1477}