1use crate::{
2 Bounds, Capslock, Context, Empty, IntoElement, Keystroke, LongPressEvent, Modifiers, Pixels,
3 Point, Render, TouchDragEvent, Window, point, seal::Sealed,
4};
5use smallvec::SmallVec;
6use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
7
8pub trait InputEvent: Sealed + 'static {
10 fn to_platform_input(self) -> PlatformInput;
12}
13
14pub trait KeyEvent: InputEvent {}
16
17pub trait MouseEvent: InputEvent {}
19
20pub trait GestureEvent: InputEvent {}
22
23#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct KeyDownEvent {
26 pub keystroke: Keystroke,
28
29 pub is_held: bool,
31
32 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#[derive(Clone, Debug)]
47pub struct KeyUpEvent {
48 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#[derive(Clone, Debug, Default)]
62pub struct ModifiersChangedEvent {
63 pub modifiers: Modifiers,
65 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
88pub enum TouchPhase {
89 Started,
91 #[default]
93 Moved,
94 Ended,
96 Cancelled,
100}
101
102#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
109pub struct TouchId(pub u64);
110
111#[derive(Clone, Debug, Default)]
119pub struct TouchEvent {
120 pub id: TouchId,
122 pub phase: TouchPhase,
124 pub position: Point<Pixels>,
126 pub predicted_position: Option<Point<Pixels>>,
135 pub force: Option<f32>,
137}
138
139impl Sealed for TouchEvent {}
140impl InputEvent for TouchEvent {
141 fn to_platform_input(self) -> PlatformInput {
142 PlatformInput::Touch(self)
143 }
144}
145
146#[derive(Clone, Debug, Default)]
148pub struct MouseDownEvent {
149 pub button: MouseButton,
151
152 pub position: Point<Pixels>,
154
155 pub modifiers: Modifiers,
157
158 pub click_count: usize,
160
161 pub first_mouse: bool,
163}
164
165impl Sealed for MouseDownEvent {}
166impl InputEvent for MouseDownEvent {
167 fn to_platform_input(self) -> PlatformInput {
168 PlatformInput::MouseDown(self)
169 }
170}
171impl MouseEvent for MouseDownEvent {}
172
173impl MouseDownEvent {
174 pub fn is_focusing(&self) -> bool {
176 match self.button {
177 MouseButton::Left => true,
178 _ => false,
179 }
180 }
181}
182
183#[derive(Clone, Debug, Default)]
185pub struct MouseUpEvent {
186 pub button: MouseButton,
188
189 pub position: Point<Pixels>,
191
192 pub modifiers: Modifiers,
194
195 pub click_count: usize,
197}
198
199impl Sealed for MouseUpEvent {}
200impl InputEvent for MouseUpEvent {
201 fn to_platform_input(self) -> PlatformInput {
202 PlatformInput::MouseUp(self)
203 }
204}
205
206impl MouseEvent for MouseUpEvent {}
207
208impl MouseUpEvent {
209 pub fn is_focusing(&self) -> bool {
211 match self.button {
212 MouseButton::Left => true,
213 _ => false,
214 }
215 }
216}
217
218#[derive(Clone, Debug, Default)]
220pub struct MouseClickEvent {
221 pub down: MouseDownEvent,
223
224 pub up: MouseUpEvent,
226}
227
228#[derive(Clone, Copy, Debug, Default, PartialEq)]
230pub enum PressureStage {
231 #[default]
233 Zero,
234 Normal,
236 Force,
238}
239
240#[derive(Debug, Clone, Default)]
243pub struct MousePressureEvent {
244 pub pressure: f32,
246 pub stage: PressureStage,
248 pub position: Point<Pixels>,
250 pub modifiers: Modifiers,
252}
253
254impl Sealed for MousePressureEvent {}
255impl InputEvent for MousePressureEvent {
256 fn to_platform_input(self) -> PlatformInput {
257 PlatformInput::MousePressure(self)
258 }
259}
260impl MouseEvent for MousePressureEvent {}
261
262#[derive(Clone, Debug, Default)]
264pub struct KeyboardClickEvent {
265 pub button: KeyboardButton,
267
268 pub bounds: Bounds<Pixels>,
270}
271
272#[derive(Clone, Debug, Default)]
275pub struct TouchClickEvent {
276 pub position: Point<Pixels>,
278 pub tap_count: usize,
281 pub long_press: bool,
285}
286
287#[derive(Clone, Debug)]
290pub enum ClickEvent {
291 Mouse(MouseClickEvent),
293 Keyboard(KeyboardClickEvent),
295 Touch(TouchClickEvent),
297}
298
299impl Default for ClickEvent {
300 fn default() -> Self {
301 ClickEvent::Keyboard(KeyboardClickEvent::default())
302 }
303}
304
305impl ClickEvent {
306 pub fn modifiers(&self) -> Modifiers {
311 match self {
312 ClickEvent::Keyboard(_) => Modifiers::default(),
314 ClickEvent::Mouse(event) => event.up.modifiers,
318 ClickEvent::Touch(_) => Modifiers::default(),
320 }
321 }
322
323 pub fn position(&self) -> Point<Pixels> {
329 match self {
330 ClickEvent::Keyboard(event) => event.bounds.bottom_left(),
331 ClickEvent::Mouse(event) => event.up.position,
332 ClickEvent::Touch(event) => event.position,
333 }
334 }
335
336 pub fn mouse_position(&self) -> Option<Point<Pixels>> {
342 match self {
343 ClickEvent::Keyboard(_) => None,
344 ClickEvent::Mouse(event) => Some(event.up.position),
345 ClickEvent::Touch(_) => None,
346 }
347 }
348
349 pub fn is_right_click(&self) -> bool {
354 match self {
355 ClickEvent::Keyboard(_) => false,
356 ClickEvent::Mouse(event) => {
357 event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
358 }
359 ClickEvent::Touch(_) => false,
360 }
361 }
362
363 pub fn is_middle_click(&self) -> bool {
368 match self {
369 ClickEvent::Keyboard(_) => false,
370 ClickEvent::Mouse(event) => {
371 event.down.button == MouseButton::Middle && event.up.button == MouseButton::Middle
372 }
373 ClickEvent::Touch(_) => false,
374 }
375 }
376
377 pub fn is_secondary(&self) -> bool {
382 match self {
383 ClickEvent::Keyboard(_) => false,
384 ClickEvent::Mouse(event) => {
385 event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
386 }
387 ClickEvent::Touch(event) => event.long_press,
388 }
389 }
390
391 pub fn standard_click(&self) -> bool {
397 match self {
398 ClickEvent::Keyboard(_) => true,
399 ClickEvent::Mouse(event) => {
400 event.down.button == MouseButton::Left && event.up.button == MouseButton::Left
401 }
402 ClickEvent::Touch(event) => !event.long_press,
403 }
404 }
405
406 pub fn first_focus(&self) -> bool {
412 match self {
413 ClickEvent::Keyboard(_) => false,
414 ClickEvent::Mouse(event) => event.down.first_mouse,
415 ClickEvent::Touch(_) => false,
416 }
417 }
418
419 pub fn click_count(&self) -> usize {
425 match self {
426 ClickEvent::Keyboard(_) => 1,
427 ClickEvent::Mouse(event) => event.up.click_count,
428 ClickEvent::Touch(event) => event.tap_count,
429 }
430 }
431
432 pub fn is_keyboard(&self) -> bool {
434 match self {
435 ClickEvent::Mouse(_) | ClickEvent::Touch(_) => false,
436 ClickEvent::Keyboard(_) => true,
437 }
438 }
439}
440
441#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, Default)]
443pub enum KeyboardButton {
444 #[default]
446 Enter,
447 Space,
449}
450
451#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
453pub enum MouseButton {
454 #[default]
456 Left,
457
458 Right,
460
461 Middle,
463
464 Navigate(NavigationDirection),
466}
467
468impl MouseButton {
469 pub fn all() -> Vec<Self> {
471 vec![
472 MouseButton::Left,
473 MouseButton::Right,
474 MouseButton::Middle,
475 MouseButton::Navigate(NavigationDirection::Back),
476 MouseButton::Navigate(NavigationDirection::Forward),
477 ]
478 }
479}
480
481#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
483pub enum NavigationDirection {
484 #[default]
486 Back,
487
488 Forward,
490}
491
492#[derive(Clone, Debug, Default)]
494pub struct MouseMoveEvent {
495 pub position: Point<Pixels>,
497
498 pub pressed_button: Option<MouseButton>,
500
501 pub modifiers: Modifiers,
503}
504
505impl Sealed for MouseMoveEvent {}
506impl InputEvent for MouseMoveEvent {
507 fn to_platform_input(self) -> PlatformInput {
508 PlatformInput::MouseMove(self)
509 }
510}
511impl MouseEvent for MouseMoveEvent {}
512
513impl MouseMoveEvent {
514 pub fn dragging(&self) -> bool {
516 self.pressed_button == Some(MouseButton::Left)
517 }
518}
519
520#[derive(Clone, Debug, Default)]
522pub struct ScrollWheelEvent {
523 pub position: Point<Pixels>,
525
526 pub delta: ScrollDelta,
528
529 pub modifiers: Modifiers,
531
532 pub touch_phase: TouchPhase,
534}
535
536impl Sealed for ScrollWheelEvent {}
537impl InputEvent for ScrollWheelEvent {
538 fn to_platform_input(self) -> PlatformInput {
539 PlatformInput::ScrollWheel(self)
540 }
541}
542impl MouseEvent for ScrollWheelEvent {}
543
544impl Deref for ScrollWheelEvent {
545 type Target = Modifiers;
546
547 fn deref(&self) -> &Self::Target {
548 &self.modifiers
549 }
550}
551
552#[derive(Clone, Copy, Debug)]
554pub enum ScrollDelta {
555 Pixels(Point<Pixels>),
557 Lines(Point<f32>),
559}
560
561impl Default for ScrollDelta {
562 fn default() -> Self {
563 Self::Lines(Default::default())
564 }
565}
566
567#[derive(Clone, Debug, Default)]
571pub struct PinchEvent {
572 pub position: Point<Pixels>,
574
575 pub delta: f32,
579
580 pub modifiers: Modifiers,
582
583 pub phase: TouchPhase,
585}
586
587impl Sealed for PinchEvent {}
588impl InputEvent for PinchEvent {
589 fn to_platform_input(self) -> PlatformInput {
590 PlatformInput::Pinch(self)
591 }
592}
593impl GestureEvent for PinchEvent {}
594impl MouseEvent for PinchEvent {}
595
596impl Deref for PinchEvent {
597 type Target = Modifiers;
598
599 fn deref(&self) -> &Self::Target {
600 &self.modifiers
601 }
602}
603
604impl ScrollDelta {
605 pub fn precise(&self) -> bool {
607 match self {
608 ScrollDelta::Pixels(_) => true,
609 ScrollDelta::Lines(_) => false,
610 }
611 }
612
613 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
615 match self {
616 ScrollDelta::Pixels(delta) => *delta,
617 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
618 }
619 }
620
621 pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
626 match (self, other) {
627 (ScrollDelta::Pixels(a), ScrollDelta::Pixels(b)) => {
628 let x = if a.x.signum() == b.x.signum() {
629 a.x + b.x
630 } else {
631 b.x
632 };
633
634 let y = if a.y.signum() == b.y.signum() {
635 a.y + b.y
636 } else {
637 b.y
638 };
639
640 ScrollDelta::Pixels(point(x, y))
641 }
642
643 (ScrollDelta::Lines(a), ScrollDelta::Lines(b)) => {
644 let x = if a.x.signum() == b.x.signum() {
645 a.x + b.x
646 } else {
647 b.x
648 };
649
650 let y = if a.y.signum() == b.y.signum() {
651 a.y + b.y
652 } else {
653 b.y
654 };
655
656 ScrollDelta::Lines(point(x, y))
657 }
658
659 _ => other,
660 }
661 }
662}
663
664#[derive(Clone, Debug, Default)]
666pub struct MouseExitEvent {
667 pub position: Point<Pixels>,
669 pub pressed_button: Option<MouseButton>,
671 pub modifiers: Modifiers,
673}
674
675impl Sealed for MouseExitEvent {}
676impl InputEvent for MouseExitEvent {
677 fn to_platform_input(self) -> PlatformInput {
678 PlatformInput::MouseExited(self)
679 }
680}
681
682impl MouseEvent for MouseExitEvent {}
683
684impl Deref for MouseExitEvent {
685 type Target = Modifiers;
686
687 fn deref(&self) -> &Self::Target {
688 &self.modifiers
689 }
690}
691
692#[derive(Debug, Clone, Default, Eq, PartialEq)]
694pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
695
696impl ExternalPaths {
697 pub fn paths(&self) -> &[PathBuf] {
699 &self.0
700 }
701}
702
703#[derive(Debug, Clone, Eq, PartialEq)]
706pub enum ExternalDragPayload {
707 Files(FileDragPaths),
709}
710
711#[derive(Debug, Clone, Default, Eq, PartialEq)]
714pub struct FileDragPaths(SmallVec<[(PathBuf, bool); 2]>);
715
716impl FileDragPaths {
717 pub fn new(entries: impl IntoIterator<Item = (PathBuf, bool)>) -> Self {
719 Self(entries.into_iter().collect())
720 }
721
722 pub fn entries(&self) -> &[(PathBuf, bool)] {
724 &self.0
725 }
726}
727
728impl Render for ExternalPaths {
729 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
730 Empty
732 }
733}
734
735#[derive(Debug, Clone)]
737pub enum FileDropEvent {
738 Entered {
740 position: Point<Pixels>,
742 paths: ExternalPaths,
744 },
745 Pending {
747 position: Point<Pixels>,
749 },
750 Submit {
752 position: Point<Pixels>,
754 },
755 Exited,
757 Ended,
759}
760
761impl Sealed for FileDropEvent {}
762impl InputEvent for FileDropEvent {
763 fn to_platform_input(self) -> PlatformInput {
764 PlatformInput::FileDrop(self)
765 }
766}
767impl MouseEvent for FileDropEvent {}
768
769#[derive(Clone, Debug)]
771pub enum PlatformInput {
772 KeyDown(KeyDownEvent),
774 KeyUp(KeyUpEvent),
776 ModifiersChanged(ModifiersChangedEvent),
778 MouseDown(MouseDownEvent),
780 MouseUp(MouseUpEvent),
782 MousePressure(MousePressureEvent),
784 MouseMove(MouseMoveEvent),
786 MouseExited(MouseExitEvent),
788 ScrollWheel(ScrollWheelEvent),
790 Pinch(PinchEvent),
792 LongPress(LongPressEvent),
794 TouchDrag(TouchDragEvent),
796 FileDrop(FileDropEvent),
798 Touch(TouchEvent),
800}
801
802impl PlatformInput {
803 pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
804 match self {
805 PlatformInput::KeyDown { .. } => None,
806 PlatformInput::KeyUp { .. } => None,
807 PlatformInput::ModifiersChanged { .. } => None,
808 PlatformInput::MouseDown(event) => Some(event),
809 PlatformInput::MouseUp(event) => Some(event),
810 PlatformInput::MouseMove(event) => Some(event),
811 PlatformInput::MousePressure(event) => Some(event),
812 PlatformInput::MouseExited(event) => Some(event),
813 PlatformInput::ScrollWheel(event) => Some(event),
814 PlatformInput::Pinch(event) => Some(event),
815 PlatformInput::LongPress(event) => Some(event),
816 PlatformInput::TouchDrag(event) => Some(event),
817 PlatformInput::FileDrop(event) => Some(event),
818 PlatformInput::Touch(_) => None,
819 }
820 }
821
822 pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
823 match self {
824 PlatformInput::KeyDown(event) => Some(event),
825 PlatformInput::KeyUp(event) => Some(event),
826 PlatformInput::ModifiersChanged(event) => Some(event),
827 PlatformInput::MouseDown(_) => None,
828 PlatformInput::MouseUp(_) => None,
829 PlatformInput::MouseMove(_) => None,
830 PlatformInput::MousePressure(_) => None,
831 PlatformInput::MouseExited(_) => None,
832 PlatformInput::ScrollWheel(_) => None,
833 PlatformInput::Pinch(_) => None,
834 PlatformInput::LongPress(_) => None,
835 PlatformInput::TouchDrag(_) => None,
836 PlatformInput::FileDrop(_) => None,
837 PlatformInput::Touch(_) => None,
838 }
839 }
840
841 pub fn kind_name(&self) -> &'static str {
844 match self {
845 PlatformInput::KeyDown(_) => "key_down",
846 PlatformInput::KeyUp(_) => "key_up",
847 PlatformInput::ModifiersChanged(_) => "modifiers_changed",
848 PlatformInput::MouseDown(_) => "mouse_down",
849 PlatformInput::MouseUp(_) => "mouse_up",
850 PlatformInput::MousePressure(_) => "mouse_pressure",
851 PlatformInput::MouseMove(_) => "mouse_move",
852 PlatformInput::MouseExited(_) => "mouse_exited",
853 PlatformInput::ScrollWheel(_) => "scroll_wheel",
854 PlatformInput::Pinch(_) => "pinch",
855 PlatformInput::LongPress(_) => "long_press",
856 PlatformInput::TouchDrag(_) => "touch_drag",
857 PlatformInput::FileDrop(_) => "file_drop",
858 PlatformInput::Touch(_) => "touch",
859 }
860 }
861
862 pub fn touch_event(&self) -> Option<&TouchEvent> {
864 match self {
865 PlatformInput::Touch(event) => Some(event),
866 _ => None,
867 }
868 }
869}
870
871#[cfg(test)]
872mod test {
873
874 use crate::{
875 self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
876 KeyBinding, Keystroke, Modifiers, ParentElement, Render, TestAppContext, Window, div,
877 };
878
879 struct TestView {
880 saw_key_down: bool,
881 saw_action: bool,
882 focus_handle: FocusHandle,
883 }
884
885 actions!(test_only, [TestAction]);
886
887 impl Render for TestView {
888 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
889 div().id("testview").child(
890 div()
891 .key_context("parent")
892 .on_key_down(cx.listener(|this, _, _, cx| {
893 cx.stop_propagation();
894 this.saw_key_down = true
895 }))
896 .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
897 this.saw_action = true
898 }))
899 .child(
900 div()
901 .key_context("nested")
902 .track_focus(&self.focus_handle)
903 .into_element(),
904 ),
905 )
906 }
907 }
908
909 #[gpui::test]
910 fn test_on_events(cx: &mut TestAppContext) {
911 let window = cx.update(|cx| {
912 cx.open_window(Default::default(), |_, cx| {
913 cx.new(|cx| TestView {
914 saw_key_down: false,
915 saw_action: false,
916 focus_handle: cx.focus_handle(),
917 })
918 })
919 .unwrap()
920 });
921
922 cx.update(|cx| {
923 cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
924 });
925
926 window
927 .update(cx, |test_view, window, cx| {
928 window.focus(&test_view.focus_handle, cx)
929 })
930 .unwrap();
931
932 cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
933 cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
934
935 window
936 .update(cx, |test_view, _, _| {
937 assert!(test_view.saw_key_down || test_view.saw_action);
938 assert!(test_view.saw_key_down);
939 assert!(test_view.saw_action);
940 })
941 .unwrap();
942 }
943
944 #[gpui::test]
945 fn test_multi_modifier_gesture_does_not_dispatch_standalone_modifier_binding(
946 cx: &mut TestAppContext,
947 ) {
948 let (test_view, cx) = cx.add_window_view(|_, cx| TestView {
949 saw_key_down: false,
950 saw_action: false,
951 focus_handle: cx.focus_handle(),
952 });
953
954 cx.update(|_, cx| {
955 cx.bind_keys(vec![KeyBinding::new("shift", TestAction, None)]);
956 });
957 test_view.update_in(cx, |test_view, window, cx| {
958 window.focus(&test_view.focus_handle, cx);
959 });
960
961 cx.simulate_modifiers_change(Modifiers::alt());
962 cx.simulate_modifiers_change(Modifiers::alt() | Modifiers::shift());
963 cx.simulate_modifiers_change(Modifiers::shift());
964 cx.simulate_modifiers_change(Modifiers::none());
965 assert!(!test_view.read_with(cx, |test_view, _| test_view.saw_action));
966
967 cx.simulate_modifiers_change(Modifiers::shift());
968 cx.simulate_modifiers_change(Modifiers::none());
969 assert!(test_view.read_with(cx, |test_view, _| test_view.saw_action));
970 }
971}