bevy_input/
touch.rs

1//! The touch input functionality.
2
3use bevy_ecs::{
4    entity::Entity,
5    event::{Event, EventReader},
6    system::{ResMut, Resource},
7};
8use bevy_math::Vec2;
9#[cfg(feature = "bevy_reflect")]
10use bevy_reflect::Reflect;
11use bevy_utils::HashMap;
12
13#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
14use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
15
16/// A touch input event.
17///
18/// ## Logic
19///
20/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique
21/// identifier for the finger is generated. When the finger is lifted, the [`TouchPhase::Ended`]
22/// event is generated with the same finger id.
23///
24/// After a [`TouchPhase::Started`] event has been emitted, there may be zero or more [`TouchPhase::Moved`]
25/// events when the finger is moved or the touch pressure changes.
26///
27/// The finger id may be reused by the system after an [`TouchPhase::Ended`] event. The user
28/// should assume that a new [`TouchPhase::Started`] event received with the same id has nothing
29/// to do with the old finger and is a new finger.
30///
31/// A [`TouchPhase::Canceled`] event is emitted when the system has canceled tracking this
32/// touch, such as when the window loses focus, or on iOS if the user moves the
33/// device against their face.
34///
35/// ## Note
36///
37/// This event is the translated version of the `WindowEvent::Touch` from the `winit` crate.
38/// It is available to the end user and can be used for game logic.
39#[derive(Event, Debug, Clone, Copy, PartialEq)]
40#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
41#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
42#[cfg_attr(
43    all(feature = "serialize", feature = "bevy_reflect"),
44    reflect(Serialize, Deserialize)
45)]
46pub struct TouchInput {
47    /// The phase of the touch input.
48    pub phase: TouchPhase,
49    /// The position of the finger on the touchscreen.
50    pub position: Vec2,
51    /// The window entity registering the touch.
52    pub window: Entity,
53    /// Describes how hard the screen was pressed.
54    ///
55    /// May be [`None`] if the platform does not support pressure sensitivity.
56    /// This feature is only available on **iOS** 9.0+ and **Windows** 8+.
57    pub force: Option<ForceTouch>,
58    /// The unique identifier of the finger.
59    pub id: u64,
60}
61
62/// A force description of a [`Touch`] input.
63#[derive(Debug, Clone, Copy, PartialEq)]
64#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
65#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
66#[cfg_attr(
67    all(feature = "serialize", feature = "bevy_reflect"),
68    reflect(Serialize, Deserialize)
69)]
70pub enum ForceTouch {
71    /// On iOS, the force is calibrated so that the same number corresponds to
72    /// roughly the same amount of pressure on the screen regardless of the
73    /// device.
74    Calibrated {
75        /// The force of the touch, where a value of 1.0 represents the force of
76        /// an average touch (predetermined by the system, not user-specific).
77        ///
78        /// The force reported by Apple Pencil is measured along the axis of the
79        /// pencil. If you want a force perpendicular to the device, you need to
80        /// calculate this value using the `altitude_angle` value.
81        force: f64,
82        /// The maximum possible force for a touch.
83        ///
84        /// The value of this field is sufficiently high to provide a wide
85        /// dynamic range for values of the `force` field.
86        max_possible_force: f64,
87        /// The altitude (in radians) of the stylus.
88        ///
89        /// A value of 0 radians indicates that the stylus is parallel to the
90        /// surface. The value of this property is Pi/2 when the stylus is
91        /// perpendicular to the surface.
92        altitude_angle: Option<f64>,
93    },
94    /// If the platform reports the force as normalized, we have no way of
95    /// knowing how much pressure 1.0 corresponds to – we know it's the maximum
96    /// amount of force, but as to how much force, you might either have to
97    /// press really hard, or not hard at all, depending on the device.
98    Normalized(f64),
99}
100
101/// A phase of a [`TouchInput`].
102///
103/// ## Usage
104///
105/// It is used to describe the phase of the touch input that is currently active.
106/// This includes a phase that indicates that a touch input has started or ended,
107/// or that a finger has moved. There is also a canceled phase that indicates that
108/// the system canceled the tracking of the finger.
109#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
110#[cfg_attr(
111    feature = "bevy_reflect",
112    derive(Reflect),
113    reflect(Debug, Hash, PartialEq)
114)]
115#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
116#[cfg_attr(
117    all(feature = "serialize", feature = "bevy_reflect"),
118    reflect(Serialize, Deserialize)
119)]
120pub enum TouchPhase {
121    /// A finger started to touch the touchscreen.
122    Started,
123    /// A finger moved over the touchscreen.
124    Moved,
125    /// A finger stopped touching the touchscreen.
126    Ended,
127    /// The system canceled the tracking of the finger.
128    ///
129    /// This occurs when the window loses focus, or on iOS if the user moves the
130    /// device against their face.
131    Canceled,
132}
133
134/// A touch input.
135///
136/// ## Usage
137///
138/// It is used to store the position and force of a touch input and also the `id` of the finger.
139/// The data of the touch input comes from the [`TouchInput`] event and is being stored
140/// inside of the [`Touches`] `bevy` resource.
141#[derive(Debug, Clone, Copy)]
142pub struct Touch {
143    /// The id of the touch input.
144    id: u64,
145    /// The starting position of the touch input.
146    start_position: Vec2,
147    /// The starting force of the touch input.
148    start_force: Option<ForceTouch>,
149    /// The previous position of the touch input.
150    previous_position: Vec2,
151    /// The previous force of the touch input.
152    previous_force: Option<ForceTouch>,
153    /// The current position of the touch input.
154    position: Vec2,
155    /// The current force of the touch input.
156    force: Option<ForceTouch>,
157}
158
159impl Touch {
160    /// The delta of the current `position` and the `previous_position`.
161    pub fn delta(&self) -> Vec2 {
162        self.position - self.previous_position
163    }
164
165    /// The distance of the `start_position` and the current `position`.
166    pub fn distance(&self) -> Vec2 {
167        self.position - self.start_position
168    }
169
170    /// Returns the `id` of the touch.
171    #[inline]
172    pub fn id(&self) -> u64 {
173        self.id
174    }
175
176    /// Returns the `start_position` of the touch.
177    #[inline]
178    pub fn start_position(&self) -> Vec2 {
179        self.start_position
180    }
181
182    /// Returns the `start_force` of the touch.
183    #[inline]
184    pub fn start_force(&self) -> Option<ForceTouch> {
185        self.start_force
186    }
187
188    /// Returns the `previous_position` of the touch.
189    #[inline]
190    pub fn previous_position(&self) -> Vec2 {
191        self.previous_position
192    }
193
194    /// Returns the `previous_force` of the touch.
195    #[inline]
196    pub fn previous_force(&self) -> Option<ForceTouch> {
197        self.previous_force
198    }
199
200    /// Returns the current `position` of the touch.
201    #[inline]
202    pub fn position(&self) -> Vec2 {
203        self.position
204    }
205
206    /// Returns the current `force` of the touch.
207    #[inline]
208    pub fn force(&self) -> Option<ForceTouch> {
209        self.force
210    }
211}
212
213impl From<&TouchInput> for Touch {
214    fn from(input: &TouchInput) -> Touch {
215        Touch {
216            id: input.id,
217            start_position: input.position,
218            start_force: input.force,
219            previous_position: input.position,
220            previous_force: input.force,
221            position: input.position,
222            force: input.force,
223        }
224    }
225}
226
227/// A collection of [`Touch`]es.
228///
229/// ## Usage
230///
231/// It is used to create a `bevy` resource that stores the data of the touches on a touchscreen
232/// and can be accessed inside of a system.
233///
234/// ## Updating
235///
236/// The resource is updated inside of the [`touch_screen_input_system`].
237#[derive(Debug, Clone, Default, Resource)]
238pub struct Touches {
239    /// A collection of every [`Touch`] that is currently being pressed.
240    pressed: HashMap<u64, Touch>,
241    /// A collection of every [`Touch`] that just got pressed.
242    just_pressed: HashMap<u64, Touch>,
243    /// A collection of every [`Touch`] that just got released.
244    just_released: HashMap<u64, Touch>,
245    /// A collection of every [`Touch`] that just got canceled.
246    just_canceled: HashMap<u64, Touch>,
247}
248
249impl Touches {
250    /// An iterator visiting every pressed [`Touch`] input in arbitrary order.
251    pub fn iter(&self) -> impl Iterator<Item = &Touch> + '_ {
252        self.pressed.values()
253    }
254
255    /// Returns the [`Touch`] input corresponding to the `id` if it is being pressed.
256    pub fn get_pressed(&self, id: u64) -> Option<&Touch> {
257        self.pressed.get(&id)
258    }
259
260    /// Checks if any touch input was just pressed.
261    pub fn any_just_pressed(&self) -> bool {
262        !self.just_pressed.is_empty()
263    }
264
265    /// Register a release for a given touch input.
266    pub fn release(&mut self, id: u64) {
267        if let Some(touch) = self.pressed.remove(&id) {
268            self.just_released.insert(id, touch);
269        }
270    }
271
272    /// Registers a release for all currently pressed touch inputs.
273    pub fn release_all(&mut self) {
274        self.just_released.extend(self.pressed.drain());
275    }
276
277    /// Returns `true` if the input corresponding to the `id` has just been pressed.
278    pub fn just_pressed(&self, id: u64) -> bool {
279        self.just_pressed.contains_key(&id)
280    }
281
282    /// Clears the `just_pressed` state of the touch input and returns `true` if the touch input has just been pressed.
283    ///
284    /// Future calls to [`Touches::just_pressed`] for the given touch input will return false until a new press event occurs.
285    pub fn clear_just_pressed(&mut self, id: u64) -> bool {
286        self.just_pressed.remove(&id).is_some()
287    }
288
289    /// An iterator visiting every just pressed [`Touch`] input in arbitrary order.
290    pub fn iter_just_pressed(&self) -> impl Iterator<Item = &Touch> {
291        self.just_pressed.values()
292    }
293
294    /// Returns the [`Touch`] input corresponding to the `id` if it has just been released.
295    pub fn get_released(&self, id: u64) -> Option<&Touch> {
296        self.just_released.get(&id)
297    }
298
299    /// Checks if any touch input was just released.
300    pub fn any_just_released(&self) -> bool {
301        !self.just_released.is_empty()
302    }
303
304    /// Returns `true` if the input corresponding to the `id` has just been released.
305    pub fn just_released(&self, id: u64) -> bool {
306        self.just_released.contains_key(&id)
307    }
308
309    /// Clears the `just_released` state of the touch input and returns `true` if the touch input has just been released.
310    ///
311    /// Future calls to [`Touches::just_released`] for the given touch input will return false until a new release event occurs.
312    pub fn clear_just_released(&mut self, id: u64) -> bool {
313        self.just_released.remove(&id).is_some()
314    }
315
316    /// An iterator visiting every just released [`Touch`] input in arbitrary order.
317    pub fn iter_just_released(&self) -> impl Iterator<Item = &Touch> {
318        self.just_released.values()
319    }
320
321    /// Checks if any touch input was just canceled.
322    pub fn any_just_canceled(&self) -> bool {
323        !self.just_canceled.is_empty()
324    }
325
326    /// Returns `true` if the input corresponding to the `id` has just been canceled.
327    pub fn just_canceled(&self, id: u64) -> bool {
328        self.just_canceled.contains_key(&id)
329    }
330
331    /// Clears the `just_canceled` state of the touch input and returns `true` if the touch input has just been canceled.
332    ///
333    /// Future calls to [`Touches::just_canceled`] for the given touch input will return false until a new cancel event occurs.
334    pub fn clear_just_canceled(&mut self, id: u64) -> bool {
335        self.just_canceled.remove(&id).is_some()
336    }
337
338    /// An iterator visiting every just canceled [`Touch`] input in arbitrary order.
339    pub fn iter_just_canceled(&self) -> impl Iterator<Item = &Touch> {
340        self.just_canceled.values()
341    }
342
343    /// Retrieves the position of the first currently pressed touch, if any
344    pub fn first_pressed_position(&self) -> Option<Vec2> {
345        // Looking for the position in `pressed`. If nothing is found, also look into `just_pressed`
346        // A touch can be in `just_pressed` but not in `pressed` if it ended in the same frame it started
347        self.pressed
348            .values()
349            .next()
350            .or_else(|| self.just_pressed.values().next())
351            .map(|t| t.position)
352    }
353
354    /// Clears `just_pressed`, `just_released`, and `just_canceled` data for every touch input.
355    ///
356    /// See also [`Touches::reset_all`] for a full reset.
357    pub fn clear(&mut self) {
358        self.just_pressed.clear();
359        self.just_released.clear();
360        self.just_canceled.clear();
361    }
362
363    /// Clears `pressed`, `just_pressed`, `just_released`, and `just_canceled` data for every touch input.
364    ///
365    /// See also [`Touches::clear`] for clearing only touches that have just been pressed, released or canceled.
366    pub fn reset_all(&mut self) {
367        self.pressed.clear();
368        self.just_pressed.clear();
369        self.just_released.clear();
370        self.just_canceled.clear();
371    }
372
373    /// Processes a [`TouchInput`] event by updating the `pressed`, `just_pressed`,
374    /// `just_released`, and `just_canceled` collections.
375    fn process_touch_event(&mut self, event: &TouchInput) {
376        match event.phase {
377            TouchPhase::Started => {
378                self.pressed.insert(event.id, event.into());
379                self.just_pressed.insert(event.id, event.into());
380            }
381            TouchPhase::Moved => {
382                if let Some(mut new_touch) = self.pressed.get(&event.id).cloned() {
383                    // NOTE: This does not update the previous_force / previous_position field;
384                    // they should be updated once per frame, not once per event
385                    // See https://github.com/bevyengine/bevy/issues/12442
386                    new_touch.position = event.position;
387                    new_touch.force = event.force;
388                    self.pressed.insert(event.id, new_touch);
389                }
390            }
391            TouchPhase::Ended => {
392                // if touch `just_released`, add related event to it
393                // the event position info is inside `pressed`, so use it unless not found
394                if let Some((_, v)) = self.pressed.remove_entry(&event.id) {
395                    self.just_released.insert(event.id, v);
396                } else {
397                    self.just_released.insert(event.id, event.into());
398                }
399            }
400            TouchPhase::Canceled => {
401                // if touch `just_canceled`, add related event to it
402                // the event position info is inside `pressed`, so use it unless not found
403                if let Some((_, v)) = self.pressed.remove_entry(&event.id) {
404                    self.just_canceled.insert(event.id, v);
405                } else {
406                    self.just_canceled.insert(event.id, event.into());
407                }
408            }
409        };
410    }
411}
412
413/// Updates the [`Touches`] resource with the latest [`TouchInput`] events.
414///
415/// This is not clearing the `pressed` collection, because it could incorrectly mark a touch input
416/// as not pressed even though it is pressed. This could happen if the touch input is not moving
417/// for a single frame and would therefore be marked as not pressed, because this function is
418/// called on every single frame no matter if there was an event or not.
419///
420/// ## Differences
421///
422/// The main difference between the [`TouchInput`] event and the [`Touches`] resource is that
423/// the latter has convenient functions like [`Touches::just_pressed`] and [`Touches::just_released`].
424pub fn touch_screen_input_system(
425    mut touch_state: ResMut<Touches>,
426    mut touch_input_events: EventReader<TouchInput>,
427) {
428    if !touch_state.just_pressed.is_empty() {
429        touch_state.just_pressed.clear();
430    }
431    if !touch_state.just_released.is_empty() {
432        touch_state.just_released.clear();
433    }
434    if !touch_state.just_canceled.is_empty() {
435        touch_state.just_canceled.clear();
436    }
437
438    if !touch_input_events.is_empty() {
439        for touch in touch_state.pressed.values_mut() {
440            touch.previous_position = touch.position;
441            touch.previous_force = touch.force;
442        }
443
444        for event in touch_input_events.read() {
445            touch_state.process_touch_event(event);
446        }
447    }
448}
449
450#[cfg(test)]
451mod test {
452    use super::Touches;
453
454    #[test]
455    fn touch_update() {
456        use crate::{touch::Touch, Touches};
457        use bevy_math::Vec2;
458
459        let mut touches = Touches::default();
460
461        let touch_event = Touch {
462            id: 4,
463            start_position: Vec2::ZERO,
464            start_force: None,
465            previous_position: Vec2::ZERO,
466            previous_force: None,
467            position: Vec2::ZERO,
468            force: None,
469        };
470
471        // Add a touch to `just_pressed`, 'just_released', and 'just canceled'
472
473        touches.just_pressed.insert(4, touch_event);
474        touches.just_released.insert(4, touch_event);
475        touches.just_canceled.insert(4, touch_event);
476
477        clear_all(&mut touches);
478
479        // Verify that all the `just_x` maps are cleared
480        assert!(touches.just_pressed.is_empty());
481        assert!(touches.just_released.is_empty());
482        assert!(touches.just_canceled.is_empty());
483    }
484
485    #[test]
486    fn touch_process() {
487        use crate::{touch::TouchPhase, TouchInput, Touches};
488        use bevy_ecs::entity::Entity;
489        use bevy_math::Vec2;
490
491        let mut touches = Touches::default();
492
493        // Test adding a `TouchPhase::Started`
494
495        let touch_event = TouchInput {
496            phase: TouchPhase::Started,
497            position: Vec2::splat(4.0),
498            window: Entity::PLACEHOLDER,
499            force: None,
500            id: 4,
501        };
502
503        clear_all(&mut touches);
504        touches.process_touch_event(&touch_event);
505
506        assert!(touches.pressed.get(&touch_event.id).is_some());
507        assert!(touches.just_pressed.get(&touch_event.id).is_some());
508
509        // Test adding a `TouchPhase::Moved`
510
511        let moved_touch_event = TouchInput {
512            phase: TouchPhase::Moved,
513            position: Vec2::splat(5.0),
514            window: Entity::PLACEHOLDER,
515            force: None,
516            id: touch_event.id,
517        };
518
519        clear_all(&mut touches);
520        touches.process_touch_event(&moved_touch_event);
521
522        assert_eq!(
523            touches
524                .pressed
525                .get(&moved_touch_event.id)
526                .expect("Missing from pressed after move.")
527                .previous_position,
528            touch_event.position
529        );
530
531        // Test cancelling an event
532
533        let cancel_touch_event = TouchInput {
534            phase: TouchPhase::Canceled,
535            position: Vec2::ONE,
536            window: Entity::PLACEHOLDER,
537            force: None,
538            id: touch_event.id,
539        };
540
541        clear_all(&mut touches);
542        touches.process_touch_event(&cancel_touch_event);
543
544        assert!(touches.just_canceled.get(&touch_event.id).is_some());
545        assert!(touches.pressed.get(&touch_event.id).is_none());
546
547        // Test ending an event
548
549        let end_touch_event = TouchInput {
550            phase: TouchPhase::Ended,
551            position: Vec2::splat(4.0),
552            window: Entity::PLACEHOLDER,
553            force: None,
554            id: touch_event.id,
555        };
556
557        clear_all(&mut touches);
558        touches.process_touch_event(&touch_event);
559        touches.process_touch_event(&moved_touch_event);
560        touches.process_touch_event(&end_touch_event);
561
562        assert!(touches.just_released.get(&touch_event.id).is_some());
563        assert!(touches.pressed.get(&touch_event.id).is_none());
564        let touch = touches.just_released.get(&touch_event.id).unwrap();
565        // Make sure the position is updated from TouchPhase::Moved and TouchPhase::Ended
566        assert_ne!(touch.previous_position, touch.position);
567    }
568
569    // See https://github.com/bevyengine/bevy/issues/12442
570    #[test]
571    fn touch_process_multi_event() {
572        use crate::{touch::TouchPhase, TouchInput, Touches};
573        use bevy_ecs::entity::Entity;
574        use bevy_math::Vec2;
575
576        let mut touches = Touches::default();
577
578        let started_touch_event = TouchInput {
579            phase: TouchPhase::Started,
580            position: Vec2::splat(4.0),
581            window: Entity::PLACEHOLDER,
582            force: None,
583            id: 4,
584        };
585
586        let moved_touch_event1 = TouchInput {
587            phase: TouchPhase::Moved,
588            position: Vec2::splat(5.0),
589            window: Entity::PLACEHOLDER,
590            force: None,
591            id: started_touch_event.id,
592        };
593
594        let moved_touch_event2 = TouchInput {
595            phase: TouchPhase::Moved,
596            position: Vec2::splat(6.0),
597            window: Entity::PLACEHOLDER,
598            force: None,
599            id: started_touch_event.id,
600        };
601
602        // tick 1: touch is started during frame
603        for touch in touches.pressed.values_mut() {
604            // update ONCE, at start of frame
605            touch.previous_position = touch.position;
606        }
607        touches.process_touch_event(&started_touch_event);
608        touches.process_touch_event(&moved_touch_event1);
609        touches.process_touch_event(&moved_touch_event2);
610
611        {
612            let touch = touches.get_pressed(started_touch_event.id).unwrap();
613            assert_eq!(touch.previous_position, started_touch_event.position);
614            assert_eq!(touch.position, moved_touch_event2.position);
615        }
616
617        // tick 2: touch was started before frame
618        for touch in touches.pressed.values_mut() {
619            touch.previous_position = touch.position;
620        }
621        touches.process_touch_event(&moved_touch_event1);
622        touches.process_touch_event(&moved_touch_event2);
623        touches.process_touch_event(&moved_touch_event1);
624
625        {
626            let touch = touches.get_pressed(started_touch_event.id).unwrap();
627            assert_eq!(touch.previous_position, moved_touch_event2.position);
628            assert_eq!(touch.position, moved_touch_event1.position);
629        }
630    }
631
632    #[test]
633    fn touch_pressed() {
634        use crate::{touch::TouchPhase, TouchInput, Touches};
635        use bevy_ecs::entity::Entity;
636        use bevy_math::Vec2;
637
638        let mut touches = Touches::default();
639
640        let touch_event = TouchInput {
641            phase: TouchPhase::Started,
642            position: Vec2::splat(4.0),
643            window: Entity::PLACEHOLDER,
644            force: None,
645            id: 4,
646        };
647
648        // Register the touch and test that it was registered correctly
649        touches.process_touch_event(&touch_event);
650
651        assert!(touches.get_pressed(touch_event.id).is_some());
652        assert!(touches.just_pressed(touch_event.id));
653        assert_eq!(touches.iter().count(), 1);
654
655        touches.clear_just_pressed(touch_event.id);
656        assert!(!touches.just_pressed(touch_event.id));
657    }
658
659    #[test]
660    fn touch_released() {
661        use crate::{touch::TouchPhase, TouchInput, Touches};
662        use bevy_ecs::entity::Entity;
663        use bevy_math::Vec2;
664
665        let mut touches = Touches::default();
666
667        let touch_event = TouchInput {
668            phase: TouchPhase::Ended,
669            position: Vec2::splat(4.0),
670            window: Entity::PLACEHOLDER,
671            force: None,
672            id: 4,
673        };
674
675        // Register the touch and test that it was registered correctly
676        touches.process_touch_event(&touch_event);
677
678        assert!(touches.get_released(touch_event.id).is_some());
679        assert!(touches.just_released(touch_event.id));
680        assert_eq!(touches.iter_just_released().count(), 1);
681
682        touches.clear_just_released(touch_event.id);
683        assert!(!touches.just_released(touch_event.id));
684    }
685
686    #[test]
687    fn touch_canceled() {
688        use crate::{touch::TouchPhase, TouchInput, Touches};
689        use bevy_ecs::entity::Entity;
690        use bevy_math::Vec2;
691
692        let mut touches = Touches::default();
693
694        let touch_event = TouchInput {
695            phase: TouchPhase::Canceled,
696            position: Vec2::splat(4.0),
697            window: Entity::PLACEHOLDER,
698            force: None,
699            id: 4,
700        };
701
702        // Register the touch and test that it was registered correctly
703        touches.process_touch_event(&touch_event);
704
705        assert!(touches.just_canceled(touch_event.id));
706        assert_eq!(touches.iter_just_canceled().count(), 1);
707
708        touches.clear_just_canceled(touch_event.id);
709        assert!(!touches.just_canceled(touch_event.id));
710    }
711
712    #[test]
713    fn release_touch() {
714        use crate::{touch::TouchPhase, TouchInput, Touches};
715        use bevy_ecs::entity::Entity;
716        use bevy_math::Vec2;
717
718        let mut touches = Touches::default();
719
720        let touch_event = TouchInput {
721            phase: TouchPhase::Started,
722            position: Vec2::splat(4.0),
723            window: Entity::PLACEHOLDER,
724            force: None,
725            id: 4,
726        };
727
728        // Register the touch and test that it was registered correctly
729        touches.process_touch_event(&touch_event);
730
731        assert!(touches.get_pressed(touch_event.id).is_some());
732
733        touches.release(touch_event.id);
734        assert!(touches.get_pressed(touch_event.id).is_none());
735        assert!(touches.just_released(touch_event.id));
736    }
737
738    #[test]
739    fn release_all_touches() {
740        use crate::{touch::TouchPhase, TouchInput, Touches};
741        use bevy_ecs::entity::Entity;
742        use bevy_math::Vec2;
743
744        let mut touches = Touches::default();
745
746        let touch_pressed_event = TouchInput {
747            phase: TouchPhase::Started,
748            position: Vec2::splat(4.0),
749            window: Entity::PLACEHOLDER,
750            force: None,
751            id: 4,
752        };
753
754        let touch_moved_event = TouchInput {
755            phase: TouchPhase::Moved,
756            position: Vec2::splat(4.0),
757            window: Entity::PLACEHOLDER,
758            force: None,
759            id: 4,
760        };
761
762        touches.process_touch_event(&touch_pressed_event);
763        touches.process_touch_event(&touch_moved_event);
764
765        assert!(touches.get_pressed(touch_pressed_event.id).is_some());
766        assert!(touches.get_pressed(touch_moved_event.id).is_some());
767
768        touches.release_all();
769
770        assert!(touches.get_pressed(touch_pressed_event.id).is_none());
771        assert!(touches.just_released(touch_pressed_event.id));
772        assert!(touches.get_pressed(touch_moved_event.id).is_none());
773        assert!(touches.just_released(touch_moved_event.id));
774    }
775
776    #[test]
777    fn clear_touches() {
778        use crate::{touch::TouchPhase, TouchInput, Touches};
779        use bevy_ecs::entity::Entity;
780        use bevy_math::Vec2;
781
782        let mut touches = Touches::default();
783
784        let touch_press_event = TouchInput {
785            phase: TouchPhase::Started,
786            position: Vec2::splat(4.0),
787            window: Entity::PLACEHOLDER,
788            force: None,
789            id: 4,
790        };
791
792        let touch_canceled_event = TouchInput {
793            phase: TouchPhase::Canceled,
794            position: Vec2::splat(4.0),
795            window: Entity::PLACEHOLDER,
796            force: None,
797            id: 5,
798        };
799
800        let touch_released_event = TouchInput {
801            phase: TouchPhase::Ended,
802            position: Vec2::splat(4.0),
803            window: Entity::PLACEHOLDER,
804            force: None,
805            id: 6,
806        };
807
808        // Register the touches and test that it was registered correctly
809        touches.process_touch_event(&touch_press_event);
810        touches.process_touch_event(&touch_canceled_event);
811        touches.process_touch_event(&touch_released_event);
812
813        assert!(touches.get_pressed(touch_press_event.id).is_some());
814        assert!(touches.just_pressed(touch_press_event.id));
815        assert!(touches.just_canceled(touch_canceled_event.id));
816        assert!(touches.just_released(touch_released_event.id));
817
818        touches.clear();
819
820        assert!(touches.get_pressed(touch_press_event.id).is_some());
821        assert!(!touches.just_pressed(touch_press_event.id));
822        assert!(!touches.just_canceled(touch_canceled_event.id));
823        assert!(!touches.just_released(touch_released_event.id));
824    }
825
826    #[test]
827    fn reset_all_touches() {
828        use crate::{touch::TouchPhase, TouchInput, Touches};
829        use bevy_ecs::entity::Entity;
830        use bevy_math::Vec2;
831
832        let mut touches = Touches::default();
833
834        let touch_press_event = TouchInput {
835            phase: TouchPhase::Started,
836            position: Vec2::splat(4.0),
837            window: Entity::PLACEHOLDER,
838            force: None,
839            id: 4,
840        };
841
842        let touch_canceled_event = TouchInput {
843            phase: TouchPhase::Canceled,
844            position: Vec2::splat(4.0),
845            window: Entity::PLACEHOLDER,
846            force: None,
847            id: 5,
848        };
849
850        let touch_released_event = TouchInput {
851            phase: TouchPhase::Ended,
852            position: Vec2::splat(4.0),
853            window: Entity::PLACEHOLDER,
854            force: None,
855            id: 6,
856        };
857
858        // Register the touches and test that it was registered correctly
859        touches.process_touch_event(&touch_press_event);
860        touches.process_touch_event(&touch_canceled_event);
861        touches.process_touch_event(&touch_released_event);
862
863        assert!(touches.get_pressed(touch_press_event.id).is_some());
864        assert!(touches.just_pressed(touch_press_event.id));
865        assert!(touches.just_canceled(touch_canceled_event.id));
866        assert!(touches.just_released(touch_released_event.id));
867
868        touches.reset_all();
869
870        assert!(touches.get_pressed(touch_press_event.id).is_none());
871        assert!(!touches.just_pressed(touch_press_event.id));
872        assert!(!touches.just_canceled(touch_canceled_event.id));
873        assert!(!touches.just_released(touch_released_event.id));
874    }
875
876    fn clear_all(touch_state: &mut Touches) {
877        touch_state.just_pressed.clear();
878        touch_state.just_released.clear();
879        touch_state.just_canceled.clear();
880    }
881}