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
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 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#[derive(Clone, Debug, Default)]
139pub struct MouseDownEvent {
140 pub button: MouseButton,
142
143 pub position: Point<Pixels>,
145
146 pub modifiers: Modifiers,
148
149 pub click_count: usize,
151
152 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 pub fn is_focusing(&self) -> bool {
167 match self.button {
168 MouseButton::Left => true,
169 _ => false,
170 }
171 }
172}
173
174#[derive(Clone, Debug, Default)]
176pub struct MouseUpEvent {
177 pub button: MouseButton,
179
180 pub position: Point<Pixels>,
182
183 pub modifiers: Modifiers,
185
186 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 pub fn is_focusing(&self) -> bool {
202 match self.button {
203 MouseButton::Left => true,
204 _ => false,
205 }
206 }
207}
208
209#[derive(Clone, Debug, Default)]
211pub struct MouseClickEvent {
212 pub down: MouseDownEvent,
214
215 pub up: MouseUpEvent,
217}
218
219#[derive(Clone, Copy, Debug, Default, PartialEq)]
221pub enum PressureStage {
222 #[default]
224 Zero,
225 Normal,
227 Force,
229}
230
231#[derive(Debug, Clone, Default)]
234pub struct MousePressureEvent {
235 pub pressure: f32,
237 pub stage: PressureStage,
239 pub position: Point<Pixels>,
241 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#[derive(Clone, Debug, Default)]
255pub struct KeyboardClickEvent {
256 pub button: KeyboardButton,
258
259 pub bounds: Bounds<Pixels>,
261}
262
263#[derive(Clone, Debug, Default)]
266pub struct TouchClickEvent {
267 pub position: Point<Pixels>,
269 pub tap_count: usize,
272 pub long_press: bool,
276}
277
278#[derive(Clone, Debug)]
281pub enum ClickEvent {
282 Mouse(MouseClickEvent),
284 Keyboard(KeyboardClickEvent),
286 Touch(TouchClickEvent),
288}
289
290impl Default for ClickEvent {
291 fn default() -> Self {
292 ClickEvent::Keyboard(KeyboardClickEvent::default())
293 }
294}
295
296impl ClickEvent {
297 pub fn modifiers(&self) -> Modifiers {
302 match self {
303 ClickEvent::Keyboard(_) => Modifiers::default(),
305 ClickEvent::Mouse(event) => event.up.modifiers,
309 ClickEvent::Touch(_) => Modifiers::default(),
311 }
312 }
313
314 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 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 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 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 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 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 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 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 pub fn is_keyboard(&self) -> bool {
425 match self {
426 ClickEvent::Mouse(_) | ClickEvent::Touch(_) => false,
427 ClickEvent::Keyboard(_) => true,
428 }
429 }
430}
431
432#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, Default)]
434pub enum KeyboardButton {
435 #[default]
437 Enter,
438 Space,
440}
441
442#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
444pub enum MouseButton {
445 #[default]
447 Left,
448
449 Right,
451
452 Middle,
454
455 Navigate(NavigationDirection),
457}
458
459impl MouseButton {
460 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#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
474pub enum NavigationDirection {
475 #[default]
477 Back,
478
479 Forward,
481}
482
483#[derive(Clone, Debug, Default)]
485pub struct MouseMoveEvent {
486 pub position: Point<Pixels>,
488
489 pub pressed_button: Option<MouseButton>,
491
492 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 pub fn dragging(&self) -> bool {
507 self.pressed_button == Some(MouseButton::Left)
508 }
509}
510
511#[derive(Clone, Debug, Default)]
513pub struct ScrollWheelEvent {
514 pub position: Point<Pixels>,
516
517 pub delta: ScrollDelta,
519
520 pub modifiers: Modifiers,
522
523 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#[derive(Clone, Copy, Debug)]
545pub enum ScrollDelta {
546 Pixels(Point<Pixels>),
548 Lines(Point<f32>),
550}
551
552impl Default for ScrollDelta {
553 fn default() -> Self {
554 Self::Lines(Default::default())
555 }
556}
557
558#[derive(Clone, Debug, Default)]
562pub struct PinchEvent {
563 pub position: Point<Pixels>,
565
566 pub delta: f32,
570
571 pub modifiers: Modifiers,
573
574 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 pub fn precise(&self) -> bool {
598 match self {
599 ScrollDelta::Pixels(_) => true,
600 ScrollDelta::Lines(_) => false,
601 }
602 }
603
604 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 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#[derive(Clone, Debug, Default)]
657pub struct MouseExitEvent {
658 pub position: Point<Pixels>,
660 pub pressed_button: Option<MouseButton>,
662 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#[derive(Debug, Clone, Default, Eq, PartialEq)]
685pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
686
687impl ExternalPaths {
688 pub fn paths(&self) -> &[PathBuf] {
690 &self.0
691 }
692}
693
694#[derive(Debug, Clone, Eq, PartialEq)]
697pub enum ExternalDragPayload {
698 Files(FileDragPaths),
700}
701
702#[derive(Debug, Clone, Default, Eq, PartialEq)]
705pub struct FileDragPaths(SmallVec<[(PathBuf, bool); 2]>);
706
707impl FileDragPaths {
708 pub fn new(entries: impl IntoIterator<Item = (PathBuf, bool)>) -> Self {
710 Self(entries.into_iter().collect())
711 }
712
713 pub fn entries(&self) -> &[(PathBuf, bool)] {
715 &self.0
716 }
717}
718
719impl Render for ExternalPaths {
720 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
721 Empty
723 }
724}
725
726#[derive(Debug, Clone)]
728pub enum FileDropEvent {
729 Entered {
731 position: Point<Pixels>,
733 paths: ExternalPaths,
735 },
736 Pending {
738 position: Point<Pixels>,
740 },
741 Submit {
743 position: Point<Pixels>,
745 },
746 Exited,
748 Ended,
750}
751
752impl Sealed for FileDropEvent {}
753impl InputEvent for FileDropEvent {
754 fn to_platform_input(self) -> PlatformInput {
755 PlatformInput::FileDrop(self)
756 }
757}
758impl MouseEvent for FileDropEvent {}
759
760#[derive(Clone, Debug)]
762pub enum PlatformInput {
763 KeyDown(KeyDownEvent),
765 KeyUp(KeyUpEvent),
767 ModifiersChanged(ModifiersChangedEvent),
769 MouseDown(MouseDownEvent),
771 MouseUp(MouseUpEvent),
773 MousePressure(MousePressureEvent),
775 MouseMove(MouseMoveEvent),
777 MouseExited(MouseExitEvent),
779 ScrollWheel(ScrollWheelEvent),
781 Pinch(PinchEvent),
783 FileDrop(FileDropEvent),
785 Touch(TouchEvent),
787}
788
789impl PlatformInput {
790 pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
791 match self {
792 PlatformInput::KeyDown { .. } => None,
793 PlatformInput::KeyUp { .. } => None,
794 PlatformInput::ModifiersChanged { .. } => None,
795 PlatformInput::MouseDown(event) => Some(event),
796 PlatformInput::MouseUp(event) => Some(event),
797 PlatformInput::MouseMove(event) => Some(event),
798 PlatformInput::MousePressure(event) => Some(event),
799 PlatformInput::MouseExited(event) => Some(event),
800 PlatformInput::ScrollWheel(event) => Some(event),
801 PlatformInput::Pinch(event) => Some(event),
802 PlatformInput::FileDrop(event) => Some(event),
803 PlatformInput::Touch(_) => None,
804 }
805 }
806
807 pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
808 match self {
809 PlatformInput::KeyDown(event) => Some(event),
810 PlatformInput::KeyUp(event) => Some(event),
811 PlatformInput::ModifiersChanged(event) => Some(event),
812 PlatformInput::MouseDown(_) => None,
813 PlatformInput::MouseUp(_) => None,
814 PlatformInput::MouseMove(_) => None,
815 PlatformInput::MousePressure(_) => None,
816 PlatformInput::MouseExited(_) => None,
817 PlatformInput::ScrollWheel(_) => None,
818 PlatformInput::Pinch(_) => None,
819 PlatformInput::FileDrop(_) => None,
820 PlatformInput::Touch(_) => None,
821 }
822 }
823
824 pub fn touch_event(&self) -> Option<&TouchEvent> {
826 match self {
827 PlatformInput::Touch(event) => Some(event),
828 _ => None,
829 }
830 }
831}
832
833#[cfg(test)]
834mod test {
835
836 use crate::{
837 self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
838 KeyBinding, Keystroke, ParentElement, Render, TestAppContext, Window, div,
839 };
840
841 struct TestView {
842 saw_key_down: bool,
843 saw_action: bool,
844 focus_handle: FocusHandle,
845 }
846
847 actions!(test_only, [TestAction]);
848
849 impl Render for TestView {
850 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
851 div().id("testview").child(
852 div()
853 .key_context("parent")
854 .on_key_down(cx.listener(|this, _, _, cx| {
855 cx.stop_propagation();
856 this.saw_key_down = true
857 }))
858 .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
859 this.saw_action = true
860 }))
861 .child(
862 div()
863 .key_context("nested")
864 .track_focus(&self.focus_handle)
865 .into_element(),
866 ),
867 )
868 }
869 }
870
871 #[gpui::test]
872 fn test_on_events(cx: &mut TestAppContext) {
873 let window = cx.update(|cx| {
874 cx.open_window(Default::default(), |_, cx| {
875 cx.new(|cx| TestView {
876 saw_key_down: false,
877 saw_action: false,
878 focus_handle: cx.focus_handle(),
879 })
880 })
881 .unwrap()
882 });
883
884 cx.update(|cx| {
885 cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
886 });
887
888 window
889 .update(cx, |test_view, window, cx| {
890 window.focus(&test_view.focus_handle, cx)
891 })
892 .unwrap();
893
894 cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
895 cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
896
897 window
898 .update(cx, |test_view, _, _| {
899 assert!(test_view.saw_key_down || test_view.saw_action);
900 assert!(test_view.saw_key_down);
901 assert!(test_view.saw_action);
902 })
903 .unwrap();
904 }
905}