Skip to main content

sequoia_input/
lib.rs

1#![forbid(unsafe_code)]
2
3use bitflags::bitflags;
4
5use sequoia_core::{Point, Vec2};
6
7bitflags! {
8    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
9    pub struct Modifiers: u8 {
10        const NONE = 0;
11
12        const SHIFT = 1 << 0;
13
14        const CONTROL = 1 << 1;
15
16        const ALT = 1 << 2;
17
18        const SUPER = 1 << 3;
19    }
20}
21
22#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
23pub struct Key(u32);
24
25impl Key {
26    pub const NONE: Self = Self(0);
27
28    pub const TAB: Self = Self(1);
29
30    pub const ENTER: Self = Self(2);
31
32    pub const ESCAPE: Self = Self(3);
33
34    pub const SPACE: Self = Self(4);
35
36    pub const BACKSPACE: Self = Self(5);
37
38    pub const DELETE: Self = Self(6);
39
40    pub const ARROW_UP: Self = Self(7);
41
42    pub const ARROW_DOWN: Self = Self(8);
43
44    pub const ARROW_LEFT: Self = Self(9);
45
46    pub const ARROW_RIGHT: Self = Self(10);
47
48    pub const HOME: Self = Self(11);
49
50    pub const END: Self = Self(12);
51
52    pub const INSERT: Self = Self(13);
53
54    pub const PAGE_UP: Self = Self(14);
55
56    pub const PAGE_DOWN: Self = Self(15);
57
58    pub const F1: Self = Self(20);
59
60    pub const F2: Self = Self(21);
61
62    pub const F3: Self = Self(22);
63
64    pub const F4: Self = Self(23);
65
66    pub const F5: Self = Self(24);
67
68    pub const F6: Self = Self(25);
69
70    pub const F7: Self = Self(26);
71
72    pub const F8: Self = Self(27);
73
74    pub const F9: Self = Self(28);
75
76    pub const F10: Self = Self(29);
77
78    pub const F11: Self = Self(30);
79
80    pub const F12: Self = Self(31);
81
82    const CHAR_BASE: u32 = 0x1000;
83
84    pub fn from_char(c: char) -> Self {
85        Self(Self::CHAR_BASE + c as u32)
86    }
87
88    pub fn char(self) -> Option<char> {
89        if self.0 >= Self::CHAR_BASE {
90            char::from_u32(self.0 - Self::CHAR_BASE)
91        } else {
92            None
93        }
94    }
95
96    pub fn allows_repeat(self) -> bool {
97        if self.char().is_some() {
98            return true;
99        }
100        matches!(
101            self,
102            Self::SPACE
103                | Self::BACKSPACE
104                | Self::DELETE
105                | Self::ARROW_UP
106                | Self::ARROW_DOWN
107                | Self::ARROW_LEFT
108                | Self::ARROW_RIGHT
109                | Self::HOME
110                | Self::END
111                | Self::PAGE_UP
112                | Self::PAGE_DOWN
113                | Self::INSERT
114        )
115    }
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119#[non_exhaustive]
120pub enum PointerButton {
121    Left,
122
123    Right,
124
125    Middle,
126}
127
128#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
129#[non_exhaustive]
130pub enum PenTool {
131    #[default]
132    Mouse,
133
134    Pen,
135
136    Eraser,
137
138    Touch,
139
140    Unknown,
141}
142
143#[derive(Clone, Copy, Debug, PartialEq)]
144#[non_exhaustive]
145pub enum PointerEvent {
146    Move {
147        pos: Point,
148        modifiers: Modifiers,
149    },
150    Down {
151        button: PointerButton,
152        pos: Point,
153        modifiers: Modifiers,
154    },
155    Up {
156        button: PointerButton,
157        pos: Point,
158        modifiers: Modifiers,
159    },
160    Cancel,
161    Wheel {
162        delta: Vec2,
163        pos: Point,
164        modifiers: Modifiers,
165    },
166
167    PenMove {
168        pos: Point,
169        pressure: f32,
170        tilt: Vec2,
171        tool: PenTool,
172        modifiers: Modifiers,
173    },
174    PenDown {
175        button: PointerButton,
176        pos: Point,
177        pressure: f32,
178        tilt: Vec2,
179        tool: PenTool,
180        modifiers: Modifiers,
181    },
182    PenUp {
183        button: PointerButton,
184        pos: Point,
185        pressure: f32,
186        modifiers: Modifiers,
187    },
188
189    TouchDown {
190        id: u64,
191        pos: Point,
192        force: Option<f32>,
193        modifiers: Modifiers,
194    },
195    TouchMove {
196        id: u64,
197        pos: Point,
198        force: Option<f32>,
199        modifiers: Modifiers,
200    },
201    TouchUp {
202        id: u64,
203        pos: Point,
204        modifiers: Modifiers,
205    },
206
207    Pinch {
208        delta: f32,
209        pos: Point,
210        modifiers: Modifiers,
211    },
212}
213
214impl PointerEvent {
215    pub fn pos(self) -> Point {
216        match self {
217            Self::Move { pos, .. }
218            | Self::Down { pos, .. }
219            | Self::Up { pos, .. }
220            | Self::Wheel { pos, .. }
221            | Self::PenMove { pos, .. }
222            | Self::PenDown { pos, .. }
223            | Self::PenUp { pos, .. }
224            | Self::TouchDown { pos, .. }
225            | Self::TouchMove { pos, .. }
226            | Self::TouchUp { pos, .. }
227            | Self::Pinch { pos, .. } => pos,
228            Self::Cancel => Point::ZERO,
229        }
230    }
231
232    pub fn pressure(self) -> f32 {
233        match self {
234            Self::PenMove { pressure, .. }
235            | Self::PenDown { pressure, .. }
236            | Self::PenUp { pressure, .. } => pressure.clamp(0.0, 1.0),
237            Self::TouchDown { force, .. } | Self::TouchMove { force, .. } => {
238                force.unwrap_or(1.0).clamp(0.0, 1.0)
239            }
240            Self::Cancel => 0.0,
241            _ => 1.0,
242        }
243    }
244
245    pub fn tool(self) -> PenTool {
246        match self {
247            Self::PenMove { tool, .. } | Self::PenDown { tool, .. } => tool,
248            Self::PenUp { .. } => PenTool::Pen,
249            Self::TouchDown { .. } | Self::TouchMove { .. } | Self::TouchUp { .. } => {
250                PenTool::Touch
251            }
252            _ => PenTool::Mouse,
253        }
254    }
255
256    pub fn touch_id(self) -> Option<u64> {
257        match self {
258            Self::TouchDown { id, .. } | Self::TouchMove { id, .. } | Self::TouchUp { id, .. } => {
259                Some(id)
260            }
261            _ => None,
262        }
263    }
264
265    pub fn modifiers(self) -> Modifiers {
266        match self {
267            Self::Move { modifiers, .. }
268            | Self::Down { modifiers, .. }
269            | Self::Up { modifiers, .. }
270            | Self::Wheel { modifiers, .. }
271            | Self::PenMove { modifiers, .. }
272            | Self::PenDown { modifiers, .. }
273            | Self::PenUp { modifiers, .. }
274            | Self::TouchDown { modifiers, .. }
275            | Self::TouchMove { modifiers, .. }
276            | Self::TouchUp { modifiers, .. }
277            | Self::Pinch { modifiers, .. } => modifiers,
278            Self::Cancel => Modifiers::NONE,
279        }
280    }
281}
282
283#[derive(Clone, Copy, Debug, PartialEq)]
284pub struct KeyEvent {
285    pub key: Key,
286
287    pub modifiers: Modifiers,
288}
289
290#[derive(Clone, Debug, PartialEq)]
291#[non_exhaustive]
292pub enum ImeEvent {
293    Enabled,
294
295    Preedit {
296        value: String,
297
298        cursor: Option<(usize, usize)>,
299    },
300
301    Commit(String),
302
303    Disabled,
304}
305
306#[derive(Clone, Debug, PartialEq)]
307#[non_exhaustive]
308pub enum Event {
309    Pointer(PointerEvent),
310
311    Key(KeyEvent),
312
313    Ime(ImeEvent),
314
315    FilesHovered {
316        paths: Vec<std::path::PathBuf>,
317        position: Point,
318    },
319
320    FilesDropped {
321        paths: Vec<std::path::PathBuf>,
322        position: Point,
323    },
324
325    FilesCancelled,
326}
327
328#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
329#[non_exhaustive]
330pub enum DropEffect {
331    None,
332
333    #[default]
334    Move,
335
336    Copy,
337
338    Link,
339}
340
341pub fn effect_for_modifiers(mods: Modifiers) -> DropEffect {
342    if mods.contains(Modifiers::CONTROL) {
343        DropEffect::Copy
344    } else if mods.contains(Modifiers::ALT) {
345        DropEffect::Link
346    } else {
347        DropEffect::Move
348    }
349}
350
351#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
352#[non_exhaustive]
353pub enum CursorIcon {
354    #[default]
355    Default,
356
357    Pointer,
358
359    Grab,
360
361    Grabbing,
362
363    Text,
364
365    ColResize,
366
367    RowResize,
368
369    Crosshair,
370}
371
372#[derive(Clone, Copy, Debug, PartialEq, Eq)]
373pub enum EventPhase {
374    Capture,
375
376    Target,
377
378    Bubble,
379}
380
381#[cfg(test)]
382mod tests {
383    use super::*;
384
385    #[test]
386    fn key_char_roundtrip() {
387        let k = Key::from_char('a');
388        assert_eq!(k.char(), Some('a'));
389        assert_ne!(k, Key::from_char('b'));
390        assert_eq!(Key::TAB.char(), None);
391    }
392
393    #[test]
394    fn modifiers_ops() {
395        let m = Modifiers::SHIFT | Modifiers::CONTROL;
396        assert!(m.contains(Modifiers::SHIFT));
397        assert!(!m.contains(Modifiers::ALT));
398        assert_eq!(Modifiers::default(), Modifiers::NONE);
399    }
400
401    #[test]
402    fn drop_effect_for_modifiers() {
403        assert_eq!(effect_for_modifiers(Modifiers::NONE), DropEffect::Move);
404        assert_eq!(effect_for_modifiers(Modifiers::CONTROL), DropEffect::Copy);
405        assert_eq!(effect_for_modifiers(Modifiers::ALT), DropEffect::Link);
406
407        assert_eq!(
408            effect_for_modifiers(Modifiers::CONTROL | Modifiers::ALT),
409            DropEffect::Copy
410        );
411    }
412}