input/
touch.rs

1use crate::{Event, Input, Motion};
2
3/// Stores the touch state.
4#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
5pub enum Touch {
6    /// The start of touch, for example
7    /// a finger pressed down on a touch screen.
8    Start,
9    /// The move of touch, for example
10    /// a finger moving while touching a touch screen.
11    Move,
12    /// The end of touch, for example
13    /// taking a finger away from a touch screen.
14    End,
15    /// The cancel of touch, for example
16    /// the window loses focus.
17    Cancel,
18}
19
20/// Touch arguments
21///
22/// The `id` might be reused for different touches that do not overlap in time.
23///
24/// - Coordinates are normalized to support both touch screens and trackpads
25/// - Supports both 2D and 3D touch
26/// - The pressure direction vector should have maximum length 1
27///
28/// For 2D touch the pressure is pointed in the z direction.
29/// Use `.pressure()` to get the pressure magnitude.
30#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd, Debug)]
31pub struct TouchArgs {
32    /// A unique identifier for touch device.
33    pub device: i64,
34    /// A unique identifier for touch event.
35    pub id: i64,
36    /// The touch position, normalized 0..1.
37    pub position_3d: [f64; 3],
38    /// The touch pressure vector, normalized 0..1.
39    pub pressure_3d: [f64; 3],
40    /// Whether the touch is in 3D.
41    pub is_3d: bool,
42    /// The touch state.
43    pub touch: Touch,
44}
45
46impl TouchArgs {
47    /// Creates arguments for 2D touch.
48    pub fn new(device: i64, id: i64, position: [f64; 2], pressure: f64, touch: Touch) -> TouchArgs {
49        TouchArgs {
50            device,
51            id,
52            position_3d: [position[0], position[1], 0.0],
53            pressure_3d: [0.0, 0.0, pressure],
54            is_3d: false,
55            touch,
56        }
57    }
58
59    /// Creates arguments for 3D touch.
60    ///
61    /// The pressure direction vector should have maximum length 1.
62    pub fn new_3d(
63        device: i64,
64        id: i64,
65        position_3d: [f64; 3],
66        pressure_3d: [f64; 3],
67        touch: Touch,
68    ) -> TouchArgs {
69        TouchArgs {
70            device,
71            id,
72            position_3d,
73            pressure_3d,
74            is_3d: true,
75            touch,
76        }
77    }
78
79    /// The position of the touch in 2D.
80    pub fn position(&self) -> [f64; 2] {
81        [self.position_3d[0], self.position_3d[1]]
82    }
83
84    /// The position of the touch in 3D.
85    pub fn position_3d(&self) -> [f64; 3] {
86        self.position_3d
87    }
88
89    /// The pressure magnitude, normalized 0..1.
90    pub fn pressure(&self) -> f64 {
91        let px = self.pressure_3d[0];
92        let py = self.pressure_3d[1];
93        let pz = self.pressure_3d[2];
94        (px * px + py * py + pz * pz).sqrt()
95    }
96
97    /// The pressure vector in 3D.
98    pub fn pressure_3d(&self) -> [f64; 3] {
99        self.pressure_3d
100    }
101}
102
103/// When a touch is started, moved, ended or cancelled.
104pub trait TouchEvent: Sized {
105    /// Creates a touch event.
106    ///
107    /// Preserves time stamp from original input event, if any.
108    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>;
109    /// Calls closure if this is a touch event.
110    fn touch<U, F>(&self, f: F) -> Option<U>
111    where
112        F: FnMut(&TouchArgs) -> U;
113    /// Returns touch arguments.
114    fn touch_args(&self) -> Option<TouchArgs> {
115        self.touch(|args| *args)
116    }
117}
118
119impl TouchEvent for Event {
120    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self> {
121        let timestamp = if let Event::Input(_, x) = old_event {
122            *x
123        } else {
124            None
125        };
126        Some(Event::Input(Input::Move(Motion::Touch(*args)), timestamp))
127    }
128
129    fn touch<U, F>(&self, mut f: F) -> Option<U>
130    where
131        F: FnMut(&TouchArgs) -> U,
132    {
133        match *self {
134            Event::Input(Input::Move(Motion::Touch(ref args)), _) => Some(f(args)),
135            _ => None,
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_input_touch() {
146        let pos = [0.0; 2];
147        let e: Event = TouchArgs::new(0, 0, pos, 1.0, Touch::Start).into();
148        let a: Option<Event> =
149            TouchEvent::from_touch_args(&TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
150        let b: Option<Event> = a
151            .clone()
152            .unwrap()
153            .touch(|t| {
154                TouchEvent::from_touch_args(
155                    &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
156                    a.as_ref().unwrap(),
157                )
158            })
159            .unwrap();
160        assert_eq!(a, b);
161    }
162
163    #[test]
164    fn test_input_touch_3d() {
165        use super::super::Event;
166
167        let pos = [0.0; 3];
168        let pressure = [0.0, 0.0, 1.0];
169        let e: Event = TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start).into();
170        let a: Option<Event> =
171            TouchEvent::from_touch_args(&TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
172        let b: Option<Event> = a
173            .clone()
174            .unwrap()
175            .touch(|t| {
176                TouchEvent::from_touch_args(
177                    &TouchArgs::new_3d(
178                        t.device,
179                        t.id,
180                        t.position_3d(),
181                        t.pressure_3d(),
182                        Touch::Start,
183                    ),
184                    a.as_ref().unwrap(),
185                )
186            })
187            .unwrap();
188        assert_eq!(a, b);
189    }
190}