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, Default, Eq, PartialEq, Hash)]
30pub struct KeyLayout {
31 pub unshifted: String,
33
34 pub shifted: Option<String>,
37
38 pub shift: bool,
41}
42
43#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct KeyDownEvent {
46 pub keystroke: Keystroke,
48
49 pub is_held: bool,
51
52 pub prefer_character_input: bool,
55
56 pub layout: Option<KeyLayout>,
60}
61
62impl Sealed for KeyDownEvent {}
63impl InputEvent for KeyDownEvent {
64 fn to_platform_input(self) -> PlatformInput {
65 PlatformInput::KeyDown(self)
66 }
67}
68impl KeyEvent for KeyDownEvent {}
69
70#[derive(Clone, Debug)]
72pub struct KeyUpEvent {
73 pub keystroke: Keystroke,
75
76 pub layout: Option<KeyLayout>,
79}
80
81impl Sealed for KeyUpEvent {}
82impl InputEvent for KeyUpEvent {
83 fn to_platform_input(self) -> PlatformInput {
84 PlatformInput::KeyUp(self)
85 }
86}
87impl KeyEvent for KeyUpEvent {}
88
89#[derive(Clone, Debug, Default)]
91pub struct ModifiersChangedEvent {
92 pub modifiers: Modifiers,
94 pub capslock: Capslock,
96}
97
98impl Sealed for ModifiersChangedEvent {}
99impl InputEvent for ModifiersChangedEvent {
100 fn to_platform_input(self) -> PlatformInput {
101 PlatformInput::ModifiersChanged(self)
102 }
103}
104impl KeyEvent for ModifiersChangedEvent {}
105
106impl Deref for ModifiersChangedEvent {
107 type Target = Modifiers;
108
109 fn deref(&self) -> &Self::Target {
110 &self.modifiers
111 }
112}
113
114#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
117pub enum TouchPhase {
118 Started,
120 #[default]
122 Moved,
123 Ended,
125 Cancelled,
129}
130
131#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
138pub struct TouchId(pub u64);
139
140#[derive(Clone, Debug, Default)]
148pub struct TouchEvent {
149 pub id: TouchId,
151 pub phase: TouchPhase,
153 pub position: Point<Pixels>,
155 pub predicted_position: Option<Point<Pixels>>,
164 pub force: Option<f32>,
166}
167
168impl Sealed for TouchEvent {}
169impl InputEvent for TouchEvent {
170 fn to_platform_input(self) -> PlatformInput {
171 PlatformInput::Touch(self)
172 }
173}
174
175#[derive(Clone, Debug, Default)]
177pub struct MouseDownEvent {
178 pub button: MouseButton,
180
181 pub position: Point<Pixels>,
183
184 pub modifiers: Modifiers,
186
187 pub click_count: usize,
189
190 pub first_mouse: bool,
192}
193
194impl Sealed for MouseDownEvent {}
195impl InputEvent for MouseDownEvent {
196 fn to_platform_input(self) -> PlatformInput {
197 PlatformInput::MouseDown(self)
198 }
199}
200impl MouseEvent for MouseDownEvent {}
201
202impl MouseDownEvent {
203 pub fn is_focusing(&self) -> bool {
205 match self.button {
206 MouseButton::Left => true,
207 _ => false,
208 }
209 }
210}
211
212#[derive(Clone, Debug, Default)]
214pub struct MouseUpEvent {
215 pub button: MouseButton,
217
218 pub position: Point<Pixels>,
220
221 pub modifiers: Modifiers,
223
224 pub click_count: usize,
226}
227
228impl Sealed for MouseUpEvent {}
229impl InputEvent for MouseUpEvent {
230 fn to_platform_input(self) -> PlatformInput {
231 PlatformInput::MouseUp(self)
232 }
233}
234
235impl MouseEvent for MouseUpEvent {}
236
237impl MouseUpEvent {
238 pub fn is_focusing(&self) -> bool {
240 match self.button {
241 MouseButton::Left => true,
242 _ => false,
243 }
244 }
245}
246
247#[derive(Clone, Debug, Default)]
249pub struct MouseClickEvent {
250 pub down: MouseDownEvent,
252
253 pub up: MouseUpEvent,
255}
256
257#[derive(Clone, Copy, Debug, Default, PartialEq)]
259pub enum PressureStage {
260 #[default]
262 Zero,
263 Normal,
265 Force,
267}
268
269#[derive(Debug, Clone, Default)]
272pub struct MousePressureEvent {
273 pub pressure: f32,
275 pub stage: PressureStage,
277 pub position: Point<Pixels>,
279 pub modifiers: Modifiers,
281}
282
283impl Sealed for MousePressureEvent {}
284impl InputEvent for MousePressureEvent {
285 fn to_platform_input(self) -> PlatformInput {
286 PlatformInput::MousePressure(self)
287 }
288}
289impl MouseEvent for MousePressureEvent {}
290
291#[derive(Clone, Debug, Default)]
293pub struct KeyboardClickEvent {
294 pub button: KeyboardButton,
296
297 pub bounds: Bounds<Pixels>,
299}
300
301#[derive(Clone, Debug, Default)]
304pub struct TouchClickEvent {
305 pub position: Point<Pixels>,
307 pub tap_count: usize,
310 pub long_press: bool,
314}
315
316#[derive(Clone, Debug)]
319pub enum ClickEvent {
320 Mouse(MouseClickEvent),
322 Keyboard(KeyboardClickEvent),
324 Touch(TouchClickEvent),
326}
327
328impl Default for ClickEvent {
329 fn default() -> Self {
330 ClickEvent::Keyboard(KeyboardClickEvent::default())
331 }
332}
333
334impl ClickEvent {
335 pub fn modifiers(&self) -> Modifiers {
340 match self {
341 ClickEvent::Keyboard(_) => Modifiers::default(),
343 ClickEvent::Mouse(event) => event.up.modifiers,
347 ClickEvent::Touch(_) => Modifiers::default(),
349 }
350 }
351
352 pub fn position(&self) -> Point<Pixels> {
358 match self {
359 ClickEvent::Keyboard(event) => event.bounds.bottom_left(),
360 ClickEvent::Mouse(event) => event.up.position,
361 ClickEvent::Touch(event) => event.position,
362 }
363 }
364
365 pub fn mouse_position(&self) -> Option<Point<Pixels>> {
371 match self {
372 ClickEvent::Keyboard(_) => None,
373 ClickEvent::Mouse(event) => Some(event.up.position),
374 ClickEvent::Touch(_) => None,
375 }
376 }
377
378 pub fn is_right_click(&self) -> bool {
383 match self {
384 ClickEvent::Keyboard(_) => false,
385 ClickEvent::Mouse(event) => {
386 event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
387 }
388 ClickEvent::Touch(_) => false,
389 }
390 }
391
392 pub fn is_middle_click(&self) -> bool {
397 match self {
398 ClickEvent::Keyboard(_) => false,
399 ClickEvent::Mouse(event) => {
400 event.down.button == MouseButton::Middle && event.up.button == MouseButton::Middle
401 }
402 ClickEvent::Touch(_) => false,
403 }
404 }
405
406 pub fn is_secondary(&self) -> bool {
411 match self {
412 ClickEvent::Keyboard(_) => false,
413 ClickEvent::Mouse(event) => {
414 event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
415 }
416 ClickEvent::Touch(event) => event.long_press,
417 }
418 }
419
420 pub fn standard_click(&self) -> bool {
426 match self {
427 ClickEvent::Keyboard(_) => true,
428 ClickEvent::Mouse(event) => {
429 event.down.button == MouseButton::Left && event.up.button == MouseButton::Left
430 }
431 ClickEvent::Touch(event) => !event.long_press,
432 }
433 }
434
435 pub fn first_focus(&self) -> bool {
441 match self {
442 ClickEvent::Keyboard(_) => false,
443 ClickEvent::Mouse(event) => event.down.first_mouse,
444 ClickEvent::Touch(_) => false,
445 }
446 }
447
448 pub fn click_count(&self) -> usize {
454 match self {
455 ClickEvent::Keyboard(_) => 1,
456 ClickEvent::Mouse(event) => event.up.click_count,
457 ClickEvent::Touch(event) => event.tap_count,
458 }
459 }
460
461 pub fn is_keyboard(&self) -> bool {
463 match self {
464 ClickEvent::Mouse(_) | ClickEvent::Touch(_) => false,
465 ClickEvent::Keyboard(_) => true,
466 }
467 }
468}
469
470#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, Default)]
472pub enum KeyboardButton {
473 #[default]
475 Enter,
476 Space,
478}
479
480#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
482pub enum MouseButton {
483 #[default]
485 Left,
486
487 Right,
489
490 Middle,
492
493 Navigate(NavigationDirection),
495}
496
497impl MouseButton {
498 pub fn all() -> Vec<Self> {
500 vec![
501 MouseButton::Left,
502 MouseButton::Right,
503 MouseButton::Middle,
504 MouseButton::Navigate(NavigationDirection::Back),
505 MouseButton::Navigate(NavigationDirection::Forward),
506 ]
507 }
508}
509
510#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
512pub enum NavigationDirection {
513 #[default]
515 Back,
516
517 Forward,
519}
520
521#[derive(Clone, Debug, Default)]
523pub struct MouseMoveEvent {
524 pub position: Point<Pixels>,
526
527 pub pressed_button: Option<MouseButton>,
529
530 pub modifiers: Modifiers,
532}
533
534impl Sealed for MouseMoveEvent {}
535impl InputEvent for MouseMoveEvent {
536 fn to_platform_input(self) -> PlatformInput {
537 PlatformInput::MouseMove(self)
538 }
539}
540impl MouseEvent for MouseMoveEvent {}
541
542impl MouseMoveEvent {
543 pub fn dragging(&self) -> bool {
545 self.pressed_button == Some(MouseButton::Left)
546 }
547}
548
549#[derive(Clone, Debug, Default)]
551pub struct ScrollWheelEvent {
552 pub position: Point<Pixels>,
554
555 pub delta: ScrollDelta,
557
558 pub modifiers: Modifiers,
560
561 pub touch_phase: TouchPhase,
563}
564
565impl Sealed for ScrollWheelEvent {}
566impl InputEvent for ScrollWheelEvent {
567 fn to_platform_input(self) -> PlatformInput {
568 PlatformInput::ScrollWheel(self)
569 }
570}
571impl MouseEvent for ScrollWheelEvent {}
572
573impl Deref for ScrollWheelEvent {
574 type Target = Modifiers;
575
576 fn deref(&self) -> &Self::Target {
577 &self.modifiers
578 }
579}
580
581#[derive(Clone, Copy, Debug)]
583pub enum ScrollDelta {
584 Pixels(Point<Pixels>),
586 Lines(Point<f32>),
588}
589
590impl Default for ScrollDelta {
591 fn default() -> Self {
592 Self::Lines(Default::default())
593 }
594}
595
596#[derive(Clone, Debug, Default)]
600pub struct PinchEvent {
601 pub position: Point<Pixels>,
603
604 pub delta: f32,
608
609 pub modifiers: Modifiers,
611
612 pub phase: TouchPhase,
614}
615
616impl Sealed for PinchEvent {}
617impl InputEvent for PinchEvent {
618 fn to_platform_input(self) -> PlatformInput {
619 PlatformInput::Pinch(self)
620 }
621}
622impl GestureEvent for PinchEvent {}
623impl MouseEvent for PinchEvent {}
624
625impl Deref for PinchEvent {
626 type Target = Modifiers;
627
628 fn deref(&self) -> &Self::Target {
629 &self.modifiers
630 }
631}
632
633impl ScrollDelta {
634 pub fn precise(&self) -> bool {
636 match self {
637 ScrollDelta::Pixels(_) => true,
638 ScrollDelta::Lines(_) => false,
639 }
640 }
641
642 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
644 match self {
645 ScrollDelta::Pixels(delta) => *delta,
646 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
647 }
648 }
649
650 pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
655 match (self, other) {
656 (ScrollDelta::Pixels(a), ScrollDelta::Pixels(b)) => {
657 let x = if a.x.signum() == b.x.signum() {
658 a.x + b.x
659 } else {
660 b.x
661 };
662
663 let y = if a.y.signum() == b.y.signum() {
664 a.y + b.y
665 } else {
666 b.y
667 };
668
669 ScrollDelta::Pixels(point(x, y))
670 }
671
672 (ScrollDelta::Lines(a), ScrollDelta::Lines(b)) => {
673 let x = if a.x.signum() == b.x.signum() {
674 a.x + b.x
675 } else {
676 b.x
677 };
678
679 let y = if a.y.signum() == b.y.signum() {
680 a.y + b.y
681 } else {
682 b.y
683 };
684
685 ScrollDelta::Lines(point(x, y))
686 }
687
688 _ => other,
689 }
690 }
691}
692
693#[derive(Clone, Debug, Default)]
695pub struct MouseExitEvent {
696 pub position: Point<Pixels>,
698 pub pressed_button: Option<MouseButton>,
700 pub modifiers: Modifiers,
702}
703
704impl Sealed for MouseExitEvent {}
705impl InputEvent for MouseExitEvent {
706 fn to_platform_input(self) -> PlatformInput {
707 PlatformInput::MouseExited(self)
708 }
709}
710
711impl MouseEvent for MouseExitEvent {}
712
713impl Deref for MouseExitEvent {
714 type Target = Modifiers;
715
716 fn deref(&self) -> &Self::Target {
717 &self.modifiers
718 }
719}
720
721#[derive(Debug, Clone, Default, Eq, PartialEq)]
723pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
724
725impl ExternalPaths {
726 pub fn paths(&self) -> &[PathBuf] {
728 &self.0
729 }
730}
731
732#[derive(Debug, Clone, Eq, PartialEq)]
735pub enum ExternalDragPayload {
736 Files(FileDragPaths),
738}
739
740#[derive(Debug, Clone, Default, Eq, PartialEq)]
743pub struct FileDragPaths(SmallVec<[(PathBuf, bool); 2]>);
744
745impl FileDragPaths {
746 pub fn new(entries: impl IntoIterator<Item = (PathBuf, bool)>) -> Self {
748 Self(entries.into_iter().collect())
749 }
750
751 pub fn entries(&self) -> &[(PathBuf, bool)] {
753 &self.0
754 }
755}
756
757impl Render for ExternalPaths {
758 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
759 Empty
761 }
762}
763
764#[derive(Debug, Clone)]
766pub enum FileDropEvent {
767 Entered {
769 position: Point<Pixels>,
771 paths: ExternalPaths,
773 },
774 Pending {
776 position: Point<Pixels>,
778 },
779 Submit {
781 position: Point<Pixels>,
783 },
784 Exited,
786 Ended,
788}
789
790impl Sealed for FileDropEvent {}
791impl InputEvent for FileDropEvent {
792 fn to_platform_input(self) -> PlatformInput {
793 PlatformInput::FileDrop(self)
794 }
795}
796impl MouseEvent for FileDropEvent {}
797
798#[derive(Clone, Debug)]
800pub enum PlatformInput {
801 KeyDown(KeyDownEvent),
803 KeyUp(KeyUpEvent),
805 ModifiersChanged(ModifiersChangedEvent),
807 MouseDown(MouseDownEvent),
809 MouseUp(MouseUpEvent),
811 MousePressure(MousePressureEvent),
813 MouseMove(MouseMoveEvent),
815 MouseExited(MouseExitEvent),
817 ScrollWheel(ScrollWheelEvent),
819 Pinch(PinchEvent),
821 LongPress(LongPressEvent),
823 TouchDrag(TouchDragEvent),
825 FileDrop(FileDropEvent),
827 Touch(TouchEvent),
829}
830
831impl PlatformInput {
832 pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
833 match self {
834 PlatformInput::KeyDown { .. } => None,
835 PlatformInput::KeyUp { .. } => None,
836 PlatformInput::ModifiersChanged { .. } => None,
837 PlatformInput::MouseDown(event) => Some(event),
838 PlatformInput::MouseUp(event) => Some(event),
839 PlatformInput::MouseMove(event) => Some(event),
840 PlatformInput::MousePressure(event) => Some(event),
841 PlatformInput::MouseExited(event) => Some(event),
842 PlatformInput::ScrollWheel(event) => Some(event),
843 PlatformInput::Pinch(event) => Some(event),
844 PlatformInput::LongPress(event) => Some(event),
845 PlatformInput::TouchDrag(event) => Some(event),
846 PlatformInput::FileDrop(event) => Some(event),
847 PlatformInput::Touch(_) => None,
848 }
849 }
850
851 pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
852 match self {
853 PlatformInput::KeyDown(event) => Some(event),
854 PlatformInput::KeyUp(event) => Some(event),
855 PlatformInput::ModifiersChanged(event) => Some(event),
856 PlatformInput::MouseDown(_) => None,
857 PlatformInput::MouseUp(_) => None,
858 PlatformInput::MouseMove(_) => None,
859 PlatformInput::MousePressure(_) => None,
860 PlatformInput::MouseExited(_) => None,
861 PlatformInput::ScrollWheel(_) => None,
862 PlatformInput::Pinch(_) => None,
863 PlatformInput::LongPress(_) => None,
864 PlatformInput::TouchDrag(_) => None,
865 PlatformInput::FileDrop(_) => None,
866 PlatformInput::Touch(_) => None,
867 }
868 }
869
870 pub fn kind_name(&self) -> &'static str {
873 match self {
874 PlatformInput::KeyDown(_) => "key_down",
875 PlatformInput::KeyUp(_) => "key_up",
876 PlatformInput::ModifiersChanged(_) => "modifiers_changed",
877 PlatformInput::MouseDown(_) => "mouse_down",
878 PlatformInput::MouseUp(_) => "mouse_up",
879 PlatformInput::MousePressure(_) => "mouse_pressure",
880 PlatformInput::MouseMove(_) => "mouse_move",
881 PlatformInput::MouseExited(_) => "mouse_exited",
882 PlatformInput::ScrollWheel(_) => "scroll_wheel",
883 PlatformInput::Pinch(_) => "pinch",
884 PlatformInput::LongPress(_) => "long_press",
885 PlatformInput::TouchDrag(_) => "touch_drag",
886 PlatformInput::FileDrop(_) => "file_drop",
887 PlatformInput::Touch(_) => "touch",
888 }
889 }
890
891 pub fn touch_event(&self) -> Option<&TouchEvent> {
893 match self {
894 PlatformInput::Touch(event) => Some(event),
895 _ => None,
896 }
897 }
898}
899
900#[cfg(test)]
901mod test {
902
903 use crate::{
904 self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
905 KeyBinding, Keystroke, Modifiers, ParentElement, Render, TestAppContext, Window, div,
906 };
907
908 struct TestView {
909 saw_key_down: bool,
910 saw_action: bool,
911 focus_handle: FocusHandle,
912 }
913
914 actions!(test_only, [TestAction]);
915
916 impl Render for TestView {
917 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
918 div().id("testview").child(
919 div()
920 .key_context("parent")
921 .on_key_down(cx.listener(|this, _, _, cx| {
922 cx.stop_propagation();
923 this.saw_key_down = true
924 }))
925 .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
926 this.saw_action = true
927 }))
928 .child(
929 div()
930 .key_context("nested")
931 .track_focus(&self.focus_handle)
932 .into_element(),
933 ),
934 )
935 }
936 }
937
938 #[gpui::test]
939 fn test_on_events(cx: &mut TestAppContext) {
940 let window = cx.update(|cx| {
941 cx.open_window(Default::default(), |_, cx| {
942 cx.new(|cx| TestView {
943 saw_key_down: false,
944 saw_action: false,
945 focus_handle: cx.focus_handle(),
946 })
947 })
948 .unwrap()
949 });
950
951 cx.update(|cx| {
952 cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
953 });
954
955 window
956 .update(cx, |test_view, window, cx| {
957 window.focus(&test_view.focus_handle, cx)
958 })
959 .unwrap();
960
961 cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
962 cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
963
964 window
965 .update(cx, |test_view, _, _| {
966 assert!(test_view.saw_key_down || test_view.saw_action);
967 assert!(test_view.saw_key_down);
968 assert!(test_view.saw_action);
969 })
970 .unwrap();
971 }
972
973 #[gpui::test]
974 fn test_multi_modifier_gesture_does_not_dispatch_standalone_modifier_binding(
975 cx: &mut TestAppContext,
976 ) {
977 let (test_view, cx) = cx.add_window_view(|_, cx| TestView {
978 saw_key_down: false,
979 saw_action: false,
980 focus_handle: cx.focus_handle(),
981 });
982
983 cx.update(|_, cx| {
984 cx.bind_keys(vec![KeyBinding::new("shift", TestAction, None)]);
985 });
986 test_view.update_in(cx, |test_view, window, cx| {
987 window.focus(&test_view.focus_handle, cx);
988 });
989
990 cx.simulate_modifiers_change(Modifiers::alt());
991 cx.simulate_modifiers_change(Modifiers::alt() | Modifiers::shift());
992 cx.simulate_modifiers_change(Modifiers::shift());
993 cx.simulate_modifiers_change(Modifiers::none());
994 assert!(!test_view.read_with(cx, |test_view, _| test_view.saw_action));
995
996 cx.simulate_modifiers_change(Modifiers::shift());
997 cx.simulate_modifiers_change(Modifiers::none());
998 assert!(test_view.read_with(cx, |test_view, _| test_view.saw_action));
999 }
1000}