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}