Skip to main content

bevy_input/
touch.rs

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