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)]
298#[non_exhaustive]
299pub enum ImeEvent {
300 Enabled,
302
303 Preedit {
311 value: String,
312
313 cursor: Option<(usize, usize)>,
314 },
315
316 Commit(String),
320
321 Disabled,
323}
324
325impl ImeEvent {
326 pub fn is_composing(&self) -> bool {
328 matches!(self, Self::Preedit { value, .. } if !value.is_empty())
329 }
330}
331
332#[derive(Clone, Debug, PartialEq)]
333#[non_exhaustive]
334pub enum Event {
335 Pointer(PointerEvent),
336
337 Key(KeyEvent),
338
339 Ime(ImeEvent),
340
341 FilesHovered {
342 paths: Vec<std::path::PathBuf>,
343 position: Point,
344 },
345
346 FilesDropped {
347 paths: Vec<std::path::PathBuf>,
348 position: Point,
349 },
350
351 FilesCancelled,
352}
353
354#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
355#[non_exhaustive]
356pub enum DropEffect {
357 None,
358
359 #[default]
360 Move,
361
362 Copy,
363
364 Link,
365}
366
367pub fn effect_for_modifiers(mods: Modifiers) -> DropEffect {
368 if mods.contains(Modifiers::CONTROL) {
369 DropEffect::Copy
370 } else if mods.contains(Modifiers::ALT) {
371 DropEffect::Link
372 } else {
373 DropEffect::Move
374 }
375}
376
377#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
378#[non_exhaustive]
379pub enum CursorIcon {
380 #[default]
381 Default,
382
383 Pointer,
384
385 Grab,
386
387 Grabbing,
388
389 Text,
390
391 ColResize,
392
393 RowResize,
394
395 Crosshair,
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Eq)]
399pub enum EventPhase {
400 Capture,
401
402 Target,
403
404 Bubble,
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410
411 #[test]
412 fn key_char_roundtrip() {
413 let k = Key::from_char('a');
414 assert_eq!(k.char(), Some('a'));
415 assert_ne!(k, Key::from_char('b'));
416 assert_eq!(Key::TAB.char(), None);
417 }
418
419 #[test]
420 fn modifiers_ops() {
421 let m = Modifiers::SHIFT | Modifiers::CONTROL;
422 assert!(m.contains(Modifiers::SHIFT));
423 assert!(!m.contains(Modifiers::ALT));
424 assert_eq!(Modifiers::default(), Modifiers::NONE);
425 }
426
427 #[test]
428 fn drop_effect_for_modifiers() {
429 assert_eq!(effect_for_modifiers(Modifiers::NONE), DropEffect::Move);
430 assert_eq!(effect_for_modifiers(Modifiers::CONTROL), DropEffect::Copy);
431 assert_eq!(effect_for_modifiers(Modifiers::ALT), DropEffect::Link);
432
433 assert_eq!(
434 effect_for_modifiers(Modifiers::CONTROL | Modifiers::ALT),
435 DropEffect::Copy
436 );
437 }
438}