//! This module defines a stateful set of interaction events driven by the `PointerInput` stream1//! and the hover state of each Pointer.2//!3//! # Usage4//!5//! To receive events from this module, you must use an [`Observer`] or [`MessageReader`] with [`Pointer<E>`] events.6//! The simplest example, registering a callback when an entity is hovered over by a pointer, looks like this:7//!8//! ```rust9//! # use bevy_ecs::prelude::*;10//! # use bevy_picking::prelude::*;11//! # let mut world = World::default();12//! world.spawn_empty()13//! .observe(|event: On<Pointer<Over>>| {14//! println!("I am being hovered over");15//! });16//! ```17//!18//! Observers give us three important properties:19//! 1. They allow for attaching event handlers to specific entities,20//! 2. they allow events to bubble up the entity hierarchy,21//! 3. and they allow events of different types to be called in a specific order.22//!23//! The order in which interaction events are received is extremely important, and you can read more24//! about it on the docs for the dispatcher system: [`pointer_events`]. This system runs in25//! [`PreUpdate`](bevy_app::PreUpdate) in [`PickingSystems::Hover`](crate::PickingSystems::Hover). All pointer-event26//! observers resolve during the sync point between [`pointer_events`] and27//! [`update_interactions`](crate::hover::update_interactions).28//!29//! # Events Types30//!31//! The events this module defines fall into a few broad categories:32//! + Hovering and movement: [`Over`], [`Move`], and [`Out`].33//! + Clicking and pressing: [`Press`], [`Release`], and [`Click`].34//! + Dragging and dropping: [`DragStart`], [`Drag`], [`DragEnd`], [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].35//!36//! When received by an observer, these events will always be wrapped by the [`Pointer`] type, which contains37//! general metadata about the pointer event.3839use core::{fmt::Debug, time::Duration};4041use bevy_camera::NormalizedRenderTarget;42use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal};43use bevy_input::mouse::MouseScrollUnit;44use bevy_math::Vec2;45use bevy_platform::collections::HashMap;46use bevy_platform::time::Instant;47use bevy_reflect::prelude::*;48use bevy_window::Window;49use tracing::debug;5051use crate::{52backend::{prelude::PointerLocation, HitData},53hover::{HoverMap, PreviousHoverMap},54pointer::{Location, PointerAction, PointerButton, PointerId, PointerInput, PointerMap},55};5657/// Stores the common data needed for all pointer events.58///59/// The documentation for the [`pointer_events`] explains the events this module exposes and60/// the order in which they fire.61#[derive(Message, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)]62#[entity_event(propagate = PointerTraversal, auto_propagate)]63#[reflect(Component, Debug, Clone)]64pub struct Pointer<E: Debug + Clone + Reflect> {65/// The entity this pointer event happened for.66pub entity: Entity,67/// The pointer that triggered this event68pub pointer_id: PointerId,69/// The location of the pointer during this event70pub pointer_location: Location,71/// Additional event-specific data. [`DragDrop`] for example, has an additional field to describe72/// the `Entity` that is being dropped on the target.73pub event: E,74}7576/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.77///78/// This will always traverse to the parent, if the entity being visited has one. Otherwise, it79/// propagates to the pointer's window and stops there.80#[derive(QueryData)]81pub struct PointerTraversal {82child_of: Option<&'static ChildOf>,83window: Option<&'static Window>,84}8586impl<E> Traversal<Pointer<E>> for PointerTraversal87where88E: Debug + Clone + Reflect,89{90fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer<E>) -> Option<Entity> {91let PointerTraversalItem { child_of, window } = item;9293// Send event to parent, if it has one.94if let Some(child_of) = child_of {95return Some(child_of.parent());96};9798// Otherwise, send it to the window entity (unless this is a window entity).99if window.is_none()100&& let NormalizedRenderTarget::Window(window_ref) = pointer.pointer_location.target101{102return Some(window_ref.entity());103}104105None106}107}108109impl<E: Debug + Clone + Reflect> core::fmt::Display for Pointer<E> {110fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {111f.write_fmt(format_args!(112"{:?}, {:.1?}, {:.1?}",113self.pointer_id, self.pointer_location.position, self.event114))115}116}117118impl<E: Debug + Clone + Reflect> core::ops::Deref for Pointer<E> {119type Target = E;120121fn deref(&self) -> &Self::Target {122&self.event123}124}125126impl<E: Debug + Clone + Reflect> Pointer<E> {127/// Construct a new `Pointer<E>` event.128pub fn new(id: PointerId, location: Location, event: E, entity: Entity) -> Self {129Self {130pointer_id: id,131pointer_location: location,132event,133entity,134}135}136}137138/// Fires when a pointer is canceled, and its current interaction state is dropped.139#[derive(Clone, PartialEq, Debug, Reflect)]140#[reflect(Clone, PartialEq)]141pub struct Cancel {142/// Information about the picking intersection.143pub hit: HitData,144}145146/// Fires when a pointer crosses into the bounds of the [target entity](EntityEvent::event_target).147#[derive(Clone, PartialEq, Debug, Reflect)]148#[reflect(Clone, PartialEq)]149pub struct Over {150/// Information about the picking intersection.151pub hit: HitData,152}153154/// Fires when a pointer crosses out of the bounds of the [target entity](EntityEvent::event_target).155#[derive(Clone, PartialEq, Debug, Reflect)]156#[reflect(Clone, PartialEq)]157pub struct Out {158/// Information about the latest prior picking intersection.159pub hit: HitData,160}161162/// Fires when a pointer button is pressed over the [target entity](EntityEvent::event_target).163#[derive(Clone, PartialEq, Debug, Reflect)]164#[reflect(Clone, PartialEq)]165pub struct Press {166/// Pointer button pressed to trigger this event.167pub button: PointerButton,168/// Information about the picking intersection.169pub hit: HitData,170}171172/// Fires when a pointer button is released over the [target entity](EntityEvent::event_target).173#[derive(Clone, PartialEq, Debug, Reflect)]174#[reflect(Clone, PartialEq)]175pub struct Release {176/// Pointer button lifted to trigger this event.177pub button: PointerButton,178/// Information about the picking intersection.179pub hit: HitData,180}181182/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same183/// [target entity](EntityEvent::event_target) for both events.184#[derive(Clone, PartialEq, Debug, Reflect)]185#[reflect(Clone, PartialEq)]186pub struct Click {187/// Pointer button pressed and lifted to trigger this event.188pub button: PointerButton,189/// Information about the picking intersection.190pub hit: HitData,191/// Duration between the pointer pressed and lifted for this click192pub duration: Duration,193}194195/// Fires while a pointer is moving over the [target entity](EntityEvent::event_target).196#[derive(Clone, PartialEq, Debug, Reflect)]197#[reflect(Clone, PartialEq)]198pub struct Move {199/// Information about the picking intersection.200pub hit: HitData,201/// The change in position since the last move event.202///203/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to204/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider205/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to206/// world-space.207pub delta: Vec2,208}209210/// Fires when the [target entity](EntityEvent::event_target) receives a pointer pressed event followed by a pointer move event.211#[derive(Clone, PartialEq, Debug, Reflect)]212#[reflect(Clone, PartialEq)]213pub struct DragStart {214/// Pointer button pressed and moved to trigger this event.215pub button: PointerButton,216/// Information about the picking intersection.217pub hit: HitData,218}219220/// Fires while the [target entity](EntityEvent::event_target) is being dragged.221#[derive(Clone, PartialEq, Debug, Reflect)]222#[reflect(Clone, PartialEq)]223pub struct Drag {224/// Pointer button pressed and moved to trigger this event.225pub button: PointerButton,226/// The total distance vector of a drag, measured from drag start to the current position.227///228/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to229/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider230/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to231/// world-space.232pub distance: Vec2,233/// The change in position since the last drag event.234///235/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to236/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider237/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to238/// world-space.239pub delta: Vec2,240}241242/// Fires when a pointer is dragging the [target entity](EntityEvent::event_target) and a pointer released event is received.243#[derive(Clone, PartialEq, Debug, Reflect)]244#[reflect(Clone, PartialEq)]245pub struct DragEnd {246/// Pointer button pressed, moved, and released to trigger this event.247pub button: PointerButton,248/// The vector of drag movement measured from start to final pointer position.249///250/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to251/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider252/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to253/// world-space.254pub distance: Vec2,255}256257/// Fires when a pointer dragging the `dragged` entity enters the [target entity](EntityEvent::event_target)258#[derive(Clone, PartialEq, Debug, Reflect)]259#[reflect(Clone, PartialEq)]260pub struct DragEnter {261/// Pointer button pressed to enter drag.262pub button: PointerButton,263/// The entity that was being dragged when the pointer entered the [target entity](EntityEvent::event_target).264pub dragged: Entity,265/// Information about the picking intersection.266pub hit: HitData,267}268269/// Fires while the `dragged` entity is being dragged over the [target entity](EntityEvent::event_target).270#[derive(Clone, PartialEq, Debug, Reflect)]271#[reflect(Clone, PartialEq)]272pub struct DragOver {273/// Pointer button pressed while dragging over.274pub button: PointerButton,275/// The entity that was being dragged when the pointer was over the [target entity](EntityEvent::event_target).276pub dragged: Entity,277/// Information about the picking intersection.278pub hit: HitData,279}280281/// Fires when a pointer dragging the `dragged` entity leaves the [target entity](EntityEvent::event_target).282#[derive(Clone, PartialEq, Debug, Reflect)]283#[reflect(Clone, PartialEq)]284pub struct DragLeave {285/// Pointer button pressed while leaving drag.286pub button: PointerButton,287/// The entity that was being dragged when the pointer left the [target entity](EntityEvent::event_target).288pub dragged: Entity,289/// Information about the latest prior picking intersection.290pub hit: HitData,291}292293/// Fires when a pointer drops the `dropped` entity onto the [target entity](EntityEvent::event_target).294#[derive(Clone, PartialEq, Debug, Reflect)]295#[reflect(Clone, PartialEq)]296pub struct DragDrop {297/// Pointer button released to drop.298pub button: PointerButton,299/// The entity that was dropped onto the [target entity](EntityEvent::event_target).300pub dropped: Entity,301/// Information about the picking intersection.302pub hit: HitData,303}304305/// Dragging state.306#[derive(Clone, PartialEq, Debug, Reflect)]307#[reflect(Clone, PartialEq)]308pub struct DragEntry {309/// The position of the pointer at drag start.310///311/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to312/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider313/// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or314/// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to315/// convert from screen-space to world-space.316pub start_pos: Vec2,317/// The latest position of the pointer during this drag, used to compute deltas.318///319/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to320/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider321/// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or322/// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to323/// convert from screen-space to world-space.324pub latest_pos: Vec2,325}326327/// Fires while a pointer is scrolling over the [target entity](EntityEvent::event_target).328#[derive(Clone, PartialEq, Debug, Reflect)]329#[reflect(Clone, PartialEq)]330pub struct Scroll {331/// The mouse scroll unit.332pub unit: MouseScrollUnit,333/// The horizontal scroll value.334pub x: f32,335/// The vertical scroll value.336pub y: f32,337/// Information about the picking intersection.338pub hit: HitData,339}340341/// An entry in the cache that drives the `pointer_events` system, storing additional data342/// about pointer button presses.343#[derive(Debug, Clone, Default)]344pub struct PointerButtonState {345/// Stores the press location and start time for each button currently being pressed by the pointer.346pub pressing: HashMap<Entity, (Location, Instant, HitData)>,347/// Stores the starting and current locations for each entity currently being dragged by the pointer.348pub dragging: HashMap<Entity, DragEntry>,349/// Stores the hit data for each entity currently being dragged over by the pointer.350pub dragging_over: HashMap<Entity, HitData>,351}352353impl PointerButtonState {354/// Clears all press and drag data tracked for this button on its pointer.355pub fn clear(&mut self) {356self.pressing.clear();357self.dragging.clear();358self.dragging_over.clear();359}360}361362/// State for all pointers.363#[derive(Debug, Clone, Default, Resource)]364pub struct PointerState {365/// Pressing and dragging state, organized by pointer and button.366pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,367}368369impl PointerState {370/// Retrieves the current state for a specific pointer and button, if it has been created.371pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {372self.pointer_buttons.get(&(pointer_id, button))373}374375/// Provides write access to the state of a pointer and button, creating it if it does not yet exist.376pub fn get_mut(377&mut self,378pointer_id: PointerId,379button: PointerButton,380) -> &mut PointerButtonState {381self.pointer_buttons382.entry((pointer_id, button))383.or_default()384}385386/// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.387pub fn clear(&mut self, pointer_id: PointerId) {388for button in PointerButton::iter() {389if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {390state.clear();391}392}393}394}395396/// A helper system param for accessing the picking event writers.397#[derive(SystemParam)]398pub struct PickingMessageWriters<'w> {399cancel_events: MessageWriter<'w, Pointer<Cancel>>,400click_events: MessageWriter<'w, Pointer<Click>>,401pressed_events: MessageWriter<'w, Pointer<Press>>,402drag_drop_events: MessageWriter<'w, Pointer<DragDrop>>,403drag_end_events: MessageWriter<'w, Pointer<DragEnd>>,404drag_enter_events: MessageWriter<'w, Pointer<DragEnter>>,405drag_events: MessageWriter<'w, Pointer<Drag>>,406drag_leave_events: MessageWriter<'w, Pointer<DragLeave>>,407drag_over_events: MessageWriter<'w, Pointer<DragOver>>,408drag_start_events: MessageWriter<'w, Pointer<DragStart>>,409scroll_events: MessageWriter<'w, Pointer<Scroll>>,410move_events: MessageWriter<'w, Pointer<Move>>,411out_events: MessageWriter<'w, Pointer<Out>>,412over_events: MessageWriter<'w, Pointer<Over>>,413released_events: MessageWriter<'w, Pointer<Release>>,414}415416/// Dispatches interaction events to the target entities.417///418/// Within a single frame, events are dispatched in the following order:419/// + [`Out`] → [`DragLeave`].420/// + [`DragEnter`] → [`Over`].421/// + Any number of any of the following:422/// + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].423/// + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].424/// + For each pointer cancellation: [`Cancel`].425///426/// Additionally, across multiple frames, the following are also strictly427/// ordered by the interaction state machine:428/// + When a pointer moves over the target:429/// [`Over`], [`Move`], [`Out`].430/// + When a pointer presses buttons on the target:431/// [`Press`], [`Click`], [`Release`].432/// + When a pointer drags the target:433/// [`DragStart`], [`Drag`], [`DragEnd`].434/// + When a pointer drags something over the target:435/// [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].436/// + When a pointer is canceled:437/// No other events will follow the [`Cancel`] event for that pointer.438///439/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].440/// The rest rely on additional data from the [`PointerInput`] event stream. To441/// receive these events for a custom pointer, you must add [`PointerInput`]442/// events.443///444/// When the pointer goes from hovering entity A to entity B, entity A will445/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever446/// receive both an [`Over`] and an [`Out`] event during the same frame.447///448/// When we account for event bubbling, this is no longer true. When the hovering focus shifts449/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.450/// In the context of UI, this is especially problematic. Additional hierarchy-aware451/// events will be added in a future release.452///453/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,454/// rather than the current frame. This is because touch pointers hover nothing455/// on the frame they are released. The end effect is that these two events can456/// be received sequentially after an [`Out`] event (but always on the same frame457/// as the [`Out`] event).458///459/// Note: Though it is common for the [`PointerInput`] stream may contain460/// multiple pointer movements and presses each frame, the hover state is461/// determined only by the pointer's *final position*. Since the hover state462/// ultimately determines which entities receive events, this may mean that an463/// entity can receive events from before or after it was actually hovered.464pub fn pointer_events(465// Input466mut input_events: MessageReader<PointerInput>,467// ECS State468pointers: Query<&PointerLocation>,469pointer_map: Res<PointerMap>,470hover_map: Res<HoverMap>,471previous_hover_map: Res<PreviousHoverMap>,472mut pointer_state: ResMut<PointerState>,473// Output474mut commands: Commands,475mut message_writers: PickingMessageWriters,476) {477// Setup utilities478let now = Instant::now();479let pointer_location = |pointer_id: PointerId| {480pointer_map481.get_entity(pointer_id)482.and_then(|entity| pointers.get(entity).ok())483.and_then(|pointer| pointer.location.clone())484};485486// If the entity was hovered by a specific pointer last frame...487for (pointer_id, hovered_entity, hit) in previous_hover_map488.iter()489.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))490{491// ...but is now not being hovered by that same pointer...492if !hover_map493.get(&pointer_id)494.iter()495.any(|e| e.contains_key(&hovered_entity))496{497let Some(location) = pointer_location(pointer_id) else {498debug!(499"Unable to get location for pointer {:?} during pointer out",500pointer_id501);502continue;503};504505// Always send Out events506let out_event = Pointer::new(507pointer_id,508location.clone(),509Out { hit: hit.clone() },510hovered_entity,511);512commands.trigger(out_event.clone());513message_writers.out_events.write(out_event);514515// Possibly send DragLeave events516for button in PointerButton::iter() {517let state = pointer_state.get_mut(pointer_id, button);518state.dragging_over.remove(&hovered_entity);519for drag_target in state.dragging.keys() {520let drag_leave_event = Pointer::new(521pointer_id,522location.clone(),523DragLeave {524button,525dragged: *drag_target,526hit: hit.clone(),527},528hovered_entity,529);530commands.trigger(drag_leave_event.clone());531message_writers.drag_leave_events.write(drag_leave_event);532}533}534}535}536537// Iterate all currently hovered entities for each pointer538for (pointer_id, hovered_entity, hit) in hover_map539.iter()540.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))541{542// Continue if the pointer does not have a valid location.543let Some(location) = pointer_location(pointer_id) else {544debug!(545"Unable to get location for pointer {:?} during pointer over",546pointer_id547);548continue;549};550551// For each button update its `dragging_over` state and possibly emit DragEnter events.552for button in PointerButton::iter() {553let state = pointer_state.get_mut(pointer_id, button);554555// Only update the `dragging_over` state if there is at least one entity being dragged.556// Only emit DragEnter events for this `hovered_entity`, if it had no previous `dragging_over` state.557if !state.dragging.is_empty()558&& state559.dragging_over560.insert(hovered_entity, hit.clone())561.is_none()562{563for drag_target in state.dragging.keys() {564let drag_enter_event = Pointer::new(565pointer_id,566location.clone(),567DragEnter {568button,569dragged: *drag_target,570hit: hit.clone(),571},572hovered_entity,573);574commands.trigger(drag_enter_event.clone());575message_writers.drag_enter_events.write(drag_enter_event);576}577}578}579580// Emit an Over event if the `hovered_entity` was not hovered by the same pointer the previous frame.581if !previous_hover_map582.get(&pointer_id)583.iter()584.any(|e| e.contains_key(&hovered_entity))585{586let over_event = Pointer::new(587pointer_id,588location.clone(),589Over { hit: hit.clone() },590hovered_entity,591);592commands.trigger(over_event.clone());593message_writers.over_events.write(over_event);594}595}596597// Dispatch input events...598for PointerInput {599pointer_id,600location,601action,602} in input_events.read().cloned()603{604match action {605PointerAction::Press(button) => {606let state = pointer_state.get_mut(pointer_id, button);607608// If it's a press, emit a Pressed event and mark the hovered entities as pressed609for (hovered_entity, hit) in hover_map610.get(&pointer_id)611.iter()612.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))613{614let pressed_event = Pointer::new(615pointer_id,616location.clone(),617Press {618button,619hit: hit.clone(),620},621hovered_entity,622);623commands.trigger(pressed_event.clone());624message_writers.pressed_events.write(pressed_event);625// Also insert the press into the state626state627.pressing628.insert(hovered_entity, (location.clone(), now, hit));629}630}631PointerAction::Release(button) => {632let state = pointer_state.get_mut(pointer_id, button);633634// Emit Click and Release events on all the previously hovered entities.635for (hovered_entity, hit) in previous_hover_map636.get(&pointer_id)637.iter()638.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))639{640// If this pointer previously pressed the hovered entity, emit a Click event641if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {642let click_event = Pointer::new(643pointer_id,644location.clone(),645Click {646button,647hit: hit.clone(),648duration: now - *press_instant,649},650hovered_entity,651);652commands.trigger(click_event.clone());653message_writers.click_events.write(click_event);654}655// Always send the Release event656let released_event = Pointer::new(657pointer_id,658location.clone(),659Release {660button,661hit: hit.clone(),662},663hovered_entity,664);665commands.trigger(released_event.clone());666message_writers.released_events.write(released_event);667}668669// Then emit the drop events.670for (drag_target, drag) in state.dragging.drain() {671// Emit DragDrop672for (dragged_over, hit) in state.dragging_over.iter() {673let drag_drop_event = Pointer::new(674pointer_id,675location.clone(),676DragDrop {677button,678dropped: drag_target,679hit: hit.clone(),680},681*dragged_over,682);683commands.trigger(drag_drop_event.clone());684message_writers.drag_drop_events.write(drag_drop_event);685}686// Emit DragEnd687let drag_end_event = Pointer::new(688pointer_id,689location.clone(),690DragEnd {691button,692distance: drag.latest_pos - drag.start_pos,693},694drag_target,695);696commands.trigger(drag_end_event.clone());697message_writers.drag_end_events.write(drag_end_event);698// Emit DragLeave699for (dragged_over, hit) in state.dragging_over.iter() {700let drag_leave_event = Pointer::new(701pointer_id,702location.clone(),703DragLeave {704button,705dragged: drag_target,706hit: hit.clone(),707},708*dragged_over,709);710commands.trigger(drag_leave_event.clone());711message_writers.drag_leave_events.write(drag_leave_event);712}713}714715// Finally, we can clear the state of everything relating to presses or drags.716state.clear();717}718// Moved719PointerAction::Move { delta } => {720if delta == Vec2::ZERO {721continue; // If delta is zero, the following events will not be triggered.722}723// Triggers during movement even if not over an entity724for button in PointerButton::iter() {725let state = pointer_state.get_mut(pointer_id, button);726727// Emit DragEntry and DragStart the first time we move while pressing an entity728for (press_target, (location, _, hit)) in state.pressing.iter() {729if state.dragging.contains_key(press_target) {730continue; // This entity is already logged as being dragged731}732state.dragging.insert(733*press_target,734DragEntry {735start_pos: location.position,736latest_pos: location.position,737},738);739let drag_start_event = Pointer::new(740pointer_id,741location.clone(),742DragStart {743button,744hit: hit.clone(),745},746*press_target,747);748749commands.trigger(drag_start_event.clone());750message_writers.drag_start_events.write(drag_start_event);751752// Insert dragging over state and emit DragEnter for hovered entities.753for (hovered_entity, hit) in hover_map754.get(&pointer_id)755.iter()756.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))757.filter(|(hovered_entity, _)| *hovered_entity != *press_target)758{759// Inserting the `dragging_over` state here ensures the `DragEnter` event won't be dispatched twice.760state.dragging_over.insert(hovered_entity, hit.clone());761let drag_enter_event = Pointer::new(762pointer_id,763location.clone(),764DragEnter {765button,766dragged: *press_target,767hit: hit.clone(),768},769hovered_entity,770);771commands.trigger(drag_enter_event.clone());772message_writers.drag_enter_events.write(drag_enter_event);773}774}775776// Emit Drag events to the entities we are dragging777for (drag_target, drag) in state.dragging.iter_mut() {778let delta = location.position - drag.latest_pos;779if delta == Vec2::ZERO {780continue; // No need to emit a Drag event if there is no movement781}782let drag_event = Pointer::new(783pointer_id,784location.clone(),785Drag {786button,787distance: location.position - drag.start_pos,788delta,789},790*drag_target,791);792commands.trigger(drag_event.clone());793message_writers.drag_events.write(drag_event);794795// Update drag position796drag.latest_pos = location.position;797798// Emit corresponding DragOver to the hovered entities799for (hovered_entity, hit) in hover_map800.get(&pointer_id)801.iter()802.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))803.filter(|(hovered_entity, _)| *hovered_entity != *drag_target)804{805let drag_over_event = Pointer::new(806pointer_id,807location.clone(),808DragOver {809button,810dragged: *drag_target,811hit: hit.clone(),812},813hovered_entity,814);815commands.trigger(drag_over_event.clone());816message_writers.drag_over_events.write(drag_over_event);817}818}819}820821for (hovered_entity, hit) in hover_map822.get(&pointer_id)823.iter()824.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))825{826// Emit Move events to the entities we are hovering827let move_event = Pointer::new(828pointer_id,829location.clone(),830Move {831hit: hit.clone(),832delta,833},834hovered_entity,835);836commands.trigger(move_event.clone());837message_writers.move_events.write(move_event);838}839}840PointerAction::Scroll { x, y, unit } => {841for (hovered_entity, hit) in hover_map842.get(&pointer_id)843.iter()844.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))845{846// Emit Scroll events to the entities we are hovering847let scroll_event = Pointer::new(848pointer_id,849location.clone(),850Scroll {851unit,852x,853y,854hit: hit.clone(),855},856hovered_entity,857);858commands.trigger(scroll_event.clone());859message_writers.scroll_events.write(scroll_event);860}861}862// Canceled863PointerAction::Cancel => {864// Emit a Cancel to the hovered entity.865for (hovered_entity, hit) in hover_map866.get(&pointer_id)867.iter()868.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))869{870let cancel_event =871Pointer::new(pointer_id, location.clone(), Cancel { hit }, hovered_entity);872commands.trigger(cancel_event.clone());873message_writers.cancel_events.write(cancel_event);874}875// Clear the state for the canceled pointer876pointer_state.clear(pointer_id);877}878}879}880}881882883