1use bevy_ecs::prelude::*;
12use bevy_math::{Rect, Vec2};
13use bevy_reflect::prelude::*;
14use bevy_render::camera::{Camera, NormalizedRenderTarget};
15use bevy_utils::HashMap;
16use bevy_window::PrimaryWindow;
17
18use uuid::Uuid;
19
20use core::{fmt::Debug, ops::Deref};
21
22use crate::backend::HitData;
23
24#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]
29#[require(PointerLocation, PointerPress, PointerInteraction)]
30#[reflect(Component, Default, Debug, Hash, PartialEq)]
31pub enum PointerId {
32 #[default]
34 Mouse,
35 Touch(u64),
37 #[reflect(ignore)]
40 Custom(Uuid),
41}
42
43impl PointerId {
44 pub fn is_touch(&self) -> bool {
46 matches!(self, PointerId::Touch(_))
47 }
48 pub fn is_mouse(&self) -> bool {
50 matches!(self, PointerId::Mouse)
51 }
52 pub fn is_custom(&self) -> bool {
54 matches!(self, PointerId::Custom(_))
55 }
56 pub fn get_touch_id(&self) -> Option<u64> {
58 if let PointerId::Touch(id) = self {
59 Some(*id)
60 } else {
61 None
62 }
63 }
64}
65
66#[derive(Debug, Default, Clone, Component, Reflect)]
69#[reflect(Component, Default, Debug)]
70pub struct PointerInteraction {
71 pub(crate) sorted_entities: Vec<(Entity, HitData)>,
72}
73
74impl PointerInteraction {
75 pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {
77 self.sorted_entities.first()
78 }
79}
80
81impl Deref for PointerInteraction {
82 type Target = Vec<(Entity, HitData)>;
83
84 fn deref(&self) -> &Self::Target {
85 &self.sorted_entities
86 }
87}
88
89#[derive(Debug, Clone, Default, Resource)]
91pub struct PointerMap {
92 inner: HashMap<PointerId, Entity>,
93}
94
95impl PointerMap {
96 pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {
98 self.inner.get(&pointer_id).copied()
99 }
100}
101
102pub fn update_pointer_map(pointers: Query<(Entity, &PointerId)>, mut map: ResMut<PointerMap>) {
104 map.inner.clear();
105 for (entity, id) in &pointers {
106 map.inner.insert(*id, entity);
107 }
108}
109
110#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]
112#[reflect(Component, Default, Debug, PartialEq)]
113pub struct PointerPress {
114 primary: bool,
115 secondary: bool,
116 middle: bool,
117}
118
119impl PointerPress {
120 #[inline]
122 pub fn is_primary_pressed(&self) -> bool {
123 self.primary
124 }
125
126 #[inline]
128 pub fn is_secondary_pressed(&self) -> bool {
129 self.secondary
130 }
131
132 #[inline]
134 pub fn is_middle_pressed(&self) -> bool {
135 self.middle
136 }
137
138 #[inline]
140 pub fn is_any_pressed(&self) -> bool {
141 self.primary || self.middle || self.secondary
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
147pub enum PressDirection {
148 Down,
150 Up,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
156pub enum PointerButton {
157 Primary,
159 Secondary,
161 Middle,
163}
164
165impl PointerButton {
166 pub fn iter() -> impl Iterator<Item = PointerButton> {
168 [Self::Primary, Self::Secondary, Self::Middle].into_iter()
169 }
170}
171
172#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]
174#[reflect(Component, Default, Debug, PartialEq)]
175pub struct PointerLocation {
176 #[reflect(ignore)]
179 pub location: Option<Location>,
180}
181
182impl PointerLocation {
183 pub fn new(location: Location) -> Self {
185 Self {
186 location: Some(location),
187 }
188 }
189
190 pub fn location(&self) -> Option<&Location> {
193 self.location.as_ref()
194 }
195}
196
197#[derive(Debug, Clone, Component, Reflect, PartialEq)]
206#[reflect(Component, Debug, PartialEq)]
207pub struct Location {
208 pub target: NormalizedRenderTarget,
210 pub position: Vec2,
212}
213
214impl Location {
215 #[inline]
219 pub fn is_in_viewport(
220 &self,
221 camera: &Camera,
222 primary_window: &Query<Entity, With<PrimaryWindow>>,
223 ) -> bool {
224 if camera
225 .target
226 .normalize(Some(match primary_window.get_single() {
227 Ok(w) => w,
228 Err(_) => return false,
229 }))
230 .as_ref()
231 != Some(&self.target)
232 {
233 return false;
234 }
235
236 let position = Vec2::new(self.position.x, self.position.y);
237
238 camera
239 .logical_viewport_rect()
240 .map(|Rect { min, max }| {
241 (position - min).min_element() >= 0.0 && (position - max).max_element() <= 0.0
242 })
243 .unwrap_or(false)
244 }
245}
246
247#[derive(Debug, Clone, Copy, Reflect)]
249pub enum PointerAction {
250 Pressed {
252 direction: PressDirection,
254 button: PointerButton,
256 },
257 Moved {
259 delta: Vec2,
261 },
262 Canceled,
264}
265
266#[derive(Event, Debug, Clone, Reflect)]
268pub struct PointerInput {
269 pub pointer_id: PointerId,
271 pub location: Location,
273 pub action: PointerAction,
275}
276
277impl PointerInput {
278 pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
282 PointerInput {
283 pointer_id,
284 location,
285 action,
286 }
287 }
288
289 #[inline]
291 pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {
292 if let PointerAction::Pressed { direction, button } = self.action {
293 direction == PressDirection::Down && button == target_button
294 } else {
295 false
296 }
297 }
298
299 #[inline]
301 pub fn button_just_released(&self, target_button: PointerButton) -> bool {
302 if let PointerAction::Pressed { direction, button } = self.action {
303 direction == PressDirection::Up && button == target_button
304 } else {
305 false
306 }
307 }
308
309 pub fn receive(
311 mut events: EventReader<PointerInput>,
312 mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPress)>,
313 ) {
314 for event in events.read() {
315 match event.action {
316 PointerAction::Pressed { direction, button } => {
317 pointers
318 .iter_mut()
319 .for_each(|(pointer_id, _, mut pointer)| {
320 if *pointer_id == event.pointer_id {
321 let is_down = direction == PressDirection::Down;
322 match button {
323 PointerButton::Primary => pointer.primary = is_down,
324 PointerButton::Secondary => pointer.secondary = is_down,
325 PointerButton::Middle => pointer.middle = is_down,
326 }
327 }
328 });
329 }
330 PointerAction::Moved { .. } => {
331 pointers.iter_mut().for_each(|(id, mut pointer, _)| {
332 if *id == event.pointer_id {
333 pointer.location = Some(event.location.to_owned());
334 }
335 });
336 }
337 _ => {}
338 }
339 }
340 }
341}