1use crate::backend::HitData;
12use bevy_camera::{Camera, NormalizedRenderTarget, RenderTarget};
13use bevy_ecs::{lifecycle::HookContext, prelude::*, world::DeferredWorld};
14use bevy_input::{mouse::MouseScrollUnit, touch::TouchPhase};
15use bevy_math::Vec2;
16use bevy_platform::collections::HashMap;
17use bevy_reflect::prelude::*;
18use bevy_window::PrimaryWindow;
19use core::{fmt::Debug, ops::Deref};
20use uuid::Uuid;
21
22#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]
27#[component(immutable, on_insert, on_remove)]
28#[require(PointerLocation, PointerPressState, PointerInteraction)]
29#[reflect(Component, Default, Debug, Hash, PartialEq, Clone)]
30pub enum PointerId {
31 #[default]
33 Mouse,
34 Touch(u64),
36 #[reflect(ignore, clone)]
39 Custom(Uuid),
40}
41
42impl PointerId {
43 fn on_insert(mut world: DeferredWorld, context: HookContext) {
44 if let Some(id) = world.entity(context.entity).get::<PointerId>().copied() {
45 world
46 .resource_mut::<PointerMap>()
47 .inner
48 .insert(id, context.entity);
49 }
50 }
51
52 fn on_remove(mut world: DeferredWorld, context: HookContext) {
53 if let Some(id) = world.entity(context.entity).get::<PointerId>().copied() {
54 world.resource_mut::<PointerMap>().inner.remove(&id);
55 }
56 }
57
58 pub fn is_touch(&self) -> bool {
60 matches!(self, PointerId::Touch(_))
61 }
62 pub fn is_mouse(&self) -> bool {
64 matches!(self, PointerId::Mouse)
65 }
66 pub fn is_custom(&self) -> bool {
68 matches!(self, PointerId::Custom(_))
69 }
70 pub fn get_touch_id(&self) -> Option<u64> {
72 if let PointerId::Touch(id) = self {
73 Some(*id)
74 } else {
75 None
76 }
77 }
78}
79
80#[derive(Debug, Default, Clone, Component, Reflect)]
83#[reflect(Component, Default, Debug, Clone)]
84pub struct PointerInteraction {
85 pub(crate) sorted_entities: Vec<(Entity, HitData)>,
86}
87
88impl PointerInteraction {
89 pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {
91 self.sorted_entities.first()
92 }
93}
94
95impl Deref for PointerInteraction {
96 type Target = Vec<(Entity, HitData)>;
97
98 fn deref(&self) -> &Self::Target {
99 &self.sorted_entities
100 }
101}
102
103#[derive(Debug, Clone, Default, Resource, Reflect)]
105#[reflect(Debug, Clone, Default, Resource)]
106pub struct PointerMap {
107 inner: HashMap<PointerId, Entity>,
108}
109
110impl PointerMap {
111 pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {
113 self.inner.get(&pointer_id).copied()
114 }
115}
116
117#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]
119#[reflect(Component, Default, Debug, PartialEq, Clone)]
120pub struct PointerPressState {
121 primary: bool,
122 secondary: bool,
123 middle: bool,
124}
125
126impl PointerPressState {
127 #[inline]
129 pub fn is_primary_pressed(&self) -> bool {
130 self.primary
131 }
132
133 #[inline]
135 pub fn is_secondary_pressed(&self) -> bool {
136 self.secondary
137 }
138
139 #[inline]
141 pub fn is_middle_pressed(&self) -> bool {
142 self.middle
143 }
144
145 #[inline]
147 pub fn is_any_pressed(&self) -> bool {
148 self.primary || self.middle || self.secondary
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
154#[reflect(Clone, PartialEq)]
155pub enum PressDirection {
156 Pressed,
158 Released,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
164#[reflect(Clone, PartialEq)]
165pub enum PointerButton {
166 Primary,
168 Secondary,
170 Middle,
172}
173
174impl PointerButton {
175 pub fn iter() -> impl Iterator<Item = PointerButton> {
177 [Self::Primary, Self::Secondary, Self::Middle].into_iter()
178 }
179}
180
181#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]
183#[reflect(Component, Default, Debug, PartialEq, Clone)]
184pub struct PointerLocation {
185 #[reflect(ignore, clone)]
188 pub location: Option<Location>,
189}
190
191impl PointerLocation {
192 pub fn new(location: Location) -> Self {
194 Self {
195 location: Some(location),
196 }
197 }
198
199 pub fn location(&self) -> Option<&Location> {
202 self.location.as_ref()
203 }
204}
205
206#[derive(Debug, Clone, Reflect, PartialEq)]
215#[reflect(Debug, PartialEq, Clone)]
216pub struct Location {
217 pub target: NormalizedRenderTarget,
219 pub position: Vec2,
221}
222
223impl Location {
224 #[inline]
228 pub fn is_in_viewport(
229 &self,
230 camera: &Camera,
231 render_target: &RenderTarget,
232 primary_window: &Query<Entity, With<PrimaryWindow>>,
233 ) -> bool {
234 if render_target
235 .normalize(Some(match primary_window.single() {
236 Ok(w) => w,
237 Err(_) => return false,
238 }))
239 .as_ref()
240 != Some(&self.target)
241 {
242 return false;
243 }
244
245 camera
246 .logical_viewport_rect()
247 .is_some_and(|rect| rect.contains(self.position))
248 }
249}
250
251#[derive(Debug, Clone, Copy, Reflect)]
253#[reflect(Clone)]
254pub enum PointerAction {
255 Press(PointerButton),
257 Release(PointerButton),
259 Move {
261 delta: Vec2,
263 },
264 Scroll {
266 unit: MouseScrollUnit,
268 x: f32,
270 y: f32,
272 phase: TouchPhase,
276 },
277 Cancel,
279}
280
281#[derive(Message, Debug, Clone, Reflect)]
283#[reflect(Clone)]
284pub struct PointerInput {
285 pub pointer_id: PointerId,
287 pub location: Location,
289 pub action: PointerAction,
291}
292
293impl PointerInput {
294 pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
298 PointerInput {
299 pointer_id,
300 location,
301 action,
302 }
303 }
304
305 #[inline]
307 pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {
308 if let PointerAction::Press(button) = self.action {
309 button == target_button
310 } else {
311 false
312 }
313 }
314
315 #[inline]
317 pub fn button_just_released(&self, target_button: PointerButton) -> bool {
318 if let PointerAction::Release(button) = self.action {
319 button == target_button
320 } else {
321 false
322 }
323 }
324
325 pub fn receive(
327 mut events: MessageReader<PointerInput>,
328 mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPressState)>,
329 ) {
330 for event in events.read() {
331 match event.action {
332 PointerAction::Press(button) => {
333 pointers
334 .iter_mut()
335 .for_each(|(pointer_id, _, mut pointer)| {
336 if *pointer_id == event.pointer_id {
337 match button {
338 PointerButton::Primary => pointer.primary = true,
339 PointerButton::Secondary => pointer.secondary = true,
340 PointerButton::Middle => pointer.middle = true,
341 }
342 }
343 });
344 }
345 PointerAction::Release(button) => {
346 pointers
347 .iter_mut()
348 .for_each(|(pointer_id, _, mut pointer)| {
349 if *pointer_id == event.pointer_id {
350 match button {
351 PointerButton::Primary => pointer.primary = false,
352 PointerButton::Secondary => pointer.secondary = false,
353 PointerButton::Middle => pointer.middle = false,
354 }
355 }
356 });
357 }
358 PointerAction::Move { .. } => {
359 pointers.iter_mut().for_each(|(id, mut pointer, _)| {
360 if *id == event.pointer_id {
361 pointer.location = Some(event.location.to_owned());
362 }
363 });
364 }
365 _ => {}
366 }
367 }
368 }
369}