1use crate::{
2 BackGestureEvent, Bounds, Capslock, Context, Empty, IntoElement, Keystroke, LongPressEvent,
3 Modifiers, Pixels, 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 ScrollEvent {
523 pub position: Point<Pixels>,
526
527 pub delta: ScrollDelta,
529
530 pub modifiers: Modifiers,
532
533 pub touch_phase: TouchPhase,
535
536 pub source: ScrollSource,
538}
539
540#[deprecated(note = "renamed to `ScrollEvent`")]
542pub type ScrollWheelEvent = ScrollEvent;
543
544#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
546pub enum ScrollSource {
547 #[default]
551 Pointer,
552 Touch,
555}
556
557impl Sealed for ScrollEvent {}
558impl InputEvent for ScrollEvent {
559 fn to_platform_input(self) -> PlatformInput {
560 PlatformInput::Scroll(self)
561 }
562}
563impl MouseEvent for ScrollEvent {}
564
565impl Deref for ScrollEvent {
566 type Target = Modifiers;
567
568 fn deref(&self) -> &Self::Target {
569 &self.modifiers
570 }
571}
572
573#[derive(Clone, Copy, Debug)]
575pub enum ScrollDelta {
576 Pixels(Point<Pixels>),
578 Lines(Point<f32>),
580}
581
582impl Default for ScrollDelta {
583 fn default() -> Self {
584 Self::Lines(Default::default())
585 }
586}
587
588#[derive(Clone, Debug, Default)]
592pub struct PinchEvent {
593 pub position: Point<Pixels>,
595
596 pub delta: f32,
600
601 pub modifiers: Modifiers,
603
604 pub phase: TouchPhase,
606}
607
608impl Sealed for PinchEvent {}
609impl InputEvent for PinchEvent {
610 fn to_platform_input(self) -> PlatformInput {
611 PlatformInput::Pinch(self)
612 }
613}
614impl GestureEvent for PinchEvent {}
615impl MouseEvent for PinchEvent {}
616
617impl Deref for PinchEvent {
618 type Target = Modifiers;
619
620 fn deref(&self) -> &Self::Target {
621 &self.modifiers
622 }
623}
624
625impl ScrollDelta {
626 pub fn precise(&self) -> bool {
628 match self {
629 ScrollDelta::Pixels(_) => true,
630 ScrollDelta::Lines(_) => false,
631 }
632 }
633
634 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
636 match self {
637 ScrollDelta::Pixels(delta) => *delta,
638 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
639 }
640 }
641
642 pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
647 match (self, other) {
648 (ScrollDelta::Pixels(a), ScrollDelta::Pixels(b)) => {
649 let x = if a.x.signum() == b.x.signum() {
650 a.x + b.x
651 } else {
652 b.x
653 };
654
655 let y = if a.y.signum() == b.y.signum() {
656 a.y + b.y
657 } else {
658 b.y
659 };
660
661 ScrollDelta::Pixels(point(x, y))
662 }
663
664 (ScrollDelta::Lines(a), ScrollDelta::Lines(b)) => {
665 let x = if a.x.signum() == b.x.signum() {
666 a.x + b.x
667 } else {
668 b.x
669 };
670
671 let y = if a.y.signum() == b.y.signum() {
672 a.y + b.y
673 } else {
674 b.y
675 };
676
677 ScrollDelta::Lines(point(x, y))
678 }
679
680 _ => other,
681 }
682 }
683}
684
685#[derive(Clone, Debug, Default)]
687pub struct MouseExitEvent {
688 pub position: Point<Pixels>,
690 pub pressed_button: Option<MouseButton>,
692 pub modifiers: Modifiers,
694}
695
696impl Sealed for MouseExitEvent {}
697impl InputEvent for MouseExitEvent {
698 fn to_platform_input(self) -> PlatformInput {
699 PlatformInput::MouseExited(self)
700 }
701}
702
703impl MouseEvent for MouseExitEvent {}
704
705impl Deref for MouseExitEvent {
706 type Target = Modifiers;
707
708 fn deref(&self) -> &Self::Target {
709 &self.modifiers
710 }
711}
712
713#[derive(Debug, Clone, Default, Eq, PartialEq)]
715pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
716
717impl ExternalPaths {
718 pub fn paths(&self) -> &[PathBuf] {
720 &self.0
721 }
722}
723
724#[derive(Debug, Clone, Eq, PartialEq)]
727pub enum ExternalDragPayload {
728 Files(FileDragPaths),
730}
731
732#[derive(Debug, Clone, Default, Eq, PartialEq)]
735pub struct FileDragPaths(SmallVec<[(PathBuf, bool); 2]>);
736
737impl FileDragPaths {
738 pub fn new(entries: impl IntoIterator<Item = (PathBuf, bool)>) -> Self {
740 Self(entries.into_iter().collect())
741 }
742
743 pub fn entries(&self) -> &[(PathBuf, bool)] {
745 &self.0
746 }
747}
748
749impl Render for ExternalPaths {
750 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
751 Empty
753 }
754}
755
756#[derive(Debug, Clone)]
758pub enum FileDropEvent {
759 Entered {
761 position: Point<Pixels>,
763 paths: ExternalPaths,
765 },
766 Pending {
768 position: Point<Pixels>,
770 },
771 Submit {
773 position: Point<Pixels>,
775 },
776 Exited,
778 Ended,
780}
781
782impl Sealed for FileDropEvent {}
783impl InputEvent for FileDropEvent {
784 fn to_platform_input(self) -> PlatformInput {
785 PlatformInput::FileDrop(self)
786 }
787}
788impl MouseEvent for FileDropEvent {}
789
790#[derive(Clone, Debug)]
792pub enum PlatformInput {
793 KeyDown(KeyDownEvent),
795 KeyUp(KeyUpEvent),
797 ModifiersChanged(ModifiersChangedEvent),
799 MouseDown(MouseDownEvent),
801 MouseUp(MouseUpEvent),
803 MousePressure(MousePressureEvent),
805 MouseMove(MouseMoveEvent),
807 MouseExited(MouseExitEvent),
809 Scroll(ScrollEvent),
812 Pinch(PinchEvent),
814 LongPress(LongPressEvent),
816 TouchDrag(TouchDragEvent),
818 BackGesture(BackGestureEvent),
820 FileDrop(FileDropEvent),
822 Touch(TouchEvent),
824}
825
826impl PlatformInput {
827 pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
828 match self {
829 PlatformInput::KeyDown { .. } => None,
830 PlatformInput::KeyUp { .. } => None,
831 PlatformInput::ModifiersChanged { .. } => None,
832 PlatformInput::MouseDown(event) => Some(event),
833 PlatformInput::MouseUp(event) => Some(event),
834 PlatformInput::MouseMove(event) => Some(event),
835 PlatformInput::MousePressure(event) => Some(event),
836 PlatformInput::MouseExited(event) => Some(event),
837 PlatformInput::Scroll(event) => Some(event),
838 PlatformInput::Pinch(event) => Some(event),
839 PlatformInput::LongPress(event) => Some(event),
840 PlatformInput::TouchDrag(event) => Some(event),
841 PlatformInput::BackGesture(event) => Some(event),
842 PlatformInput::FileDrop(event) => Some(event),
843 PlatformInput::Touch(_) => None,
844 }
845 }
846
847 pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
848 match self {
849 PlatformInput::KeyDown(event) => Some(event),
850 PlatformInput::KeyUp(event) => Some(event),
851 PlatformInput::ModifiersChanged(event) => Some(event),
852 PlatformInput::MouseDown(_) => None,
853 PlatformInput::MouseUp(_) => None,
854 PlatformInput::MouseMove(_) => None,
855 PlatformInput::MousePressure(_) => None,
856 PlatformInput::MouseExited(_) => None,
857 PlatformInput::Scroll(_) => None,
858 PlatformInput::Pinch(_) => None,
859 PlatformInput::LongPress(_) => None,
860 PlatformInput::TouchDrag(_) => None,
861 PlatformInput::BackGesture(_) => None,
862 PlatformInput::FileDrop(_) => None,
863 PlatformInput::Touch(_) => None,
864 }
865 }
866
867 pub fn kind_name(&self) -> &'static str {
870 match self {
871 PlatformInput::KeyDown(_) => "key_down",
872 PlatformInput::KeyUp(_) => "key_up",
873 PlatformInput::ModifiersChanged(_) => "modifiers_changed",
874 PlatformInput::MouseDown(_) => "mouse_down",
875 PlatformInput::MouseUp(_) => "mouse_up",
876 PlatformInput::MousePressure(_) => "mouse_pressure",
877 PlatformInput::MouseMove(_) => "mouse_move",
878 PlatformInput::MouseExited(_) => "mouse_exited",
879 PlatformInput::Scroll(_) => "scroll",
880 PlatformInput::Pinch(_) => "pinch",
881 PlatformInput::LongPress(_) => "long_press",
882 PlatformInput::TouchDrag(_) => "touch_drag",
883 PlatformInput::BackGesture(_) => "back_gesture",
884 PlatformInput::FileDrop(_) => "file_drop",
885 PlatformInput::Touch(_) => "touch",
886 }
887 }
888
889 pub fn touch_event(&self) -> Option<&TouchEvent> {
891 match self {
892 PlatformInput::Touch(event) => Some(event),
893 _ => None,
894 }
895 }
896}
897
898#[cfg(test)]
899mod test {
900
901 use crate::{
902 self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
903 KeyBinding, Keystroke, Modifiers, ParentElement, Render, TestAppContext, Window, div,
904 };
905
906 struct TestView {
907 saw_key_down: bool,
908 saw_action: bool,
909 focus_handle: FocusHandle,
910 }
911
912 actions!(test_only, [TestAction]);
913
914 impl Render for TestView {
915 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
916 div().id("testview").child(
917 div()
918 .key_context("parent")
919 .on_key_down(cx.listener(|this, _, _, cx| {
920 cx.stop_propagation();
921 this.saw_key_down = true
922 }))
923 .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
924 this.saw_action = true
925 }))
926 .child(
927 div()
928 .key_context("nested")
929 .track_focus(&self.focus_handle)
930 .into_element(),
931 ),
932 )
933 }
934 }
935
936 #[gpui::test]
937 fn test_on_events(cx: &mut TestAppContext) {
938 let window = cx.update(|cx| {
939 cx.open_window(Default::default(), |_, cx| {
940 cx.new(|cx| TestView {
941 saw_key_down: false,
942 saw_action: false,
943 focus_handle: cx.focus_handle(),
944 })
945 })
946 .unwrap()
947 });
948
949 cx.update(|cx| {
950 cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
951 });
952
953 window
954 .update(cx, |test_view, window, cx| {
955 window.focus(&test_view.focus_handle, cx)
956 })
957 .unwrap();
958
959 cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
960 cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
961
962 window
963 .update(cx, |test_view, _, _| {
964 assert!(test_view.saw_key_down || test_view.saw_action);
965 assert!(test_view.saw_key_down);
966 assert!(test_view.saw_action);
967 })
968 .unwrap();
969 }
970
971 #[gpui::test]
972 fn test_multi_modifier_gesture_does_not_dispatch_standalone_modifier_binding(
973 cx: &mut TestAppContext,
974 ) {
975 let (test_view, cx) = cx.add_window_view(|_, cx| TestView {
976 saw_key_down: false,
977 saw_action: false,
978 focus_handle: cx.focus_handle(),
979 });
980
981 cx.update(|_, cx| {
982 cx.bind_keys(vec![KeyBinding::new("shift", TestAction, None)]);
983 });
984 test_view.update_in(cx, |test_view, window, cx| {
985 window.focus(&test_view.focus_handle, cx);
986 });
987
988 cx.simulate_modifiers_change(Modifiers::alt());
989 cx.simulate_modifiers_change(Modifiers::alt() | Modifiers::shift());
990 cx.simulate_modifiers_change(Modifiers::shift());
991 cx.simulate_modifiers_change(Modifiers::none());
992 assert!(!test_view.read_with(cx, |test_view, _| test_view.saw_action));
993
994 cx.simulate_modifiers_change(Modifiers::shift());
995 cx.simulate_modifiers_change(Modifiers::none());
996 assert!(test_view.read_with(cx, |test_view, _| test_view.saw_action));
997 }
998}