//! Types and systems for pointer inputs, such as position and buttons.1//!2//! The picking system is built around the concept of a 'Pointer', which is an3//! abstract representation of a user input with a specific screen location. The cursor4//! and touch input is provided under [`input`](`crate::input`), but you can also implement5//! your own custom pointers by supplying a unique ID.6//!7//! The purpose of this module is primarily to provide a common interface that can be8//! driven by lower-level input devices and consumed by higher-level interaction systems.910use bevy_camera::Camera;11use bevy_camera::NormalizedRenderTarget;12use bevy_ecs::prelude::*;13use bevy_input::mouse::MouseScrollUnit;14use bevy_math::Vec2;15use bevy_platform::collections::HashMap;16use bevy_reflect::prelude::*;17use bevy_window::PrimaryWindow;1819use uuid::Uuid;2021use core::{fmt::Debug, ops::Deref};2223use crate::backend::HitData;2425/// Identifies a unique pointer entity. `Mouse` and `Touch` pointers are automatically spawned.26///27/// This component is needed because pointers can be spawned and despawned, but they need to have a28/// stable ID that persists regardless of the Entity they are associated with.29#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]30#[require(PointerLocation, PointerPress, PointerInteraction)]31#[reflect(Component, Default, Debug, Hash, PartialEq, Clone)]32pub enum PointerId {33/// The mouse pointer.34#[default]35Mouse,36/// A touch input, usually numbered by window touch events from `winit`.37Touch(u64),38/// A custom, uniquely identified pointer. Useful for mocking inputs or implementing a software39/// controlled cursor.40#[reflect(ignore, clone)]41Custom(Uuid),42}4344impl PointerId {45/// Returns true if the pointer is a touch input.46pub fn is_touch(&self) -> bool {47matches!(self, PointerId::Touch(_))48}49/// Returns true if the pointer is the mouse.50pub fn is_mouse(&self) -> bool {51matches!(self, PointerId::Mouse)52}53/// Returns true if the pointer is a custom input.54pub fn is_custom(&self) -> bool {55matches!(self, PointerId::Custom(_))56}57/// Returns the touch id if the pointer is a touch input.58pub fn get_touch_id(&self) -> Option<u64> {59if let PointerId::Touch(id) = self {60Some(*id)61} else {62None63}64}65}6667/// Holds a list of entities this pointer is currently interacting with, sorted from nearest to68/// farthest.69#[derive(Debug, Default, Clone, Component, Reflect)]70#[reflect(Component, Default, Debug, Clone)]71pub struct PointerInteraction {72pub(crate) sorted_entities: Vec<(Entity, HitData)>,73}7475impl PointerInteraction {76/// Returns the nearest hit entity and data about that intersection.77pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {78self.sorted_entities.first()79}80}8182impl Deref for PointerInteraction {83type Target = Vec<(Entity, HitData)>;8485fn deref(&self) -> &Self::Target {86&self.sorted_entities87}88}8990/// A resource that maps each [`PointerId`] to their [`Entity`] for easy lookups.91#[derive(Debug, Clone, Default, Resource)]92pub struct PointerMap {93inner: HashMap<PointerId, Entity>,94}9596impl PointerMap {97/// Get the [`Entity`] of the supplied [`PointerId`].98pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {99self.inner.get(&pointer_id).copied()100}101}102103/// Update the [`PointerMap`] resource with the current frame's data.104pub fn update_pointer_map(pointers: Query<(Entity, &PointerId)>, mut map: ResMut<PointerMap>) {105map.inner.clear();106for (entity, id) in &pointers {107map.inner.insert(*id, entity);108}109}110111/// Tracks the state of the pointer's buttons in response to [`PointerInput`] events.112#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]113#[reflect(Component, Default, Debug, PartialEq, Clone)]114pub struct PointerPress {115primary: bool,116secondary: bool,117middle: bool,118}119120impl PointerPress {121/// Returns true if the primary pointer button is pressed.122#[inline]123pub fn is_primary_pressed(&self) -> bool {124self.primary125}126127/// Returns true if the secondary pointer button is pressed.128#[inline]129pub fn is_secondary_pressed(&self) -> bool {130self.secondary131}132133/// Returns true if the middle (tertiary) pointer button is pressed.134#[inline]135pub fn is_middle_pressed(&self) -> bool {136self.middle137}138139/// Returns true if any pointer button is pressed.140#[inline]141pub fn is_any_pressed(&self) -> bool {142self.primary || self.middle || self.secondary143}144}145146/// The stage of the pointer button press event147#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]148#[reflect(Clone, PartialEq)]149pub enum PressDirection {150/// The pointer button was just pressed151Pressed,152/// The pointer button was just released153Released,154}155156/// The button that was just pressed or released157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]158#[reflect(Clone, PartialEq)]159pub enum PointerButton {160/// The primary pointer button161Primary,162/// The secondary pointer button163Secondary,164/// The tertiary pointer button165Middle,166}167168impl PointerButton {169/// Iterator over all buttons that a pointer can have.170pub fn iter() -> impl Iterator<Item = PointerButton> {171[Self::Primary, Self::Secondary, Self::Middle].into_iter()172}173}174175/// Component that tracks a pointer's current [`Location`].176#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]177#[reflect(Component, Default, Debug, PartialEq, Clone)]178pub struct PointerLocation {179/// The [`Location`] of the pointer. Note that a location is both the target, and the position180/// on the target.181#[reflect(ignore, clone)]182pub location: Option<Location>,183}184185impl PointerLocation {186///Returns a [`PointerLocation`] associated with the given location187pub fn new(location: Location) -> Self {188Self {189location: Some(location),190}191}192193/// Returns `Some(&`[`Location`]`)` if the pointer is active, or `None` if the pointer is194/// inactive.195pub fn location(&self) -> Option<&Location> {196self.location.as_ref()197}198}199200/// The location of a pointer, including the current [`NormalizedRenderTarget`], and the x/y201/// position of the pointer on this render target.202///203/// Note that:204/// - a pointer can move freely between render targets205/// - a pointer is not associated with a [`Camera`] because multiple cameras can target the same206/// render target. It is up to picking backends to associate a Pointer's `Location` with a207/// specific `Camera`, if any.208#[derive(Debug, Clone, Reflect, PartialEq)]209#[reflect(Debug, PartialEq, Clone)]210pub struct Location {211/// The [`NormalizedRenderTarget`] associated with the pointer, usually a window.212pub target: NormalizedRenderTarget,213/// The position of the pointer in the `target`.214pub position: Vec2,215}216217impl Location {218/// Returns `true` if this pointer's [`Location`] is within the [`Camera`]'s viewport.219///220/// Note this returns `false` if the location and camera have different render targets.221#[inline]222pub fn is_in_viewport(223&self,224camera: &Camera,225primary_window: &Query<Entity, With<PrimaryWindow>>,226) -> bool {227if camera228.target229.normalize(Some(match primary_window.single() {230Ok(w) => w,231Err(_) => return false,232}))233.as_ref()234!= Some(&self.target)235{236return false;237}238239camera240.logical_viewport_rect()241.is_some_and(|rect| rect.contains(self.position))242}243}244245/// Event sent to drive a pointer.246#[derive(Debug, Clone, Copy, Reflect)]247#[reflect(Clone)]248pub enum PointerAction {249/// Causes the pointer to press a button.250Press(PointerButton),251/// Causes the pointer to release a button.252Release(PointerButton),253/// Move the pointer.254Move {255/// How much the pointer moved from the previous position.256delta: Vec2,257},258/// Scroll the pointer259Scroll {260/// The mouse scroll unit.261unit: MouseScrollUnit,262/// The horizontal scroll value.263x: f32,264/// The vertical scroll value.265y: f32,266},267/// Cancel the pointer. Often used for touch events.268Cancel,269}270271/// An input event effecting a pointer.272#[derive(BufferedEvent, Debug, Clone, Reflect)]273#[reflect(Clone)]274pub struct PointerInput {275/// The id of the pointer.276pub pointer_id: PointerId,277/// The location of the pointer. For [`PointerAction::Move`], this is the location after the movement.278pub location: Location,279/// The action that the event describes.280pub action: PointerAction,281}282283impl PointerInput {284/// Creates a new pointer input event.285///286/// Note that `location` refers to the position of the pointer *after* the event occurred.287pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {288PointerInput {289pointer_id,290location,291action,292}293}294295/// Returns true if the `target_button` of this pointer was just pressed.296#[inline]297pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {298if let PointerAction::Press(button) = self.action {299button == target_button300} else {301false302}303}304305/// Returns true if the `target_button` of this pointer was just released.306#[inline]307pub fn button_just_released(&self, target_button: PointerButton) -> bool {308if let PointerAction::Release(button) = self.action {309button == target_button310} else {311false312}313}314315/// Updates pointer entities according to the input events.316pub fn receive(317mut events: EventReader<PointerInput>,318mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPress)>,319) {320for event in events.read() {321match event.action {322PointerAction::Press(button) => {323pointers324.iter_mut()325.for_each(|(pointer_id, _, mut pointer)| {326if *pointer_id == event.pointer_id {327match button {328PointerButton::Primary => pointer.primary = true,329PointerButton::Secondary => pointer.secondary = true,330PointerButton::Middle => pointer.middle = true,331}332}333});334}335PointerAction::Release(button) => {336pointers337.iter_mut()338.for_each(|(pointer_id, _, mut pointer)| {339if *pointer_id == event.pointer_id {340match button {341PointerButton::Primary => pointer.primary = false,342PointerButton::Secondary => pointer.secondary = false,343PointerButton::Middle => pointer.middle = false,344}345}346});347}348PointerAction::Move { .. } => {349pointers.iter_mut().for_each(|(id, mut pointer, _)| {350if *id == event.pointer_id {351pointer.location = Some(event.location.to_owned());352}353});354}355_ => {}356}357}358}359}360361362