Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/event.rs
1use std::ops::Range;
2 
3use crate::platform::keyboard::KeyCode;
4use crate::{
5 elements::{Point, ZIndex},
6 keymap::Keystroke,
7 zoom::{Scale, ZoomFactor},
8 EventContext,
9};
10use pathfinder_geometry::{rect::RectF, vector::Vector2F};
11 
12#[derive(Debug)]
13pub struct DispatchedEvent {
14 event: Event,
15}
16 
17impl DispatchedEvent {
18 /// Filters out event types that most-likely shouldn't be handled if this
19 /// event is being received by an element at the given z-index
20 pub fn at_z_index(&self, z_index: ZIndex, ctx: &EventContext) -> Option<&Event> {
21 match self.event {
22 Event::KeyDown { .. } => Some(&self.event),
23 Event::ScrollWheel { position, .. }
24 | Event::LeftMouseDown { position, .. }
25 | Event::LeftMouseUp { position, .. }
26 | Event::LeftMouseDragged { position, .. }
27 | Event::MiddleMouseDown { position, .. }
28 | Event::RightMouseDown { position, .. }
29 | Event::BackMouseDown { position, .. }
30 | Event::ForwardMouseDown { position, .. } => {
31 if !ctx.is_covered(Point::from_vec2f(position, z_index)) {
32 Some(&self.event)
33 } else {
34 None
35 }
36 }
37 Event::MouseMoved { .. } => Some(&self.event),
38 Event::ModifierStateChanged { .. } => Some(&self.event),
39 Event::ModifierKeyChanged { .. } => Some(&self.event),
40 Event::TypedCharacters { .. } => Some(&self.event),
41 Event::DragAndDropFiles { .. } => Some(&self.event),
42 Event::DragFiles { .. } => Some(&self.event),
43 Event::DragFileExit => Some(&self.event),
44 Event::SetMarkedText { .. } => Some(&self.event),
45 Event::ClearMarkedText => Some(&self.event),
46 }
47 }
48 
49 /// Returns the raw event - note that an element at a higher z-index
50 /// may already have handled it.
51 pub fn raw_event(&self) -> &Event {
52 &self.event
53 }
54}
55 
56impl From<Event> for DispatchedEvent {
57 fn from(event: Event) -> Self {
58 Self { event }
59 }
60}
61 
62/// Additional metadata about key events.
63#[derive(Clone, Debug, Default)]
64pub struct KeyEventDetails {
65 // allows distinguishing between left and right alt
66 pub left_alt: bool,
67 pub right_alt: bool,
68 /// The key that would have been produced without any modifiers (including Shift).
69 pub key_without_modifiers: Option<String>,
70}
71 
72#[derive(Copy, Clone, Debug, Default)]
73pub struct ModifiersState {
74 pub alt: bool,
75 pub cmd: bool,
76 pub shift: bool,
77 pub ctrl: bool,
78 /// The function key, often labeled as "fn" on keyboards.
79 /// We use "func" to avoid clashing with the `fn` keyword.
80 /// Note this is NOT fully implemented for non-Mac platforms yet.
81 pub func: bool,
82}
83 
84#[derive(Copy, Clone, Debug)]
85pub enum KeyState {
86 Pressed,
87 Released,
88}
89 
90/// TODO: for the events that have modifiers (e.g. cmd, shift), we should
91/// combine these into a Modifiers struct and pass these along from fn to fn.
92#[derive(Clone, Debug, strum_macros::EnumDiscriminants)]
93pub enum Event {
94 /// Gets fired when a key is pressed. The keystroke attribute contains the raw
95 /// key code and its modifiers.
96 KeyDown {
97 keystroke: Keystroke,
98 chars: String,
99 details: KeyEventDetails,
100 is_composing: bool,
101 },
102 ScrollWheel {
103 position: Vector2F,
104 delta: Vector2F,
105 precise: bool,
106 modifiers: ModifiersState,
107 },
108 LeftMouseDown {
109 position: Vector2F,
110 modifiers: ModifiersState,
111 click_count: u32,
112 /// Whether this is the first mouse down event on an inactive window
113 /// that is causing the window to activate.
114 is_first_mouse: bool,
115 },
116 LeftMouseUp {
117 position: Vector2F,
118 modifiers: ModifiersState,
119 },
120 LeftMouseDragged {
121 position: Vector2F,
122 modifiers: ModifiersState,
123 },
124 MiddleMouseDown {
125 position: Vector2F,
126 cmd: bool,
127 shift: bool,
128 click_count: u32,
129 },
130 RightMouseDown {
131 position: Vector2F,
132 cmd: bool,
133 shift: bool,
134 click_count: u32,
135 },
136 /// One of the side buttons often found on external mice.
137 /// It cycles forward between tabs by default.
138 ForwardMouseDown {
139 position: Vector2F,
140 cmd: bool,
141 shift: bool,
142 click_count: u32,
143 },
144 /// One of the side buttons often found on external mice.
145 /// It cycles backward between tabs by default.
146 BackMouseDown {
147 position: Vector2F,
148 cmd: bool,
149 shift: bool,
150 click_count: u32,
151 },
152 MouseMoved {
153 position: Vector2F,
154 cmd: bool,
155 shift: bool,
156 /// Whether this mouse event was initiated by the user or by Warp synthetically.
157 /// We create such synthetic mouse events for certain behaviors behaviors within
158 /// Warp, such as triggering the correct tab to be "hovered" when the user closes
159 /// a tab to the left. In this case, there's no user-initiated mouse event, however,
160 /// we create a synthetic mouse event so that we carry over the "hovered" state from
161 /// the old tab (closed) to the new tab (shifted). Certain elements within Warp,
162 /// such as the alt-screen, may not want such synthetic mouse events since it can
163 /// interfere with actions such as mouse dragging.
164 is_synthetic: bool,
165 },
166 /// Gets fired when the modifier flag states changed -- this could happen either
167 /// when a user presses down on or releases a modifier key.
168 ModifierStateChanged {
169 // Note that in web framework modifier keypresses do not contain mouse
170 // position information. But we also have cases where modifier flag state
171 // is closely coupled with mouse position for determine whether certain events
172 // should be fired. This position meta data will be kept in the event for now,
173 // we could always remove it in the future if this does not fit.
174 mouse_position: Vector2F,
175 modifiers: ModifiersState,
176 /// The specific key code for the event. Can be used to identify which modifier key
177 /// was pressed or released.
178 key_code: Option<KeyCode>,
179 },
180 /// Gets fired when a modifier key is pressed/released. Contains details on whether the
181 /// key was pressed or released and the key code.
182 ModifierKeyChanged {
183 key_code: KeyCode,
184 state: KeyState,
185 },
186 /// Gets fired when a printable character is produced by the text input system and
187 /// the corresponding KeyDown event is not handled. This event is not dispatched
188 /// when intermediary keys are pressed (e.g. dead keys or key presses in the IME).
189 /// TypedCharacter needs to be of type String because CJK languages represent words
190 /// with a set of characters and thus the output from IME could be more than one single character.
191 TypedCharacters {
192 chars: String,
193 },
194 /// Gets fired when user drags a file or folder into Warp. Note that there could exist
195 /// multiple file paths in one event as user could drag and drop multiple targets.
196 DragAndDropFiles {
197 paths: Vec<String>,
198 location: Vector2F,
199 },
200 
201 DragFiles {
202 location: Vector2F,
203 },
204 
205 DragFileExit,
206 
207 SetMarkedText {
208 marked_text: String,
209 selected_range: Range<usize>,
210 },
211 
212 ClearMarkedText,
213}
214 
215impl Event {
216 /// Returns the mouse-down position of the event,
217 /// iff the event is one of the many `*MouseDown` events.
218 pub fn mouse_down_position(&self) -> Option<Vector2F> {
219 match self {
220 Self::LeftMouseDown { position, .. }
221 | Self::RightMouseDown { position, .. }
222 | Self::MiddleMouseDown { position, .. }
223 | Self::ForwardMouseDown { position, .. }
224 | Self::BackMouseDown { position, .. } => Some(*position),
225 _ => None,
226 }
227 }
228 
229 /// Returns a copy of the event marked as synthetic, if this is a
230 /// `MouseMoved` event, otherwise returns [`None`].
231 pub fn to_synthetic_mouse_move_event(&self) -> Option<Self> {
232 if let Event::MouseMoved {
233 cmd,
234 shift,
235 position,
236 is_synthetic: _,
237 } = self
238 {
239 Some(Event::MouseMoved {
240 cmd: *cmd,
241 shift: *shift,
242 position: *position,
243 is_synthetic: true,
244 })
245 } else {
246 None
247 }
248 }
249}
250 
251pub trait InBoundsExt {
252 /// Check whether something occurred within the given bounding box.
253 fn in_bounds(&self, bounds: RectF) -> bool;
254}
255 
256impl InBoundsExt for Event {
257 fn in_bounds(&self, bounds: RectF) -> bool {
258 use Event::*;
259 
260 match self {
261 ScrollWheel { position, .. }
262 | LeftMouseDown { position, .. }
263 | LeftMouseUp { position, .. }
264 | LeftMouseDragged { position, .. }
265 | RightMouseDown { position, .. }
266 | MouseMoved { position, .. }
267 | MiddleMouseDown { position, .. } => bounds.contains_point(*position),
268 ModifierStateChanged { mouse_position, .. } => bounds.contains_point(*mouse_position),
269 DragAndDropFiles { location, .. } | DragFiles { location } => {
270 bounds.contains_point(*location)
271 }
272 // This trait is meant to check whether the Self is within the bounds,
273 // however in this implementation Self is Event that may not always be related to the
274 // mouse - so for all such cases, lets just return true.
275 _ => true,
276 }
277 }
278}
279 
280impl Scale for Event {
281 fn scale_up(self, zoom_factor: ZoomFactor) -> Self {
282 match self {
283 Event::ScrollWheel {
284 position,
285 delta,
286 precise,
287 modifiers,
288 } => Event::ScrollWheel {
289 position: position.scale_up(zoom_factor),
290 delta,
291 precise,
292 modifiers,
293 },
294 Event::LeftMouseDown {
295 position,
296 modifiers,
297 click_count,
298 is_first_mouse,
299 } => Event::LeftMouseDown {
300 position: position.scale_up(zoom_factor),
301 modifiers,
302 click_count,
303 is_first_mouse,
304 },
305 Event::LeftMouseUp {
306 position,
307 modifiers,
308 } => Event::LeftMouseUp {
309 position: position.scale_up(zoom_factor),
310 modifiers,
311 },
312 Event::LeftMouseDragged {
313 position,
314 modifiers,
315 } => Event::LeftMouseDragged {
316 position: position.scale_up(zoom_factor),
317 modifiers,
318 },
319 Event::MiddleMouseDown {
320 position,
321 cmd,
322 shift,
323 click_count,
324 } => Event::MiddleMouseDown {
325 position: position.scale_up(zoom_factor),
326 cmd,
327 shift,
328 click_count,
329 },
330 Event::RightMouseDown {
331 position,
332 cmd,
333 shift,
334 click_count,
335 } => Event::RightMouseDown {
336 position: position.scale_up(zoom_factor),
337 cmd,
338 shift,
339 click_count,
340 },
341 Event::ForwardMouseDown {
342 position,
343 cmd,
344 shift,
345 click_count,
346 } => Event::ForwardMouseDown {
347 position: position.scale_up(zoom_factor),
348 cmd,
349 shift,
350 click_count,
351 },
352 Event::BackMouseDown {
353 position,
354 cmd,
355 shift,
356 click_count,
357 } => Event::BackMouseDown {
358 position: position.scale_up(zoom_factor),
359 cmd,
360 shift,
361 click_count,
362 },
363 Event::MouseMoved {
364 position,
365 cmd,
366 shift,
367 is_synthetic,
368 } => Event::MouseMoved {
369 position: position.scale_up(zoom_factor),
370 cmd,
371 shift,
372 is_synthetic,
373 },
374 Event::ModifierStateChanged {
375 mouse_position,
376 modifiers,
377 key_code,
378 } => Event::ModifierStateChanged {
379 mouse_position: mouse_position.scale_up(zoom_factor),
380 modifiers,
381 key_code,
382 },
383 Event::DragAndDropFiles { paths, location } => Event::DragAndDropFiles {
384 paths,
385 location: location.scale_up(zoom_factor),
386 },
387 Event::DragFiles { location } => Event::DragFiles {
388 location: location.scale_up(zoom_factor),
389 },
390 Event::DragFileExit => Event::DragFileExit,
391 Event::KeyDown {
392 keystroke,
393 chars,
394 details,
395 is_composing,
396 } => Event::KeyDown {
397 keystroke,
398 chars,
399 details,
400 is_composing,
401 },
402 Event::ModifierKeyChanged { key_code, state } => {
403 Event::ModifierKeyChanged { key_code, state }
404 }
405 Event::TypedCharacters { chars } => Event::TypedCharacters { chars },
406 Event::SetMarkedText {
407 marked_text,
408 selected_range,
409 } => Event::SetMarkedText {
410 marked_text,
411 selected_range,
412 },
413 Event::ClearMarkedText => Event::ClearMarkedText,
414 }
415 }
416}
417