1use bevy_camera::NormalizedRenderTarget;
12use bevy_camera::{Camera, RenderTarget};
13use bevy_ecs::prelude::*;
14use bevy_input::mouse::MouseScrollUnit;
15use bevy_input::touch::TouchPhase;
16use bevy_math::Vec2;
17use bevy_platform::collections::HashMap;
18use bevy_reflect::prelude::*;
19use bevy_window::PrimaryWindow;
20
21use uuid::Uuid;
22
23use core::{fmt::Debug, ops::Deref};
24
25use crate::backend::HitData;
26
27#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]
32#[require(PointerLocation, PointerPress, PointerInteraction)]
33#[reflect(Component, Default, Debug, Hash, PartialEq, Clone)]
34pub enum PointerId {
35 #[default]
37 Mouse,
38 Touch(u64),
40 #[reflect(ignore, clone)]
43 Custom(Uuid),
44}
45
46impl PointerId {
47 pub fn is_touch(&self) -> bool {
49 matches!(self, PointerId::Touch(_))
50 }
51 pub fn is_mouse(&self) -> bool {
53 matches!(self, PointerId::Mouse)
54 }
55 pub fn is_custom(&self) -> bool {
57 matches!(self, PointerId::Custom(_))
58 }
59 pub fn get_touch_id(&self) -> Option<u64> {
61 if let PointerId::Touch(id) = self {
62 Some(*id)
63 } else {
64 None
65 }
66 }
67}
68
69#[derive(Debug, Default, Clone, Component, Reflect)]
72#[reflect(Component, Default, Debug, Clone)]
73pub struct PointerInteraction {
74 pub(crate) sorted_entities: Vec<(Entity, HitData)>,
75}
76
77impl PointerInteraction {
78 pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {
80 self.sorted_entities.first()
81 }
82}
83
84impl Deref for PointerInteraction {
85 type Target = Vec<(Entity, HitData)>;
86
87 fn deref(&self) -> &Self::Target {
88 &self.sorted_entities
89 }
90}
91
92#[derive(Debug, Clone, Default, Resource)]
94pub struct PointerMap {
95 inner: HashMap<PointerId, Entity>,
96}
97
98impl PointerMap {
99 pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {
101 self.inner.get(&pointer_id).copied()
102 }
103}
104
105pub fn update_pointer_map(pointers: Query<(Entity, &PointerId)>, mut map: ResMut<PointerMap>) {
107 map.inner.clear();
108 for (entity, id) in &pointers {
109 map.inner.insert(*id, entity);
110 }
111}
112
113#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]
115#[reflect(Component, Default, Debug, PartialEq, Clone)]
116pub struct PointerPress {
117 primary: bool,
118 secondary: bool,
119 middle: bool,
120}
121
122impl PointerPress {
123 #[inline]
125 pub fn is_primary_pressed(&self) -> bool {
126 self.primary
127 }
128
129 #[inline]
131 pub fn is_secondary_pressed(&self) -> bool {
132 self.secondary
133 }
134
135 #[inline]
137 pub fn is_middle_pressed(&self) -> bool {
138 self.middle
139 }
140
141 #[inline]
143 pub fn is_any_pressed(&self) -> bool {
144 self.primary || self.middle || self.secondary
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
150#[reflect(Clone, PartialEq)]
151pub enum PressDirection {
152 Pressed,
154 Released,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
160#[reflect(Clone, PartialEq)]
161pub enum PointerButton {
162 Primary,
164 Secondary,
166 Middle,
168}
169
170impl PointerButton {
171 pub fn iter() -> impl Iterator<Item = PointerButton> {
173 [Self::Primary, Self::Secondary, Self::Middle].into_iter()
174 }
175}
176
177#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]
179#[reflect(Component, Default, Debug, PartialEq, Clone)]
180pub struct PointerLocation {
181 #[reflect(ignore, clone)]
184 pub location: Option<Location>,
185}
186
187impl PointerLocation {
188 pub fn new(location: Location) -> Self {
190 Self {
191 location: Some(location),
192 }
193 }
194
195 pub fn location(&self) -> Option<&Location> {
198 self.location.as_ref()
199 }
200}
201
202#[derive(Debug, Clone, Reflect, PartialEq)]
211#[reflect(Debug, PartialEq, Clone)]
212pub struct Location {
213 pub target: NormalizedRenderTarget,
215 pub position: Vec2,
217}
218
219impl Location {
220 #[inline]
224 pub fn is_in_viewport(
225 &self,
226 camera: &Camera,
227 render_target: &RenderTarget,
228 primary_window: &Query<Entity, With<PrimaryWindow>>,
229 ) -> bool {
230 if render_target
231 .normalize(Some(match primary_window.single() {
232 Ok(w) => w,
233 Err(_) => return false,
234 }))
235 .as_ref()
236 != Some(&self.target)
237 {
238 return false;
239 }
240
241 camera
242 .logical_viewport_rect()
243 .is_some_and(|rect| rect.contains(self.position))
244 }
245}
246
247#[derive(Debug, Clone, Copy, Reflect)]
249#[reflect(Clone)]
250pub enum PointerAction {
251 Press(PointerButton),
253 Release(PointerButton),
255 Move {
257 delta: Vec2,
259 },
260 Scroll {
262 unit: MouseScrollUnit,
264 x: f32,
266 y: f32,
268 phase: TouchPhase,
272 },
273 Cancel,
275}
276
277#[derive(Message, Debug, Clone, Reflect)]
279#[reflect(Clone)]
280pub struct PointerInput {
281 pub pointer_id: PointerId,
283 pub location: Location,
285 pub action: PointerAction,
287}
288
289impl PointerInput {
290 pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
294 PointerInput {
295 pointer_id,
296 location,
297 action,
298 }
299 }
300
301 #[inline]
303 pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {
304 if let PointerAction::Press(button) = self.action {
305 button == target_button
306 } else {
307 false
308 }
309 }
310
311 #[inline]
313 pub fn button_just_released(&self, target_button: PointerButton) -> bool {
314 if let PointerAction::Release(button) = self.action {
315 button == target_button
316 } else {
317 false
318 }
319 }
320
321 pub fn receive(
323 mut events: MessageReader<PointerInput>,
324 mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPress)>,
325 ) {
326 for event in events.read() {
327 match event.action {
328 PointerAction::Press(button) => {
329 pointers
330 .iter_mut()
331 .for_each(|(pointer_id, _, mut pointer)| {
332 if *pointer_id == event.pointer_id {
333 match button {
334 PointerButton::Primary => pointer.primary = true,
335 PointerButton::Secondary => pointer.secondary = true,
336 PointerButton::Middle => pointer.middle = true,
337 }
338 }
339 });
340 }
341 PointerAction::Release(button) => {
342 pointers
343 .iter_mut()
344 .for_each(|(pointer_id, _, mut pointer)| {
345 if *pointer_id == event.pointer_id {
346 match button {
347 PointerButton::Primary => pointer.primary = false,
348 PointerButton::Secondary => pointer.secondary = false,
349 PointerButton::Middle => pointer.middle = false,
350 }
351 }
352 });
353 }
354 PointerAction::Move { .. } => {
355 pointers.iter_mut().for_each(|(id, mut pointer, _)| {
356 if *id == event.pointer_id {
357 pointer.location = Some(event.location.to_owned());
358 }
359 });
360 }
361 _ => {}
362 }
363 }
364 }
365}