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    use bevy_ecs::{entity::Entity, world::World};
465
466    fn dummy_window_entity() -> Entity {
467        World::default().spawn_empty().id()
468    }
469
470    #[test]
471    fn touch_update() {
472        use crate::{touch::Touch, Touches};
473        use bevy_math::Vec2;
474
475        let mut touches = Touches::default();
476
477        let touch_event = Touch {
478            id: 4,
479            start_position: Vec2::ZERO,
480            start_force: None,
481            previous_position: Vec2::ZERO,
482            previous_force: None,
483            position: Vec2::ZERO,
484            force: None,
485        };
486
487        // Add a touch to `just_pressed`, 'just_released', and 'just canceled'
488
489        touches.just_pressed.insert(4, touch_event);
490        touches.just_released.insert(4, touch_event);
491        touches.just_canceled.insert(4, touch_event);
492
493        clear_all(&mut touches);
494
495        // Verify that all the `just_x` maps are cleared
496        assert!(touches.just_pressed.is_empty());
497        assert!(touches.just_released.is_empty());
498        assert!(touches.just_canceled.is_empty());
499    }
500
501    #[test]
502    fn touch_process() {
503        use crate::{touch::TouchPhase, TouchInput, Touches};
504        use bevy_math::Vec2;
505
506        let mut touches = Touches::default();
507
508        // Test adding a `TouchPhase::Started`
509
510        let touch_event = TouchInput {
511            phase: TouchPhase::Started,
512            position: Vec2::splat(4.0),
513            window: dummy_window_entity(),
514            force: None,
515            id: 4,
516        };
517
518        clear_all(&mut touches);
519        touches.process_touch_event(&touch_event);
520
521        assert!(touches.pressed.get(&touch_event.id).is_some());
522        assert!(touches.just_pressed.get(&touch_event.id).is_some());
523
524        // Test adding a `TouchPhase::Moved`
525
526        let moved_touch_event = TouchInput {
527            phase: TouchPhase::Moved,
528            position: Vec2::splat(5.0),
529            window: dummy_window_entity(),
530            force: None,
531            id: touch_event.id,
532        };
533
534        clear_all(&mut touches);
535        touches.process_touch_event(&moved_touch_event);
536
537        assert_eq!(
538            touches
539                .pressed
540                .get(&moved_touch_event.id)
541                .expect("Missing from pressed after move.")
542                .previous_position,
543            touch_event.position
544        );
545
546        // Test cancelling an event
547
548        let cancel_touch_event = TouchInput {
549            phase: TouchPhase::Canceled,
550            position: Vec2::ONE,
551            window: dummy_window_entity(),
552            force: None,
553            id: touch_event.id,
554        };
555
556        clear_all(&mut touches);
557        touches.process_touch_event(&cancel_touch_event);
558
559        assert!(touches.just_canceled.get(&touch_event.id).is_some());
560        assert!(touches.pressed.get(&touch_event.id).is_none());
561
562        // Test ending an event
563
564        let end_touch_event = TouchInput {
565            phase: TouchPhase::Ended,
566            position: Vec2::splat(4.0),
567            window: dummy_window_entity(),
568            force: None,
569            id: touch_event.id,
570        };
571
572        clear_all(&mut touches);
573        touches.process_touch_event(&touch_event);
574        touches.process_touch_event(&moved_touch_event);
575        touches.process_touch_event(&end_touch_event);
576
577        assert!(touches.just_released.get(&touch_event.id).is_some());
578        assert!(touches.pressed.get(&touch_event.id).is_none());
579        let touch = touches.just_released.get(&touch_event.id).unwrap();
580        // Make sure the position is updated from TouchPhase::Moved and TouchPhase::Ended
581        assert_ne!(touch.previous_position, touch.position);
582    }
583
584    // See https://github.com/bevyengine/bevy/issues/12442
585    #[test]
586    fn touch_process_multi_event() {
587        use crate::{touch::TouchPhase, TouchInput, Touches};
588        use bevy_math::Vec2;
589
590        let mut touches = Touches::default();
591
592        let started_touch_event = TouchInput {
593            phase: TouchPhase::Started,
594            position: Vec2::splat(4.0),
595            window: dummy_window_entity(),
596            force: None,
597            id: 4,
598        };
599
600        let moved_touch_event1 = TouchInput {
601            phase: TouchPhase::Moved,
602            position: Vec2::splat(5.0),
603            window: dummy_window_entity(),
604            force: None,
605            id: started_touch_event.id,
606        };
607
608        let moved_touch_event2 = TouchInput {
609            phase: TouchPhase::Moved,
610            position: Vec2::splat(6.0),
611            window: dummy_window_entity(),
612            force: None,
613            id: started_touch_event.id,
614        };
615
616        // tick 1: touch is started during frame
617        for touch in touches.pressed.values_mut() {
618            // update ONCE, at start of frame
619            touch.previous_position = touch.position;
620        }
621        touches.process_touch_event(&started_touch_event);
622        touches.process_touch_event(&moved_touch_event1);
623        touches.process_touch_event(&moved_touch_event2);
624
625        {
626            let touch = touches.get_pressed(started_touch_event.id).unwrap();
627            assert_eq!(touch.previous_position, started_touch_event.position);
628            assert_eq!(touch.position, moved_touch_event2.position);
629        }
630
631        // tick 2: touch was started before frame
632        for touch in touches.pressed.values_mut() {
633            touch.previous_position = touch.position;
634        }
635        touches.process_touch_event(&moved_touch_event1);
636        touches.process_touch_event(&moved_touch_event2);
637        touches.process_touch_event(&moved_touch_event1);
638
639        {
640            let touch = touches.get_pressed(started_touch_event.id).unwrap();
641            assert_eq!(touch.previous_position, moved_touch_event2.position);
642            assert_eq!(touch.position, moved_touch_event1.position);
643        }
644    }
645
646    #[test]
647    fn touch_pressed() {
648        use crate::{touch::TouchPhase, TouchInput, Touches};
649        use bevy_math::Vec2;
650
651        let mut touches = Touches::default();
652
653        let touch_event = TouchInput {
654            phase: TouchPhase::Started,
655            position: Vec2::splat(4.0),
656            window: dummy_window_entity(),
657            force: None,
658            id: 4,
659        };
660
661        // Register the touch and test that it was registered correctly
662        touches.process_touch_event(&touch_event);
663
664        assert!(touches.get_pressed(touch_event.id).is_some());
665        assert!(touches.just_pressed(touch_event.id));
666        assert_eq!(touches.iter().count(), 1);
667
668        touches.clear_just_pressed(touch_event.id);
669        assert!(!touches.just_pressed(touch_event.id));
670    }
671
672    #[test]
673    fn touch_released() {
674        use crate::{touch::TouchPhase, TouchInput, Touches};
675        use bevy_math::Vec2;
676
677        let mut touches = Touches::default();
678
679        let touch_event = TouchInput {
680            phase: TouchPhase::Ended,
681            position: Vec2::splat(4.0),
682            window: dummy_window_entity(),
683            force: None,
684            id: 4,
685        };
686
687        // Register the touch and test that it was registered correctly
688        touches.process_touch_event(&touch_event);
689
690        assert!(touches.get_released(touch_event.id).is_some());
691        assert!(touches.just_released(touch_event.id));
692        assert_eq!(touches.iter_just_released().count(), 1);
693
694        touches.clear_just_released(touch_event.id);
695        assert!(!touches.just_released(touch_event.id));
696    }
697
698    #[test]
699    fn touch_canceled() {
700        use crate::{touch::TouchPhase, TouchInput, Touches};
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: dummy_window_entity(),
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_math::Vec2;
727
728        let mut touches = Touches::default();
729
730        let touch_event = TouchInput {
731            phase: TouchPhase::Started,
732            position: Vec2::splat(4.0),
733            window: dummy_window_entity(),
734            force: None,
735            id: 4,
736        };
737
738        // Register the touch and test that it was registered correctly
739        touches.process_touch_event(&touch_event);
740
741        assert!(touches.get_pressed(touch_event.id).is_some());
742
743        touches.release(touch_event.id);
744        assert!(touches.get_pressed(touch_event.id).is_none());
745        assert!(touches.just_released(touch_event.id));
746    }
747
748    #[test]
749    fn release_all_touches() {
750        use crate::{touch::TouchPhase, TouchInput, Touches};
751        use bevy_math::Vec2;
752
753        let mut touches = Touches::default();
754
755        let touch_pressed_event = TouchInput {
756            phase: TouchPhase::Started,
757            position: Vec2::splat(4.0),
758            window: dummy_window_entity(),
759            force: None,
760            id: 4,
761        };
762
763        let touch_moved_event = TouchInput {
764            phase: TouchPhase::Moved,
765            position: Vec2::splat(4.0),
766            window: dummy_window_entity(),
767            force: None,
768            id: 4,
769        };
770
771        touches.process_touch_event(&touch_pressed_event);
772        touches.process_touch_event(&touch_moved_event);
773
774        assert!(touches.get_pressed(touch_pressed_event.id).is_some());
775        assert!(touches.get_pressed(touch_moved_event.id).is_some());
776
777        touches.release_all();
778
779        assert!(touches.get_pressed(touch_pressed_event.id).is_none());
780        assert!(touches.just_released(touch_pressed_event.id));
781        assert!(touches.get_pressed(touch_moved_event.id).is_none());
782        assert!(touches.just_released(touch_moved_event.id));
783    }
784
785    #[test]
786    fn clear_touches() {
787        use crate::{touch::TouchPhase, TouchInput, Touches};
788        use bevy_math::Vec2;
789
790        let mut touches = Touches::default();
791
792        let touch_press_event = TouchInput {
793            phase: TouchPhase::Started,
794            position: Vec2::splat(4.0),
795            window: dummy_window_entity(),
796            force: None,
797            id: 4,
798        };
799
800        let touch_canceled_event = TouchInput {
801            phase: TouchPhase::Canceled,
802            position: Vec2::splat(4.0),
803            window: dummy_window_entity(),
804            force: None,
805            id: 5,
806        };
807
808        let touch_released_event = TouchInput {
809            phase: TouchPhase::Ended,
810            position: Vec2::splat(4.0),
811            window: dummy_window_entity(),
812            force: None,
813            id: 6,
814        };
815
816        // Register the touches and test that it was registered correctly
817        touches.process_touch_event(&touch_press_event);
818        touches.process_touch_event(&touch_canceled_event);
819        touches.process_touch_event(&touch_released_event);
820
821        assert!(touches.get_pressed(touch_press_event.id).is_some());
822        assert!(touches.just_pressed(touch_press_event.id));
823        assert!(touches.just_canceled(touch_canceled_event.id));
824        assert!(touches.just_released(touch_released_event.id));
825
826        touches.clear();
827
828        assert!(touches.get_pressed(touch_press_event.id).is_some());
829        assert!(!touches.just_pressed(touch_press_event.id));
830        assert!(!touches.just_canceled(touch_canceled_event.id));
831        assert!(!touches.just_released(touch_released_event.id));
832    }
833
834    #[test]
835    fn reset_all_touches() {
836        use crate::{touch::TouchPhase, TouchInput, Touches};
837        use bevy_math::Vec2;
838
839        let mut touches = Touches::default();
840
841        let touch_press_event = TouchInput {
842            phase: TouchPhase::Started,
843            position: Vec2::splat(4.0),
844            window: dummy_window_entity(),
845            force: None,
846            id: 4,
847        };
848
849        let touch_canceled_event = TouchInput {
850            phase: TouchPhase::Canceled,
851            position: Vec2::splat(4.0),
852            window: dummy_window_entity(),
853            force: None,
854            id: 5,
855        };
856
857        let touch_released_event = TouchInput {
858            phase: TouchPhase::Ended,
859            position: Vec2::splat(4.0),
860            window: dummy_window_entity(),
861            force: None,
862            id: 6,
863        };
864
865        // Register the touches and test that it was registered correctly
866        touches.process_touch_event(&touch_press_event);
867        touches.process_touch_event(&touch_canceled_event);
868        touches.process_touch_event(&touch_released_event);
869
870        assert!(touches.get_pressed(touch_press_event.id).is_some());
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        touches.reset_all();
876
877        assert!(touches.get_pressed(touch_press_event.id).is_none());
878        assert!(!touches.just_pressed(touch_press_event.id));
879        assert!(!touches.just_canceled(touch_canceled_event.id));
880        assert!(!touches.just_released(touch_released_event.id));
881    }
882
883    fn clear_all(touch_state: &mut Touches) {
884        touch_state.just_pressed.clear();
885        touch_state.just_released.clear();
886        touch_state.just_canceled.clear();
887    }
888}