//! 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 [`EventReader`] 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(BufferedEvent, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)]62#[entity_event(traversal = PointerTraversal, auto_propagate)]63#[reflect(Component, Debug, Clone)]64pub struct Pointer<E: Debug + Clone + Reflect> {65/// The pointer that triggered this event66pub pointer_id: PointerId,67/// The location of the pointer during this event68pub pointer_location: Location,69/// Additional event-specific data. [`DragDrop`] for example, has an additional field to describe70/// the `Entity` that is being dropped on the target.71pub event: E,72}7374/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.75///76/// This will always traverse to the parent, if the entity being visited has one. Otherwise, it77/// propagates to the pointer's window and stops there.78#[derive(QueryData)]79pub struct PointerTraversal {80child_of: Option<&'static ChildOf>,81window: Option<&'static Window>,82}8384impl<E> Traversal<Pointer<E>> for PointerTraversal85where86E: Debug + Clone + Reflect,87{88fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer<E>) -> Option<Entity> {89let PointerTraversalItem { child_of, window } = item;9091// Send event to parent, if it has one.92if let Some(child_of) = child_of {93return Some(child_of.parent());94};9596// Otherwise, send it to the window entity (unless this is a window entity).97if window.is_none()98&& let NormalizedRenderTarget::Window(window_ref) = pointer.pointer_location.target99{100return Some(window_ref.entity());101}102103None104}105}106107impl<E: Debug + Clone + Reflect> core::fmt::Display for Pointer<E> {108fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {109f.write_fmt(format_args!(110"{:?}, {:.1?}, {:.1?}",111self.pointer_id, self.pointer_location.position, self.event112))113}114}115116impl<E: Debug + Clone + Reflect> core::ops::Deref for Pointer<E> {117type Target = E;118119fn deref(&self) -> &Self::Target {120&self.event121}122}123124impl<E: Debug + Clone + Reflect> Pointer<E> {125/// Construct a new `Pointer<E>` event.126pub fn new(id: PointerId, location: Location, event: E) -> Self {127Self {128pointer_id: id,129pointer_location: location,130event,131}132}133}134135/// Fires when a pointer is canceled, and its current interaction state is dropped.136#[derive(Clone, PartialEq, Debug, Reflect)]137#[reflect(Clone, PartialEq)]138pub struct Cancel {139/// Information about the picking intersection.140pub hit: HitData,141}142143/// Fires when a pointer crosses into the bounds of the [target entity](On::entity).144#[derive(Clone, PartialEq, Debug, Reflect)]145#[reflect(Clone, PartialEq)]146pub struct Over {147/// Information about the picking intersection.148pub hit: HitData,149}150151/// Fires when a pointer crosses out of the bounds of the [target entity](On::entity).152#[derive(Clone, PartialEq, Debug, Reflect)]153#[reflect(Clone, PartialEq)]154pub struct Out {155/// Information about the latest prior picking intersection.156pub hit: HitData,157}158159/// Fires when a pointer button is pressed over the [target entity](On::entity).160#[derive(Clone, PartialEq, Debug, Reflect)]161#[reflect(Clone, PartialEq)]162pub struct Press {163/// Pointer button pressed to trigger this event.164pub button: PointerButton,165/// Information about the picking intersection.166pub hit: HitData,167}168169/// Fires when a pointer button is released over the [target entity](On::entity).170#[derive(Clone, PartialEq, Debug, Reflect)]171#[reflect(Clone, PartialEq)]172pub struct Release {173/// Pointer button lifted to trigger this event.174pub button: PointerButton,175/// Information about the picking intersection.176pub hit: HitData,177}178179/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same180/// [target entity](On::entity) for both events.181#[derive(Clone, PartialEq, Debug, Reflect)]182#[reflect(Clone, PartialEq)]183pub struct Click {184/// Pointer button pressed and lifted to trigger this event.185pub button: PointerButton,186/// Information about the picking intersection.187pub hit: HitData,188/// Duration between the pointer pressed and lifted for this click189pub duration: Duration,190}191192/// Fires while a pointer is moving over the [target entity](On::entity).193#[derive(Clone, PartialEq, Debug, Reflect)]194#[reflect(Clone, PartialEq)]195pub struct Move {196/// Information about the picking intersection.197pub hit: HitData,198/// The change in position since the last move event.199///200/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to201/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider202/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to203/// world-space.204pub delta: Vec2,205}206207/// Fires when the [target entity](On::entity) receives a pointer pressed event followed by a pointer move event.208#[derive(Clone, PartialEq, Debug, Reflect)]209#[reflect(Clone, PartialEq)]210pub struct DragStart {211/// Pointer button pressed and moved to trigger this event.212pub button: PointerButton,213/// Information about the picking intersection.214pub hit: HitData,215}216217/// Fires while the [target entity](On::entity) is being dragged.218#[derive(Clone, PartialEq, Debug, Reflect)]219#[reflect(Clone, PartialEq)]220pub struct Drag {221/// Pointer button pressed and moved to trigger this event.222pub button: PointerButton,223/// The total distance vector of a drag, measured from drag start to the current position.224///225/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to226/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider227/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to228/// world-space.229pub distance: Vec2,230/// The change in position since the last drag event.231///232/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to233/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider234/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to235/// world-space.236pub delta: Vec2,237}238239/// Fires when a pointer is dragging the [target entity](On::entity) and a pointer released event is received.240#[derive(Clone, PartialEq, Debug, Reflect)]241#[reflect(Clone, PartialEq)]242pub struct DragEnd {243/// Pointer button pressed, moved, and released to trigger this event.244pub button: PointerButton,245/// The vector of drag movement measured from start to final pointer position.246///247/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to248/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider249/// using methods on [`Camera`](bevy_camera::Camera) to convert from screen-space to250/// world-space.251pub distance: Vec2,252}253254/// Fires when a pointer dragging the `dragged` entity enters the [target entity](On::entity).255#[derive(Clone, PartialEq, Debug, Reflect)]256#[reflect(Clone, PartialEq)]257pub struct DragEnter {258/// Pointer button pressed to enter drag.259pub button: PointerButton,260/// The entity that was being dragged when the pointer entered the [target entity](On::entity).261pub dragged: Entity,262/// Information about the picking intersection.263pub hit: HitData,264}265266/// Fires while the `dragged` entity is being dragged over the [target entity](On::entity).267#[derive(Clone, PartialEq, Debug, Reflect)]268#[reflect(Clone, PartialEq)]269pub struct DragOver {270/// Pointer button pressed while dragging over.271pub button: PointerButton,272/// The entity that was being dragged when the pointer was over the [target entity](On::entity).273pub dragged: Entity,274/// Information about the picking intersection.275pub hit: HitData,276}277278/// Fires when a pointer dragging the `dragged` entity leaves the [target entity](On::entity).279#[derive(Clone, PartialEq, Debug, Reflect)]280#[reflect(Clone, PartialEq)]281pub struct DragLeave {282/// Pointer button pressed while leaving drag.283pub button: PointerButton,284/// The entity that was being dragged when the pointer left the [target entity](On::entity).285pub dragged: Entity,286/// Information about the latest prior picking intersection.287pub hit: HitData,288}289290/// Fires when a pointer drops the `dropped` entity onto the [target entity](On::entity).291#[derive(Clone, PartialEq, Debug, Reflect)]292#[reflect(Clone, PartialEq)]293pub struct DragDrop {294/// Pointer button released to drop.295pub button: PointerButton,296/// The entity that was dropped onto the [target entity](On::entity).297pub dropped: Entity,298/// Information about the picking intersection.299pub hit: HitData,300}301302/// Dragging state.303#[derive(Clone, PartialEq, Debug, Reflect)]304#[reflect(Clone, PartialEq)]305pub struct DragEntry {306/// The position of the pointer at drag start.307///308/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to309/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider310/// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or311/// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to312/// convert from screen-space to world-space.313pub start_pos: Vec2,314/// The latest position of the pointer during this drag, used to compute deltas.315///316/// This is stored in screen pixels, not world coordinates. Screen pixels go from top-left to317/// bottom-right, whereas (in 2D) world coordinates go from bottom-left to top-right. Consider318/// using [`Camera::viewport_to_world`](bevy_camera::Camera::viewport_to_world) or319/// [`Camera::viewport_to_world_2d`](bevy_camera::Camera::viewport_to_world_2d) to320/// convert from screen-space to world-space.321pub latest_pos: Vec2,322}323324/// Fires while a pointer is scrolling over the [target entity](On::entity).325#[derive(Clone, PartialEq, Debug, Reflect)]326#[reflect(Clone, PartialEq)]327pub struct Scroll {328/// The mouse scroll unit.329pub unit: MouseScrollUnit,330/// The horizontal scroll value.331pub x: f32,332/// The vertical scroll value.333pub y: f32,334/// Information about the picking intersection.335pub hit: HitData,336}337338/// An entry in the cache that drives the `pointer_events` system, storing additional data339/// about pointer button presses.340#[derive(Debug, Clone, Default)]341pub struct PointerButtonState {342/// Stores the press location and start time for each button currently being pressed by the pointer.343pub pressing: HashMap<Entity, (Location, Instant, HitData)>,344/// Stores the starting and current locations for each entity currently being dragged by the pointer.345pub dragging: HashMap<Entity, DragEntry>,346/// Stores the hit data for each entity currently being dragged over by the pointer.347pub dragging_over: HashMap<Entity, HitData>,348}349350/// State for all pointers.351#[derive(Debug, Clone, Default, Resource)]352pub struct PointerState {353/// Pressing and dragging state, organized by pointer and button.354pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,355}356357impl PointerState {358/// Retrieves the current state for a specific pointer and button, if it has been created.359pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {360self.pointer_buttons.get(&(pointer_id, button))361}362363/// Provides write access to the state of a pointer and button, creating it if it does not yet exist.364pub fn get_mut(365&mut self,366pointer_id: PointerId,367button: PointerButton,368) -> &mut PointerButtonState {369self.pointer_buttons370.entry((pointer_id, button))371.or_default()372}373374/// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.375pub fn clear(&mut self, pointer_id: PointerId) {376for button in PointerButton::iter() {377if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {378state.pressing.clear();379state.dragging.clear();380state.dragging_over.clear();381}382}383}384}385386/// A helper system param for accessing the picking event writers.387#[derive(SystemParam)]388pub struct PickingEventWriters<'w> {389cancel_events: EventWriter<'w, Pointer<Cancel>>,390click_events: EventWriter<'w, Pointer<Click>>,391pressed_events: EventWriter<'w, Pointer<Press>>,392drag_drop_events: EventWriter<'w, Pointer<DragDrop>>,393drag_end_events: EventWriter<'w, Pointer<DragEnd>>,394drag_enter_events: EventWriter<'w, Pointer<DragEnter>>,395drag_events: EventWriter<'w, Pointer<Drag>>,396drag_leave_events: EventWriter<'w, Pointer<DragLeave>>,397drag_over_events: EventWriter<'w, Pointer<DragOver>>,398drag_start_events: EventWriter<'w, Pointer<DragStart>>,399scroll_events: EventWriter<'w, Pointer<Scroll>>,400move_events: EventWriter<'w, Pointer<Move>>,401out_events: EventWriter<'w, Pointer<Out>>,402over_events: EventWriter<'w, Pointer<Over>>,403released_events: EventWriter<'w, Pointer<Release>>,404}405406/// Dispatches interaction events to the target entities.407///408/// Within a single frame, events are dispatched in the following order:409/// + [`Out`] → [`DragLeave`].410/// + [`DragEnter`] → [`Over`].411/// + Any number of any of the following:412/// + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].413/// + For each button press: [`Press`] or [`Click`] → [`Release`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].414/// + For each pointer cancellation: [`Cancel`].415///416/// Additionally, across multiple frames, the following are also strictly417/// ordered by the interaction state machine:418/// + When a pointer moves over the target:419/// [`Over`], [`Move`], [`Out`].420/// + When a pointer presses buttons on the target:421/// [`Press`], [`Click`], [`Release`].422/// + When a pointer drags the target:423/// [`DragStart`], [`Drag`], [`DragEnd`].424/// + When a pointer drags something over the target:425/// [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].426/// + When a pointer is canceled:427/// No other events will follow the [`Cancel`] event for that pointer.428///429/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].430/// The rest rely on additional data from the [`PointerInput`] event stream. To431/// receive these events for a custom pointer, you must add [`PointerInput`]432/// events.433///434/// When the pointer goes from hovering entity A to entity B, entity A will435/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever436/// receive both an [`Over`] and and a [`Out`] event during the same frame.437///438/// When we account for event bubbling, this is no longer true. When the hovering focus shifts439/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.440/// In the context of UI, this is especially problematic. Additional hierarchy-aware441/// events will be added in a future release.442///443/// Both [`Click`] and [`Release`] target the entity hovered in the *previous frame*,444/// rather than the current frame. This is because touch pointers hover nothing445/// on the frame they are released. The end effect is that these two events can446/// be received sequentially after an [`Out`] event (but always on the same frame447/// as the [`Out`] event).448///449/// Note: Though it is common for the [`PointerInput`] stream may contain450/// multiple pointer movements and presses each frame, the hover state is451/// determined only by the pointer's *final position*. Since the hover state452/// ultimately determines which entities receive events, this may mean that an453/// entity can receive events from before or after it was actually hovered.454pub fn pointer_events(455// Input456mut input_events: EventReader<PointerInput>,457// ECS State458pointers: Query<&PointerLocation>,459pointer_map: Res<PointerMap>,460hover_map: Res<HoverMap>,461previous_hover_map: Res<PreviousHoverMap>,462mut pointer_state: ResMut<PointerState>,463// Output464mut commands: Commands,465mut event_writers: PickingEventWriters,466) {467// Setup utilities468let now = Instant::now();469let pointer_location = |pointer_id: PointerId| {470pointer_map471.get_entity(pointer_id)472.and_then(|entity| pointers.get(entity).ok())473.and_then(|pointer| pointer.location.clone())474};475476// If the entity was hovered by a specific pointer last frame...477for (pointer_id, hovered_entity, hit) in previous_hover_map478.iter()479.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))480{481// ...but is now not being hovered by that same pointer...482if !hover_map483.get(&pointer_id)484.iter()485.any(|e| e.contains_key(&hovered_entity))486{487let Some(location) = pointer_location(pointer_id) else {488debug!(489"Unable to get location for pointer {:?} during pointer out",490pointer_id491);492continue;493};494495// Always send Out events496let out_event = Pointer::new(pointer_id, location.clone(), Out { hit: hit.clone() });497commands.trigger_targets(out_event.clone(), hovered_entity);498event_writers.out_events.write(out_event);499500// Possibly send DragLeave events501for button in PointerButton::iter() {502let state = pointer_state.get_mut(pointer_id, button);503state.dragging_over.remove(&hovered_entity);504for drag_target in state.dragging.keys() {505let drag_leave_event = Pointer::new(506pointer_id,507location.clone(),508DragLeave {509button,510dragged: *drag_target,511hit: hit.clone(),512},513);514commands.trigger_targets(drag_leave_event.clone(), hovered_entity);515event_writers.drag_leave_events.write(drag_leave_event);516}517}518}519}520521// If the entity is hovered...522for (pointer_id, hovered_entity, hit) in hover_map523.iter()524.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))525{526// ...but was not hovered last frame...527if !previous_hover_map528.get(&pointer_id)529.iter()530.any(|e| e.contains_key(&hovered_entity))531{532let Some(location) = pointer_location(pointer_id) else {533debug!(534"Unable to get location for pointer {:?} during pointer over",535pointer_id536);537continue;538};539540// Possibly send DragEnter events541for button in PointerButton::iter() {542let state = pointer_state.get_mut(pointer_id, button);543544for drag_target in state.dragging.keys() {545state.dragging_over.insert(hovered_entity, hit.clone());546let drag_enter_event = Pointer::new(547pointer_id,548location.clone(),549DragEnter {550button,551dragged: *drag_target,552hit: hit.clone(),553},554);555commands.trigger_targets(drag_enter_event.clone(), hovered_entity);556event_writers.drag_enter_events.write(drag_enter_event);557}558}559560// Always send Over events561let over_event = Pointer::new(pointer_id, location.clone(), Over { hit: hit.clone() });562commands.trigger_targets(over_event.clone(), hovered_entity);563event_writers.over_events.write(over_event);564}565}566567// Dispatch input events...568for PointerInput {569pointer_id,570location,571action,572} in input_events.read().cloned()573{574match action {575PointerAction::Press(button) => {576let state = pointer_state.get_mut(pointer_id, button);577578// If it's a press, emit a Pressed event and mark the hovered entities as pressed579for (hovered_entity, hit) in hover_map580.get(&pointer_id)581.iter()582.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))583{584let pressed_event = Pointer::new(585pointer_id,586location.clone(),587Press {588button,589hit: hit.clone(),590},591);592commands.trigger_targets(pressed_event.clone(), hovered_entity);593event_writers.pressed_events.write(pressed_event);594// Also insert the press into the state595state596.pressing597.insert(hovered_entity, (location.clone(), now, hit));598}599}600PointerAction::Release(button) => {601let state = pointer_state.get_mut(pointer_id, button);602603// Emit Click and Up events on all the previously hovered entities.604for (hovered_entity, hit) in previous_hover_map605.get(&pointer_id)606.iter()607.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))608{609// If this pointer previously pressed the hovered entity, emit a Click event610if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {611let click_event = Pointer::new(612pointer_id,613location.clone(),614Click {615button,616hit: hit.clone(),617duration: now - *press_instant,618},619);620commands.trigger_targets(click_event.clone(), hovered_entity);621event_writers.click_events.write(click_event);622}623// Always send the Release event624let released_event = Pointer::new(625pointer_id,626location.clone(),627Release {628button,629hit: hit.clone(),630},631);632commands.trigger_targets(released_event.clone(), hovered_entity);633event_writers.released_events.write(released_event);634}635636// Then emit the drop events.637for (drag_target, drag) in state.dragging.drain() {638// Emit DragDrop639for (dragged_over, hit) in state.dragging_over.iter() {640let drag_drop_event = Pointer::new(641pointer_id,642location.clone(),643DragDrop {644button,645dropped: drag_target,646hit: hit.clone(),647},648);649commands.trigger_targets(drag_drop_event.clone(), *dragged_over);650event_writers.drag_drop_events.write(drag_drop_event);651}652// Emit DragEnd653let drag_end_event = Pointer::new(654pointer_id,655location.clone(),656DragEnd {657button,658distance: drag.latest_pos - drag.start_pos,659},660);661commands.trigger_targets(drag_end_event.clone(), drag_target);662event_writers.drag_end_events.write(drag_end_event);663// Emit DragLeave664for (dragged_over, hit) in state.dragging_over.iter() {665let drag_leave_event = Pointer::new(666pointer_id,667location.clone(),668DragLeave {669button,670dragged: drag_target,671hit: hit.clone(),672},673);674commands.trigger_targets(drag_leave_event.clone(), *dragged_over);675event_writers.drag_leave_events.write(drag_leave_event);676}677}678679// Finally, we can clear the state of everything relating to presses or drags.680state.pressing.clear();681state.dragging.clear();682state.dragging_over.clear();683}684// Moved685PointerAction::Move { delta } => {686if delta == Vec2::ZERO {687continue; // If delta is zero, the following events will not be triggered.688}689// Triggers during movement even if not over an entity690for button in PointerButton::iter() {691let state = pointer_state.get_mut(pointer_id, button);692693// Emit DragEntry and DragStart the first time we move while pressing an entity694for (press_target, (location, _, hit)) in state.pressing.iter() {695if state.dragging.contains_key(press_target) {696continue; // This entity is already logged as being dragged697}698state.dragging.insert(699*press_target,700DragEntry {701start_pos: location.position,702latest_pos: location.position,703},704);705let drag_start_event = Pointer::new(706pointer_id,707location.clone(),708DragStart {709button,710hit: hit.clone(),711},712);713commands.trigger_targets(drag_start_event.clone(), *press_target);714event_writers.drag_start_events.write(drag_start_event);715}716717// Emit Drag events to the entities we are dragging718for (drag_target, drag) in state.dragging.iter_mut() {719let delta = location.position - drag.latest_pos;720if delta == Vec2::ZERO {721continue; // No need to emit a Drag event if there is no movement722}723let drag_event = Pointer::new(724pointer_id,725location.clone(),726Drag {727button,728distance: location.position - drag.start_pos,729delta,730},731);732commands.trigger_targets(drag_event.clone(), *drag_target);733event_writers.drag_events.write(drag_event);734735// Update drag position736drag.latest_pos = location.position;737738// Emit corresponding DragOver to the hovered entities739for (hovered_entity, hit) in hover_map740.get(&pointer_id)741.iter()742.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))743.filter(|(hovered_entity, _)| *hovered_entity != *drag_target)744{745let drag_over_event = Pointer::new(746pointer_id,747location.clone(),748DragOver {749button,750dragged: *drag_target,751hit: hit.clone(),752},753);754commands.trigger_targets(drag_over_event.clone(), hovered_entity);755event_writers.drag_over_events.write(drag_over_event);756}757}758}759760for (hovered_entity, hit) in hover_map761.get(&pointer_id)762.iter()763.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))764{765// Emit Move events to the entities we are hovering766let move_event = Pointer::new(767pointer_id,768location.clone(),769Move {770hit: hit.clone(),771delta,772},773);774commands.trigger_targets(move_event.clone(), hovered_entity);775event_writers.move_events.write(move_event);776}777}778PointerAction::Scroll { x, y, unit } => {779for (hovered_entity, hit) in hover_map780.get(&pointer_id)781.iter()782.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))783{784// Emit Scroll events to the entities we are hovering785let scroll_event = Pointer::new(786pointer_id,787location.clone(),788Scroll {789unit,790x,791y,792hit: hit.clone(),793},794);795commands.trigger_targets(scroll_event.clone(), hovered_entity);796event_writers.scroll_events.write(scroll_event);797}798}799// Canceled800PointerAction::Cancel => {801// Emit a Cancel to the hovered entity.802for (hovered_entity, hit) in hover_map803.get(&pointer_id)804.iter()805.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))806{807let cancel_event = Pointer::new(pointer_id, location.clone(), Cancel { hit });808commands.trigger_targets(cancel_event.clone(), hovered_entity);809event_writers.cancel_events.write(cancel_event);810}811// Clear the state for the canceled pointer812pointer_state.clear(pointer_id);813}814}815}816}817818819