Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:33:17.253Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

interactive.rs

879 lines · 27.6 KB · rust
1use crate::{
2    Bounds, Capslock, Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render,
3    Window, point, seal::Sealed,
4};
5use smallvec::SmallVec;
6use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
7
8/// An event from a platform input source.
9pub trait InputEvent: Sealed + 'static {
10    /// Convert this event into the platform input enum.
11    fn to_platform_input(self) -> PlatformInput;
12}
13
14/// A key event from the platform.
15pub trait KeyEvent: InputEvent {}
16
17/// A mouse event from the platform.
18pub trait MouseEvent: InputEvent {}
19
20/// A gesture event from the platform.
21pub trait GestureEvent: InputEvent {}
22
23/// The key down event equivalent for the platform.
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct KeyDownEvent {
26    /// The keystroke that was generated.
27    pub keystroke: Keystroke,
28
29    /// Whether the key is currently held down.
30    pub is_held: bool,
31
32    /// Whether to prefer character input over keybindings for this keystroke.
33    /// In some cases, like AltGr on Windows, modifiers are significant for character input.
34    pub prefer_character_input: bool,
35}
36
37impl Sealed for KeyDownEvent {}
38impl InputEvent for KeyDownEvent {
39    fn to_platform_input(self) -> PlatformInput {
40        PlatformInput::KeyDown(self)
41    }
42}
43impl KeyEvent for KeyDownEvent {}
44
45/// The key up event equivalent for the platform.
46#[derive(Clone, Debug)]
47pub struct KeyUpEvent {
48    /// The keystroke that was released.
49    pub keystroke: Keystroke,
50}
51
52impl Sealed for KeyUpEvent {}
53impl InputEvent for KeyUpEvent {
54    fn to_platform_input(self) -> PlatformInput {
55        PlatformInput::KeyUp(self)
56    }
57}
58impl KeyEvent for KeyUpEvent {}
59
60/// The modifiers changed event equivalent for the platform.
61#[derive(Clone, Debug, Default)]
62pub struct ModifiersChangedEvent {
63    /// The new state of the modifier keys
64    pub modifiers: Modifiers,
65    /// The new state of the capslock key
66    pub capslock: Capslock,
67}
68
69impl Sealed for ModifiersChangedEvent {}
70impl InputEvent for ModifiersChangedEvent {
71    fn to_platform_input(self) -> PlatformInput {
72        PlatformInput::ModifiersChanged(self)
73    }
74}
75impl KeyEvent for ModifiersChangedEvent {}
76
77impl Deref for ModifiersChangedEvent {
78    type Target = Modifiers;
79
80    fn deref(&self) -> &Self::Target {
81        &self.modifiers
82    }
83}
84
85/// The phase of a touch motion event.
86/// Based on the winit enum of the same name.
87#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
88pub enum TouchPhase {
89    /// The touch started.
90    Started,
91    /// The touch event is moving.
92    #[default]
93    Moved,
94    /// The touch phase has ended
95    Ended,
96    /// The touch was cancelled: the system took it and it will not end
97    /// normally. Consumers must fully unwind any in-progress interaction,
98    /// treating the touch as if it never committed.
99    Cancelled,
100}
101
102/// Identifies one touch (finger or stylus contact) for its lifetime, from
103/// [`TouchPhase::Started`] through [`TouchPhase::Ended`] or
104/// [`TouchPhase::Cancelled`].
105///
106/// The value is opaque and platform-defined; it is only guaranteed to be
107/// stable for the duration of the touch and unique among concurrent touches.
108#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
109pub struct TouchId(pub u64);
110
111/// A raw touch event from the platform.
112///
113///
114/// Dispatch contract (core implementation pending): a touch is hit-tested
115/// once, at [`TouchPhase::Started`], occlusion-aware; all subsequent events
116/// for the same [`TouchId`] are delivered to the elements under the starting
117/// position, even after the touch moves outside them.
118#[derive(Clone, Debug, Default)]
119pub struct TouchEvent {
120    /// Which touch this event belongs to.
121    pub id: TouchId,
122    /// The phase of the touch.
123    pub phase: TouchPhase,
124    /// The position of the touch in window coordinates.
125    pub position: Point<Pixels>,
126    /// Normalized touch force in `0.0..=1.0`, if the hardware reports it.
127    pub force: Option<f32>,
128}
129
130impl Sealed for TouchEvent {}
131impl InputEvent for TouchEvent {
132    fn to_platform_input(self) -> PlatformInput {
133        PlatformInput::Touch(self)
134    }
135}
136
137/// A mouse down event from the platform
138#[derive(Clone, Debug, Default)]
139pub struct MouseDownEvent {
140    /// Which mouse button was pressed.
141    pub button: MouseButton,
142
143    /// The position of the mouse on the window.
144    pub position: Point<Pixels>,
145
146    /// The modifiers that were held down when the mouse was pressed.
147    pub modifiers: Modifiers,
148
149    /// The number of times the button has been clicked.
150    pub click_count: usize,
151
152    /// Whether this is the first, focusing click.
153    pub first_mouse: bool,
154}
155
156impl Sealed for MouseDownEvent {}
157impl InputEvent for MouseDownEvent {
158    fn to_platform_input(self) -> PlatformInput {
159        PlatformInput::MouseDown(self)
160    }
161}
162impl MouseEvent for MouseDownEvent {}
163
164impl MouseDownEvent {
165    /// Returns true if this mouse up event should focus the element.
166    pub fn is_focusing(&self) -> bool {
167        match self.button {
168            MouseButton::Left => true,
169            _ => false,
170        }
171    }
172}
173
174/// A mouse up event from the platform
175#[derive(Clone, Debug, Default)]
176pub struct MouseUpEvent {
177    /// Which mouse button was released.
178    pub button: MouseButton,
179
180    /// The position of the mouse on the window.
181    pub position: Point<Pixels>,
182
183    /// The modifiers that were held down when the mouse was released.
184    pub modifiers: Modifiers,
185
186    /// The number of times the button has been clicked.
187    pub click_count: usize,
188}
189
190impl Sealed for MouseUpEvent {}
191impl InputEvent for MouseUpEvent {
192    fn to_platform_input(self) -> PlatformInput {
193        PlatformInput::MouseUp(self)
194    }
195}
196
197impl MouseEvent for MouseUpEvent {}
198
199impl MouseUpEvent {
200    /// Returns true if this mouse up event should focus the element.
201    pub fn is_focusing(&self) -> bool {
202        match self.button {
203            MouseButton::Left => true,
204            _ => false,
205        }
206    }
207}
208
209/// A click event, generated when a mouse button is pressed and released.
210#[derive(Clone, Debug, Default)]
211pub struct MouseClickEvent {
212    /// The mouse event when the button was pressed.
213    pub down: MouseDownEvent,
214
215    /// The mouse event when the button was released.
216    pub up: MouseUpEvent,
217}
218
219/// The stage of a pressure click event.
220#[derive(Clone, Copy, Debug, Default, PartialEq)]
221pub enum PressureStage {
222    /// No pressure.
223    #[default]
224    Zero,
225    /// Normal click pressure.
226    Normal,
227    /// High pressure, enough to trigger a force click.
228    Force,
229}
230
231/// A mouse pressure event from the platform. Generated when a force-sensitive trackpad is pressed hard.
232/// Currently only implemented for macOS trackpads.
233#[derive(Debug, Clone, Default)]
234pub struct MousePressureEvent {
235    /// Pressure of the current stage as a float between 0 and 1
236    pub pressure: f32,
237    /// The pressure stage of the event.
238    pub stage: PressureStage,
239    /// The position of the mouse on the window.
240    pub position: Point<Pixels>,
241    /// The modifiers that were held down when the mouse pressure changed.
242    pub modifiers: Modifiers,
243}
244
245impl Sealed for MousePressureEvent {}
246impl InputEvent for MousePressureEvent {
247    fn to_platform_input(self) -> PlatformInput {
248        PlatformInput::MousePressure(self)
249    }
250}
251impl MouseEvent for MousePressureEvent {}
252
253/// A click event that was generated by a keyboard button being pressed and released.
254#[derive(Clone, Debug, Default)]
255pub struct KeyboardClickEvent {
256    /// The keyboard button that was pressed to trigger the click.
257    pub button: KeyboardButton,
258
259    /// The bounds of the element that was clicked.
260    pub bounds: Bounds<Pixels>,
261}
262
263/// A click event that was generated by a recognized tap gesture on a touch
264/// screen.
265#[derive(Clone, Debug, Default)]
266pub struct TouchClickEvent {
267    /// The position of the tap in window coordinates.
268    pub position: Point<Pixels>,
269    /// The number of consecutive taps at this location (double tap = 2),
270    /// analogous to the mouse `click_count`.
271    pub tap_count: usize,
272    /// Whether this was a long press rather than a tap. Long presses are
273    /// touch's secondary activation: they are delivered to aux-click
274    /// listeners alongside right clicks, not to primary click listeners.
275    pub long_press: bool,
276}
277
278/// A click event, generated when a mouse button or keyboard button is pressed and released,
279/// or when a tap gesture is recognized on a touch screen.
280#[derive(Clone, Debug)]
281pub enum ClickEvent {
282    /// A click event trigger by a mouse button being pressed and released.
283    Mouse(MouseClickEvent),
284    /// A click event trigger by a keyboard button being pressed and released.
285    Keyboard(KeyboardClickEvent),
286    /// A click event triggered by a recognized tap gesture on a touch screen.
287    Touch(TouchClickEvent),
288}
289
290impl Default for ClickEvent {
291    fn default() -> Self {
292        ClickEvent::Keyboard(KeyboardClickEvent::default())
293    }
294}
295
296impl ClickEvent {
297    /// Returns the modifiers that were held during the click event
298    ///
299    /// `Keyboard`: The keyboard click events never have modifiers.
300    /// `Mouse`: Modifiers that were held during the mouse key up event.
301    pub fn modifiers(&self) -> Modifiers {
302        match self {
303            // Click events are only generated from keyboard events _without any modifiers_, so we know the modifiers are always Default
304            ClickEvent::Keyboard(_) => Modifiers::default(),
305            // Click events on the web only reflect the modifiers for the keyup event,
306            // tested via observing the behavior of the `ClickEvent.shiftKey` field in Chrome 138
307            // under various combinations of modifiers and keyUp / keyDown events.
308            ClickEvent::Mouse(event) => event.up.modifiers,
309            // Touch screens have no modifier keys.
310            ClickEvent::Touch(_) => Modifiers::default(),
311        }
312    }
313
314    /// Returns the position of the click event
315    ///
316    /// `Keyboard`: The bottom left corner of the clicked hitbox
317    /// `Mouse`: The position of the mouse when the button was released.
318    /// `Touch`: The position of the tap.
319    pub fn position(&self) -> Point<Pixels> {
320        match self {
321            ClickEvent::Keyboard(event) => event.bounds.bottom_left(),
322            ClickEvent::Mouse(event) => event.up.position,
323            ClickEvent::Touch(event) => event.position,
324        }
325    }
326
327    /// Returns the mouse position of the click event
328    ///
329    /// `Keyboard`: None
330    /// `Mouse`: The position of the mouse when the button was released.
331    /// `Touch`: None, touches are not mouse input and there is no cursor.
332    pub fn mouse_position(&self) -> Option<Point<Pixels>> {
333        match self {
334            ClickEvent::Keyboard(_) => None,
335            ClickEvent::Mouse(event) => Some(event.up.position),
336            ClickEvent::Touch(_) => None,
337        }
338    }
339
340    /// Returns if this was a right click
341    ///
342    /// `Keyboard`: false
343    /// `Mouse`: Whether the right button was pressed and released
344    pub fn is_right_click(&self) -> bool {
345        match self {
346            ClickEvent::Keyboard(_) => false,
347            ClickEvent::Mouse(event) => {
348                event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
349            }
350            ClickEvent::Touch(_) => false,
351        }
352    }
353
354    /// Returns if this was a middle click
355    ///
356    /// `Keyboard`: false
357    /// `Mouse`: Whether the middle button was pressed and released
358    pub fn is_middle_click(&self) -> bool {
359        match self {
360            ClickEvent::Keyboard(_) => false,
361            ClickEvent::Mouse(event) => {
362                event.down.button == MouseButton::Middle && event.up.button == MouseButton::Middle
363            }
364            ClickEvent::Touch(_) => false,
365        }
366    }
367
368    /// Returns whether the click is a secondary activation, i.e. a context
369    /// menu trigger: a right click from a mouse (macOS ctrl-clicks arrive
370    /// already converted to right clicks by the platform layer), or a long
371    /// press on a touch screen.
372    pub fn is_secondary(&self) -> bool {
373        match self {
374            ClickEvent::Keyboard(_) => false,
375            ClickEvent::Mouse(event) => {
376                event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
377            }
378            ClickEvent::Touch(event) => event.long_press,
379        }
380    }
381
382    /// Returns whether the click was a standard click
383    ///
384    /// `Keyboard`: Always true
385    /// `Mouse`: Left button pressed and released
386    /// `Touch`: A tap, but not a long press
387    pub fn standard_click(&self) -> bool {
388        match self {
389            ClickEvent::Keyboard(_) => true,
390            ClickEvent::Mouse(event) => {
391                event.down.button == MouseButton::Left && event.up.button == MouseButton::Left
392            }
393            ClickEvent::Touch(event) => !event.long_press,
394        }
395    }
396
397    /// Returns whether the click focused the element
398    ///
399    /// `Keyboard`: false, keyboard clicks only work if an element is already focused
400    /// `Mouse`: Whether this was the first focusing click
401    /// `Touch`: false, mobile windows are already active when tappable
402    pub fn first_focus(&self) -> bool {
403        match self {
404            ClickEvent::Keyboard(_) => false,
405            ClickEvent::Mouse(event) => event.down.first_mouse,
406            ClickEvent::Touch(_) => false,
407        }
408    }
409
410    /// Returns the click count of the click event
411    ///
412    /// `Keyboard`: Always 1
413    /// `Mouse`: Count of clicks from MouseUpEvent
414    /// `Touch`: Count of consecutive taps
415    pub fn click_count(&self) -> usize {
416        match self {
417            ClickEvent::Keyboard(_) => 1,
418            ClickEvent::Mouse(event) => event.up.click_count,
419            ClickEvent::Touch(event) => event.tap_count,
420        }
421    }
422
423    /// Returns whether the click event is generated by a keyboard event
424    pub fn is_keyboard(&self) -> bool {
425        match self {
426            ClickEvent::Mouse(_) | ClickEvent::Touch(_) => false,
427            ClickEvent::Keyboard(_) => true,
428        }
429    }
430}
431
432/// An enum representing the keyboard button that was pressed for a click event.
433#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, Default)]
434pub enum KeyboardButton {
435    /// Enter key was clicked
436    #[default]
437    Enter,
438    /// Space key was clicked
439    Space,
440}
441
442/// An enum representing the mouse button that was pressed.
443#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
444pub enum MouseButton {
445    /// The left mouse button.
446    #[default]
447    Left,
448
449    /// The right mouse button.
450    Right,
451
452    /// The middle mouse button.
453    Middle,
454
455    /// A navigation button, such as back or forward.
456    Navigate(NavigationDirection),
457}
458
459impl MouseButton {
460    /// Get all the mouse buttons in a list.
461    pub fn all() -> Vec<Self> {
462        vec![
463            MouseButton::Left,
464            MouseButton::Right,
465            MouseButton::Middle,
466            MouseButton::Navigate(NavigationDirection::Back),
467            MouseButton::Navigate(NavigationDirection::Forward),
468        ]
469    }
470}
471
472/// A navigation direction, such as back or forward.
473#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
474pub enum NavigationDirection {
475    /// The back button.
476    #[default]
477    Back,
478
479    /// The forward button.
480    Forward,
481}
482
483/// A mouse move event from the platform.
484#[derive(Clone, Debug, Default)]
485pub struct MouseMoveEvent {
486    /// The position of the mouse on the window.
487    pub position: Point<Pixels>,
488
489    /// The mouse button that was pressed, if any.
490    pub pressed_button: Option<MouseButton>,
491
492    /// The modifiers that were held down when the mouse was moved.
493    pub modifiers: Modifiers,
494}
495
496impl Sealed for MouseMoveEvent {}
497impl InputEvent for MouseMoveEvent {
498    fn to_platform_input(self) -> PlatformInput {
499        PlatformInput::MouseMove(self)
500    }
501}
502impl MouseEvent for MouseMoveEvent {}
503
504impl MouseMoveEvent {
505    /// Returns true if the left mouse button is currently held down.
506    pub fn dragging(&self) -> bool {
507        self.pressed_button == Some(MouseButton::Left)
508    }
509}
510
511/// A mouse wheel event from the platform.
512#[derive(Clone, Debug, Default)]
513pub struct ScrollWheelEvent {
514    /// The position of the mouse on the window.
515    pub position: Point<Pixels>,
516
517    /// The change in scroll wheel position for this event.
518    pub delta: ScrollDelta,
519
520    /// The modifiers that were held down when the mouse was moved.
521    pub modifiers: Modifiers,
522
523    /// The phase of the touch event.
524    pub touch_phase: TouchPhase,
525}
526
527impl Sealed for ScrollWheelEvent {}
528impl InputEvent for ScrollWheelEvent {
529    fn to_platform_input(self) -> PlatformInput {
530        PlatformInput::ScrollWheel(self)
531    }
532}
533impl MouseEvent for ScrollWheelEvent {}
534
535impl Deref for ScrollWheelEvent {
536    type Target = Modifiers;
537
538    fn deref(&self) -> &Self::Target {
539        &self.modifiers
540    }
541}
542
543/// The scroll delta for a scroll wheel event.
544#[derive(Clone, Copy, Debug)]
545pub enum ScrollDelta {
546    /// An exact scroll delta in pixels.
547    Pixels(Point<Pixels>),
548    /// An inexact scroll delta in lines.
549    Lines(Point<f32>),
550}
551
552impl Default for ScrollDelta {
553    fn default() -> Self {
554        Self::Lines(Default::default())
555    }
556}
557
558/// A pinch gesture event from the platform, generated when the user performs
559/// a pinch-to-zoom gesture (typically on a trackpad).
560///
561#[derive(Clone, Debug, Default)]
562pub struct PinchEvent {
563    /// The position of the pinch center on the window.
564    pub position: Point<Pixels>,
565
566    /// The zoom delta for this event.
567    /// Positive values indicate zooming in, negative values indicate zooming out.
568    /// For example, 0.1 represents a 10% zoom increase.
569    pub delta: f32,
570
571    /// The modifiers that were held down during the pinch gesture.
572    pub modifiers: Modifiers,
573
574    /// The phase of the pinch gesture.
575    pub phase: TouchPhase,
576}
577
578impl Sealed for PinchEvent {}
579impl InputEvent for PinchEvent {
580    fn to_platform_input(self) -> PlatformInput {
581        PlatformInput::Pinch(self)
582    }
583}
584impl GestureEvent for PinchEvent {}
585impl MouseEvent for PinchEvent {}
586
587impl Deref for PinchEvent {
588    type Target = Modifiers;
589
590    fn deref(&self) -> &Self::Target {
591        &self.modifiers
592    }
593}
594
595impl ScrollDelta {
596    /// Returns true if this is a precise scroll delta in pixels.
597    pub fn precise(&self) -> bool {
598        match self {
599            ScrollDelta::Pixels(_) => true,
600            ScrollDelta::Lines(_) => false,
601        }
602    }
603
604    /// Converts this scroll event into exact pixels.
605    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
606        match self {
607            ScrollDelta::Pixels(delta) => *delta,
608            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
609        }
610    }
611
612    /// Combines two scroll deltas into one.
613    /// If the signs of the deltas are the same (both positive or both negative),
614    /// the deltas are added together. If the signs are opposite, the second delta
615    /// (other) is used, effectively overriding the first delta.
616    pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
617        match (self, other) {
618            (ScrollDelta::Pixels(a), ScrollDelta::Pixels(b)) => {
619                let x = if a.x.signum() == b.x.signum() {
620                    a.x + b.x
621                } else {
622                    b.x
623                };
624
625                let y = if a.y.signum() == b.y.signum() {
626                    a.y + b.y
627                } else {
628                    b.y
629                };
630
631                ScrollDelta::Pixels(point(x, y))
632            }
633
634            (ScrollDelta::Lines(a), ScrollDelta::Lines(b)) => {
635                let x = if a.x.signum() == b.x.signum() {
636                    a.x + b.x
637                } else {
638                    b.x
639                };
640
641                let y = if a.y.signum() == b.y.signum() {
642                    a.y + b.y
643                } else {
644                    b.y
645                };
646
647                ScrollDelta::Lines(point(x, y))
648            }
649
650            _ => other,
651        }
652    }
653}
654
655/// A mouse exit event from the platform, generated when the mouse leaves the window.
656#[derive(Clone, Debug, Default)]
657pub struct MouseExitEvent {
658    /// The position of the mouse relative to the window.
659    pub position: Point<Pixels>,
660    /// The mouse button that was pressed, if any.
661    pub pressed_button: Option<MouseButton>,
662    /// The modifiers that were held down when the mouse was moved.
663    pub modifiers: Modifiers,
664}
665
666impl Sealed for MouseExitEvent {}
667impl InputEvent for MouseExitEvent {
668    fn to_platform_input(self) -> PlatformInput {
669        PlatformInput::MouseExited(self)
670    }
671}
672
673impl MouseEvent for MouseExitEvent {}
674
675impl Deref for MouseExitEvent {
676    type Target = Modifiers;
677
678    fn deref(&self) -> &Self::Target {
679        &self.modifiers
680    }
681}
682
683/// A collection of paths from the platform, such as from a file drop.
684#[derive(Debug, Clone, Default, Eq, PartialEq)]
685pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
686
687impl ExternalPaths {
688    /// Convert this collection of paths into a slice.
689    pub fn paths(&self) -> &[PathBuf] {
690        &self.0
691    }
692}
693
694impl Render for ExternalPaths {
695    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
696        // the platform will render icons for the dragged files
697        Empty
698    }
699}
700
701/// A file drop event from the platform, generated when files are dragged and dropped onto the window.
702#[derive(Debug, Clone)]
703pub enum FileDropEvent {
704    /// The files have entered the window.
705    Entered {
706        /// The position of the mouse relative to the window.
707        position: Point<Pixels>,
708        /// The paths of the files that are being dragged.
709        paths: ExternalPaths,
710    },
711    /// The files are being dragged over the window
712    Pending {
713        /// The position of the mouse relative to the window.
714        position: Point<Pixels>,
715    },
716    /// The files have been dropped onto the window.
717    Submit {
718        /// The position of the mouse relative to the window.
719        position: Point<Pixels>,
720    },
721    /// The user has stopped dragging the files over the window.
722    Exited,
723}
724
725impl Sealed for FileDropEvent {}
726impl InputEvent for FileDropEvent {
727    fn to_platform_input(self) -> PlatformInput {
728        PlatformInput::FileDrop(self)
729    }
730}
731impl MouseEvent for FileDropEvent {}
732
733/// An enum corresponding to all kinds of platform input events.
734#[derive(Clone, Debug)]
735pub enum PlatformInput {
736    /// A key was pressed.
737    KeyDown(KeyDownEvent),
738    /// A key was released.
739    KeyUp(KeyUpEvent),
740    /// The keyboard modifiers were changed.
741    ModifiersChanged(ModifiersChangedEvent),
742    /// The mouse was pressed.
743    MouseDown(MouseDownEvent),
744    /// The mouse was released.
745    MouseUp(MouseUpEvent),
746    /// Mouse pressure.
747    MousePressure(MousePressureEvent),
748    /// The mouse was moved.
749    MouseMove(MouseMoveEvent),
750    /// The mouse exited the window.
751    MouseExited(MouseExitEvent),
752    /// The scroll wheel was used.
753    ScrollWheel(ScrollWheelEvent),
754    /// A pinch gesture was performed.
755    Pinch(PinchEvent),
756    /// Files were dragged and dropped onto the window.
757    FileDrop(FileDropEvent),
758    /// A raw touch event on a touch screen.
759    Touch(TouchEvent),
760}
761
762impl PlatformInput {
763    pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
764        match self {
765            PlatformInput::KeyDown { .. } => None,
766            PlatformInput::KeyUp { .. } => None,
767            PlatformInput::ModifiersChanged { .. } => None,
768            PlatformInput::MouseDown(event) => Some(event),
769            PlatformInput::MouseUp(event) => Some(event),
770            PlatformInput::MouseMove(event) => Some(event),
771            PlatformInput::MousePressure(event) => Some(event),
772            PlatformInput::MouseExited(event) => Some(event),
773            PlatformInput::ScrollWheel(event) => Some(event),
774            PlatformInput::Pinch(event) => Some(event),
775            PlatformInput::FileDrop(event) => Some(event),
776            PlatformInput::Touch(_) => None,
777        }
778    }
779
780    pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
781        match self {
782            PlatformInput::KeyDown(event) => Some(event),
783            PlatformInput::KeyUp(event) => Some(event),
784            PlatformInput::ModifiersChanged(event) => Some(event),
785            PlatformInput::MouseDown(_) => None,
786            PlatformInput::MouseUp(_) => None,
787            PlatformInput::MouseMove(_) => None,
788            PlatformInput::MousePressure(_) => None,
789            PlatformInput::MouseExited(_) => None,
790            PlatformInput::ScrollWheel(_) => None,
791            PlatformInput::Pinch(_) => None,
792            PlatformInput::FileDrop(_) => None,
793            PlatformInput::Touch(_) => None,
794        }
795    }
796
797    /// Returns the touch event contained in this input, if any.
798    pub fn touch_event(&self) -> Option<&TouchEvent> {
799        match self {
800            PlatformInput::Touch(event) => Some(event),
801            _ => None,
802        }
803    }
804}
805
806#[cfg(test)]
807mod test {
808
809    use crate::{
810        self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
811        KeyBinding, Keystroke, ParentElement, Render, TestAppContext, Window, div,
812    };
813
814    struct TestView {
815        saw_key_down: bool,
816        saw_action: bool,
817        focus_handle: FocusHandle,
818    }
819
820    actions!(test_only, [TestAction]);
821
822    impl Render for TestView {
823        fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
824            div().id("testview").child(
825                div()
826                    .key_context("parent")
827                    .on_key_down(cx.listener(|this, _, _, cx| {
828                        cx.stop_propagation();
829                        this.saw_key_down = true
830                    }))
831                    .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
832                        this.saw_action = true
833                    }))
834                    .child(
835                        div()
836                            .key_context("nested")
837                            .track_focus(&self.focus_handle)
838                            .into_element(),
839                    ),
840            )
841        }
842    }
843
844    #[gpui::test]
845    fn test_on_events(cx: &mut TestAppContext) {
846        let window = cx.update(|cx| {
847            cx.open_window(Default::default(), |_, cx| {
848                cx.new(|cx| TestView {
849                    saw_key_down: false,
850                    saw_action: false,
851                    focus_handle: cx.focus_handle(),
852                })
853            })
854            .unwrap()
855        });
856
857        cx.update(|cx| {
858            cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
859        });
860
861        window
862            .update(cx, |test_view, window, cx| {
863                window.focus(&test_view.focus_handle, cx)
864            })
865            .unwrap();
866
867        cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
868        cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
869
870        window
871            .update(cx, |test_view, _, _| {
872                assert!(test_view.saw_key_down || test_view.saw_action);
873                assert!(test_view.saw_key_down);
874                assert!(test_view.saw_action);
875            })
876            .unwrap();
877    }
878}
879
Served at tenant.openagents/omega Member data and write actions are omitted.