//! 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}352353/// State for all pointers.354#[derive(Debug, Clone, Default, Resource)]355pub struct PointerState {356/// Pressing and dragging state, organized by pointer and button.357pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,358}359360impl PointerState {361/// Retrieves the current state for a specific pointer and button, if it has been created.362pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {363self.pointer_buttons.get(&(pointer_id, button))364}365366/// Provides write access to the state of a pointer and button, creating it if it does not yet exist.367pub fn get_mut(368&mut self,369pointer_id: PointerId,370button: PointerButton,371) -> &mut PointerButtonState {372self.pointer_buttons373.entry((pointer_id, button))374.or_default()375}376377/// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.378pub fn clear(&mut self, pointer_id: PointerId) {379for button in PointerButton::iter() {380if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {381state.pressing.clear();382state.dragging.clear();383state.dragging_over.clear();384}385}386}387}388389/// A helper system param for accessing the picking event writers.390#[derive(SystemParam)]391pub struct PickingMessageWriters<'w> {392cancel_events: MessageWriter<'w, Pointer<Cancel>>,393click_events: MessageWriter<'w, Pointer<Click>>,394pressed_events: MessageWriter<'w, Pointer<Press>>,395drag_drop_events: MessageWriter<'w, Pointer<DragDrop>>,396drag_end_events: MessageWriter<'w, Pointer<DragEnd>>,397drag_enter_events: MessageWriter<'w, Pointer<DragEnter>>,398drag_events: MessageWriter<'w, Pointer<Drag>>,399drag_leave_events: MessageWriter<'w, Pointer<DragLeave>>,400drag_over_events: MessageWriter<'w, Pointer<DragOver>>,401drag_start_events: MessageWriter<'w, Pointer<DragStart>>,402scroll_events: MessageWriter<'w, Pointer<Scroll>>,403move_events: MessageWriter<'w, Pointer<Move>>,404out_events: MessageWriter<'w, Pointer<Out>>,405over_events: MessageWriter<'w, Pointer<Over>>,406released_events: MessageWriter<'w, Pointer<Release>>,407}408409/// Dispatches interaction events to the target entities.410///411/// Within a single frame, events are dispatched in the following order:412/// + [`Out`] → [`DragLeave`].413/// + [`DragEnter`] → [`Over`].414/// + Any number of any of the following:415/// + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].416/// + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].417/// + For each pointer cancellation: [`Cancel`].418///419/// Additionally, across multiple frames, the following are also strictly420/// ordered by the interaction state machine:421/// + When a pointer moves over the target:422/// [`Over`], [`Move`], [`Out`].423/// + When a pointer presses buttons on the target:424/// [`Press`], [`Click`], [`Release`].425/// + When a pointer drags the target:426/// [`DragStart`], [`Drag`], [`DragEnd`].427/// + When a pointer drags something over the target:428/// [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].429/// + When a pointer is canceled:430/// No other events will follow the [`Cancel`] event for that pointer.431///432/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].433/// The rest rely on additional data from the [`PointerInput`] event stream. To434/// receive these events for a custom pointer, you must add [`PointerInput`]435/// events.436///437/// When the pointer goes from hovering entity A to entity B, entity A will438/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever439/// receive both an [`Over`] and and a [`Out`] event during the same frame.440///441/// When we account for event bubbling, this is no longer true. When the hovering focus shifts442/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.443/// In the context of UI, this is especially problematic. Additional hierarchy-aware444/// events will be added in a future release.445///446/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,447/// rather than the current frame. This is because touch pointers hover nothing448/// on the frame they are released. The end effect is that these two events can449/// be received sequentially after an [`Out`] event (but always on the same frame450/// as the [`Out`] event).451///452/// Note: Though it is common for the [`PointerInput`] stream may contain453/// multiple pointer movements and presses each frame, the hover state is454/// determined only by the pointer's *final position*. Since the hover state455/// ultimately determines which entities receive events, this may mean that an456/// entity can receive events from before or after it was actually hovered.457pub fn pointer_events(458// Input459mut input_events: MessageReader<PointerInput>,460// ECS State461pointers: Query<&PointerLocation>,462pointer_map: Res<PointerMap>,463hover_map: Res<HoverMap>,464previous_hover_map: Res<PreviousHoverMap>,465mut pointer_state: ResMut<PointerState>,466// Output467mut commands: Commands,468mut message_writers: PickingMessageWriters,469) {470// Setup utilities471let now = Instant::now();472let pointer_location = |pointer_id: PointerId| {473pointer_map474.get_entity(pointer_id)475.and_then(|entity| pointers.get(entity).ok())476.and_then(|pointer| pointer.location.clone())477};478479// If the entity was hovered by a specific pointer last frame...480for (pointer_id, hovered_entity, hit) in previous_hover_map481.iter()482.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))483{484// ...but is now not being hovered by that same pointer...485if !hover_map486.get(&pointer_id)487.iter()488.any(|e| e.contains_key(&hovered_entity))489{490let Some(location) = pointer_location(pointer_id) else {491debug!(492"Unable to get location for pointer {:?} during pointer out",493pointer_id494);495continue;496};497498// Always send Out events499let out_event = Pointer::new(500pointer_id,501location.clone(),502Out { hit: hit.clone() },503hovered_entity,504);505commands.trigger(out_event.clone());506message_writers.out_events.write(out_event);507508// Possibly send DragLeave events509for button in PointerButton::iter() {510let state = pointer_state.get_mut(pointer_id, button);511state.dragging_over.remove(&hovered_entity);512for drag_target in state.dragging.keys() {513let drag_leave_event = Pointer::new(514pointer_id,515location.clone(),516DragLeave {517button,518dragged: *drag_target,519hit: hit.clone(),520},521hovered_entity,522);523commands.trigger(drag_leave_event.clone());524message_writers.drag_leave_events.write(drag_leave_event);525}526}527}528}529530// If the entity is hovered...531for (pointer_id, hovered_entity, hit) in hover_map532.iter()533.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))534{535// ...but was not hovered last frame...536if !previous_hover_map537.get(&pointer_id)538.iter()539.any(|e| e.contains_key(&hovered_entity))540{541let Some(location) = pointer_location(pointer_id) else {542debug!(543"Unable to get location for pointer {:?} during pointer over",544pointer_id545);546continue;547};548549// Possibly send DragEnter events550for button in PointerButton::iter() {551let state = pointer_state.get_mut(pointer_id, button);552553for drag_target in state.dragging.keys() {554state.dragging_over.insert(hovered_entity, hit.clone());555let drag_enter_event = Pointer::new(556pointer_id,557location.clone(),558DragEnter {559button,560dragged: *drag_target,561hit: hit.clone(),562},563hovered_entity,564);565commands.trigger(drag_enter_event.clone());566message_writers.drag_enter_events.write(drag_enter_event);567}568}569570// Always send Over events571let over_event = Pointer::new(572pointer_id,573location.clone(),574Over { hit: hit.clone() },575hovered_entity,576);577commands.trigger(over_event.clone());578message_writers.over_events.write(over_event);579}580}581582// Dispatch input events...583for PointerInput {584pointer_id,585location,586action,587} in input_events.read().cloned()588{589match action {590PointerAction::Press(button) => {591let state = pointer_state.get_mut(pointer_id, button);592593// If it's a press, emit a Pressed event and mark the hovered entities as pressed594for (hovered_entity, hit) in hover_map595.get(&pointer_id)596.iter()597.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))598{599let pressed_event = Pointer::new(600pointer_id,601location.clone(),602Press {603button,604hit: hit.clone(),605},606hovered_entity,607);608commands.trigger(pressed_event.clone());609message_writers.pressed_events.write(pressed_event);610// Also insert the press into the state611state612.pressing613.insert(hovered_entity, (location.clone(), now, hit));614}615}616PointerAction::Release(button) => {617let state = pointer_state.get_mut(pointer_id, button);618619// Emit Click and Up events on all the previously hovered entities.620for (hovered_entity, hit) in previous_hover_map621.get(&pointer_id)622.iter()623.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))624{625// If this pointer previously pressed the hovered entity, emit a Click event626if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {627let click_event = Pointer::new(628pointer_id,629location.clone(),630Click {631button,632hit: hit.clone(),633duration: now - *press_instant,634},635hovered_entity,636);637commands.trigger(click_event.clone());638message_writers.click_events.write(click_event);639}640// Always send the Release event641let released_event = Pointer::new(642pointer_id,643location.clone(),644Release {645button,646hit: hit.clone(),647},648hovered_entity,649);650commands.trigger(released_event.clone());651message_writers.released_events.write(released_event);652}653654// Then emit the drop events.655for (drag_target, drag) in state.dragging.drain() {656// Emit DragDrop657for (dragged_over, hit) in state.dragging_over.iter() {658let drag_drop_event = Pointer::new(659pointer_id,660location.clone(),661DragDrop {662button,663dropped: drag_target,664hit: hit.clone(),665},666*dragged_over,667);668commands.trigger(drag_drop_event.clone());669message_writers.drag_drop_events.write(drag_drop_event);670}671// Emit DragEnd672let drag_end_event = Pointer::new(673pointer_id,674location.clone(),675DragEnd {676button,677distance: drag.latest_pos - drag.start_pos,678},679drag_target,680);681commands.trigger(drag_end_event.clone());682message_writers.drag_end_events.write(drag_end_event);683// Emit DragLeave684for (dragged_over, hit) in state.dragging_over.iter() {685let drag_leave_event = Pointer::new(686pointer_id,687location.clone(),688DragLeave {689button,690dragged: drag_target,691hit: hit.clone(),692},693*dragged_over,694);695commands.trigger(drag_leave_event.clone());696message_writers.drag_leave_events.write(drag_leave_event);697}698}699700// Finally, we can clear the state of everything relating to presses or drags.701state.pressing.clear();702state.dragging.clear();703state.dragging_over.clear();704}705// Moved706PointerAction::Move { delta } => {707if delta == Vec2::ZERO {708continue; // If delta is zero, the following events will not be triggered.709}710// Triggers during movement even if not over an entity711for button in PointerButton::iter() {712let state = pointer_state.get_mut(pointer_id, button);713714// Emit DragEntry and DragStart the first time we move while pressing an entity715for (press_target, (location, _, hit)) in state.pressing.iter() {716if state.dragging.contains_key(press_target) {717continue; // This entity is already logged as being dragged718}719state.dragging.insert(720*press_target,721DragEntry {722start_pos: location.position,723latest_pos: location.position,724},725);726let drag_start_event = Pointer::new(727pointer_id,728location.clone(),729DragStart {730button,731hit: hit.clone(),732},733*press_target,734);735commands.trigger(drag_start_event.clone());736message_writers.drag_start_events.write(drag_start_event);737}738739// Emit Drag events to the entities we are dragging740for (drag_target, drag) in state.dragging.iter_mut() {741let delta = location.position - drag.latest_pos;742if delta == Vec2::ZERO {743continue; // No need to emit a Drag event if there is no movement744}745let drag_event = Pointer::new(746pointer_id,747location.clone(),748Drag {749button,750distance: location.position - drag.start_pos,751delta,752},753*drag_target,754);755commands.trigger(drag_event.clone());756message_writers.drag_events.write(drag_event);757758// Update drag position759drag.latest_pos = location.position;760761// Emit corresponding DragOver to the hovered entities762for (hovered_entity, hit) in hover_map763.get(&pointer_id)764.iter()765.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))766.filter(|(hovered_entity, _)| *hovered_entity != *drag_target)767{768let drag_over_event = Pointer::new(769pointer_id,770location.clone(),771DragOver {772button,773dragged: *drag_target,774hit: hit.clone(),775},776hovered_entity,777);778commands.trigger(drag_over_event.clone());779message_writers.drag_over_events.write(drag_over_event);780}781}782}783784for (hovered_entity, hit) in hover_map785.get(&pointer_id)786.iter()787.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))788{789// Emit Move events to the entities we are hovering790let move_event = Pointer::new(791pointer_id,792location.clone(),793Move {794hit: hit.clone(),795delta,796},797hovered_entity,798);799commands.trigger(move_event.clone());800message_writers.move_events.write(move_event);801}802}803PointerAction::Scroll { x, y, unit } => {804for (hovered_entity, hit) in hover_map805.get(&pointer_id)806.iter()807.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))808{809// Emit Scroll events to the entities we are hovering810let scroll_event = Pointer::new(811pointer_id,812location.clone(),813Scroll {814unit,815x,816y,817hit: hit.clone(),818},819hovered_entity,820);821commands.trigger(scroll_event.clone());822message_writers.scroll_events.write(scroll_event);823}824}825// Canceled826PointerAction::Cancel => {827// Emit a Cancel to the hovered entity.828for (hovered_entity, hit) in hover_map829.get(&pointer_id)830.iter()831.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))832{833let cancel_event =834Pointer::new(pointer_id, location.clone(), Cancel { hit }, hovered_entity);835commands.trigger(cancel_event.clone());836message_writers.cancel_events.write(cancel_event);837}838// Clear the state for the canceled pointer839pointer_state.clear(pointer_id);840}841}842}843}844845846