Skip to main content

gpui/
interactive.rs

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
8/// A platform input event that GPUI can dispatch to a window.
9///
10/// Implemented by the event structs in this module. Each one converts into the
11/// matching [`PlatformInput`] variant. The trait is sealed, so only GPUI can
12/// implement it.
13pub trait InputEvent: Sealed + 'static {
14    /// Converts this event into the [`PlatformInput`] variant that wraps it.
15    fn to_platform_input(self) -> PlatformInput;
16}
17
18/// An input event from the keyboard: [`KeyDownEvent`], [`KeyUpEvent`] or
19/// [`ModifiersChangedEvent`].
20pub trait KeyEvent: InputEvent {}
21
22/// An input event from a pointer or touch: mouse buttons, movement, scrolling,
23/// pinch, pressure, file drops and the gesture events.
24pub trait MouseEvent: InputEvent {}
25
26/// A gesture event recognized from touch input, such as [`PinchEvent`],
27/// [`LongPressEvent`] or [`TouchDragEvent`].
28pub trait GestureEvent: InputEvent {}
29
30/// A key press, sent when the platform reports that a key went down.
31///
32/// GPUI first matches the keystroke against your key bindings and dispatches
33/// matching actions. Unless an action stops propagation, the event then reaches
34/// [`InteractiveElement::on_key_down`](crate::InteractiveElement::on_key_down)
35/// listeners on the focus path. The [keyboard guide](crate::_documentation::guides::keyboard)
36/// explains the order.
37#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct KeyDownEvent {
39    /// The key and modifiers that were pressed.
40    pub keystroke: Keystroke,
41
42    /// Whether this is an automatic repeat from a key that is still held down.
43    pub is_held: bool,
44
45    /// Whether the focused text input should receive this keystroke as text
46    /// instead of matching key bindings.
47    ///
48    /// Set on a keystroke that GPUI replays after a multi-stroke binding fails to
49    /// complete. Bindings are skipped only if the focused input handler accepts
50    /// text input.
51    pub prefer_character_input: bool,
52}
53
54impl Sealed for KeyDownEvent {}
55impl InputEvent for KeyDownEvent {
56    fn to_platform_input(self) -> PlatformInput {
57        PlatformInput::KeyDown(self)
58    }
59}
60impl KeyEvent for KeyDownEvent {}
61
62/// A key release, sent when the platform reports that a key went up.
63///
64/// Dispatched to [`InteractiveElement::on_key_up`](crate::InteractiveElement::on_key_up) listeners on the focus path.
65#[derive(Clone, Debug)]
66pub struct KeyUpEvent {
67    /// The key and modifiers that were released.
68    pub keystroke: Keystroke,
69}
70
71impl Sealed for KeyUpEvent {}
72impl InputEvent for KeyUpEvent {
73    fn to_platform_input(self) -> PlatformInput {
74        PlatformInput::KeyUp(self)
75    }
76}
77impl KeyEvent for KeyUpEvent {}
78
79/// A change to the modifier keys (Shift, Control, Alt, Platform, Function) or
80/// Caps Lock, sent without a key press.
81///
82/// Dereferences to [`Modifiers`], so `event.shift` reads the new Shift state.
83/// Modifier-only bindings, such as one bound to `"shift"`, are matched from
84/// this event.
85#[derive(Clone, Debug, Default)]
86pub struct ModifiersChangedEvent {
87    /// The modifier keys held after the change.
88    pub modifiers: Modifiers,
89    /// The Caps Lock state after the change.
90    pub capslock: Capslock,
91}
92
93impl Sealed for ModifiersChangedEvent {}
94impl InputEvent for ModifiersChangedEvent {
95    fn to_platform_input(self) -> PlatformInput {
96        PlatformInput::ModifiersChanged(self)
97    }
98}
99impl KeyEvent for ModifiersChangedEvent {}
100
101impl Deref for ModifiersChangedEvent {
102    type Target = Modifiers;
103
104    fn deref(&self) -> &Self::Target {
105        &self.modifiers
106    }
107}
108
109/// The lifecycle stage of a touch, scroll or pinch event.
110///
111/// Based on the winit enum of the same name. A gesture reports `Started` when it
112/// begins, zero or more `Moved` events, and then `Ended` or `Cancelled`.
113#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
114pub enum TouchPhase {
115    /// The touch, scroll or pinch began.
116    Started,
117    /// The touch, scroll or pinch changed position or scale.
118    #[default]
119    Moved,
120    /// The touch, scroll or pinch finished normally.
121    Ended,
122    /// The system ended the gesture abnormally. Consumers must fully unwind any
123    /// in-progress interaction, treating the touch as if it never committed.
124    Cancelled,
125}
126
127/// Identifies one touch (finger or stylus contact) from its
128/// [`TouchPhase::Started`] event through its [`TouchPhase::Ended`] or
129/// [`TouchPhase::Cancelled`] event.
130///
131/// The value is assigned by the platform and is opaque. A platform window must
132/// not reuse an identifier for a later touch.
133#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
134pub struct TouchId(pub u64);
135
136/// A raw touch sample from a touch screen, before gesture recognition.
137///
138/// GPUI does not deliver raw touches to element listeners. The gesture
139/// recognizer reads them and reports what they mean: a tap becomes a
140/// synthesized left mouse down and up, a pan becomes [`ScrollWheelEvent`]s, a
141/// held touch becomes [`LongPressEvent`], and a drag claimed by an element
142/// becomes [`TouchDragEvent`]. See the
143/// [mouse and touch guide](crate::_documentation::guides::mouse_and_touch).
144#[derive(Clone, Debug, Default)]
145pub struct TouchEvent {
146    /// The touch this sample belongs to.
147    pub id: TouchId,
148    /// Whether the touch started, moved, ended or was cancelled.
149    pub phase: TouchPhase,
150    /// The touch position in window coordinates, in logical pixels.
151    pub position: Point<Pixels>,
152    /// The position the platform predicts the touch will reach about one frame
153    /// later, in the same coordinates as `position`. Only set for
154    /// [`TouchPhase::Moved`] samples, and only when the platform can predict.
155    ///
156    /// This is latency compensation. It can change how far a pan scrolls within
157    /// a frame. It never affects hit testing, gesture classification or
158    /// velocity, and later samples correct any error it introduces.
159    pub predicted_position: Option<Point<Pixels>>,
160    /// The normalized touch force from `0.0` to `1.0`, if the hardware reports it.
161    pub force: Option<f32>,
162}
163
164impl Sealed for TouchEvent {}
165impl InputEvent for TouchEvent {
166    fn to_platform_input(self) -> PlatformInput {
167        PlatformInput::Touch(self)
168    }
169}
170
171/// A mouse button press, sent to [`InteractiveElement::on_mouse_down`](crate::InteractiveElement::on_mouse_down) listeners.
172///
173/// Use it to react as soon as the button goes down. Use [`MouseClickEvent`]
174/// instead when you need the completed click.
175#[derive(Clone, Debug, Default)]
176pub struct MouseDownEvent {
177    /// The button that was pressed.
178    pub button: MouseButton,
179
180    /// The pointer position in window coordinates, in logical pixels.
181    pub position: Point<Pixels>,
182
183    /// The modifier keys held when the button was pressed.
184    pub modifiers: Modifiers,
185
186    /// The number of presses in a row at this spot, starting at 1. A double
187    /// click has a count of 2.
188    pub click_count: usize,
189
190    /// Whether this press activated an inactive window. Set on macOS and Wayland
191    /// when the window accepts the press that brings it forward.
192    pub first_mouse: bool,
193}
194
195impl Sealed for MouseDownEvent {}
196impl InputEvent for MouseDownEvent {
197    fn to_platform_input(self) -> PlatformInput {
198        PlatformInput::MouseDown(self)
199    }
200}
201impl MouseEvent for MouseDownEvent {}
202
203impl MouseDownEvent {
204    /// Returns `true` if this press should focus the element.
205    ///
206    /// Only left button presses focus. Right and middle presses leave focus where
207    /// it is.
208    pub fn is_focusing(&self) -> bool {
209        match self.button {
210            MouseButton::Left => true,
211            _ => false,
212        }
213    }
214}
215
216/// A mouse button release, sent to [`InteractiveElement::on_mouse_up`](crate::InteractiveElement::on_mouse_up) listeners.
217///
218/// A release alone is not a click. Use [`MouseClickEvent`] or
219/// [`StatefulInteractiveElement::on_click`](crate::StatefulInteractiveElement::on_click) for a completed click.
220#[derive(Clone, Debug, Default)]
221pub struct MouseUpEvent {
222    /// The button that was released.
223    pub button: MouseButton,
224
225    /// The pointer position in window coordinates, in logical pixels.
226    pub position: Point<Pixels>,
227
228    /// The modifier keys held when the button was released.
229    pub modifiers: Modifiers,
230
231    /// The number of presses in a row, as reported by the platform with this release.
232    pub click_count: usize,
233}
234
235impl Sealed for MouseUpEvent {}
236impl InputEvent for MouseUpEvent {
237    fn to_platform_input(self) -> PlatformInput {
238        PlatformInput::MouseUp(self)
239    }
240}
241
242impl MouseEvent for MouseUpEvent {}
243
244impl MouseUpEvent {
245    /// Returns `true` if this release should focus the element.
246    ///
247    /// Only left button releases focus.
248    pub fn is_focusing(&self) -> bool {
249        match self.button {
250            MouseButton::Left => true,
251            _ => false,
252        }
253    }
254}
255
256/// A completed mouse click: the press and the release of one button on one
257/// element.
258///
259/// GPUI sends it to [`StatefulInteractiveElement::on_click`](crate::StatefulInteractiveElement::on_click) for the left button and to
260/// [`StatefulInteractiveElement::on_aux_click`](crate::StatefulInteractiveElement::on_aux_click) for every other button. It is wrapped in
261/// [`ClickEvent::Mouse`].
262#[derive(Clone, Debug, Default)]
263pub struct MouseClickEvent {
264    /// The press that started the click.
265    pub down: MouseDownEvent,
266
267    /// The release that completed the click.
268    pub up: MouseUpEvent,
269}
270
271/// How hard a force-sensitive trackpad is pressed.
272#[derive(Clone, Copy, Debug, Default, PartialEq)]
273pub enum PressureStage {
274    /// No pressure.
275    #[default]
276    Zero,
277    /// Normal click pressure.
278    Normal,
279    /// Firm pressure, enough to trigger a force click.
280    Force,
281}
282
283/// A pressure update from a force-sensitive trackpad, sent as the user presses
284/// harder or eases off. Only macOS trackpads report it today.
285#[derive(Debug, Clone, Default)]
286pub struct MousePressureEvent {
287    /// The pressure within the current stage, from `0.0` to `1.0`.
288    pub pressure: f32,
289    /// The pressure stage the trackpad is in.
290    pub stage: PressureStage,
291    /// The pointer position in window coordinates, in logical pixels.
292    pub position: Point<Pixels>,
293    /// The modifier keys held when the pressure changed.
294    pub modifiers: Modifiers,
295}
296
297impl Sealed for MousePressureEvent {}
298impl InputEvent for MousePressureEvent {
299    fn to_platform_input(self) -> PlatformInput {
300        PlatformInput::MousePressure(self)
301    }
302}
303impl MouseEvent for MousePressureEvent {}
304
305/// A click triggered from the keyboard: Enter or Space on a focused element.
306///
307/// Keyboard clicks carry no pointer data. The element's bounds stand in for the
308/// position.
309#[derive(Clone, Debug, Default)]
310pub struct KeyboardClickEvent {
311    /// The key that triggered the click.
312    pub button: KeyboardButton,
313
314    /// The bounds of the clicked element in window coordinates, in logical pixels.
315    pub bounds: Bounds<Pixels>,
316}
317
318/// A tap, or a long press, recognized on a touch screen.
319///
320/// GPUI does not produce this event yet. Taps currently reach click listeners
321/// as [`ClickEvent::Mouse`] with a left button press and release.
322#[derive(Clone, Debug, Default)]
323pub struct TouchClickEvent {
324    /// The position of the tap in window coordinates, in logical pixels.
325    pub position: Point<Pixels>,
326    /// The number of consecutive taps at this spot. A double tap has a count of 2.
327    pub tap_count: usize,
328    /// Whether this was a long press rather than a tap. A long press is the touch
329    /// equivalent of a right click.
330    pub long_press: bool,
331}
332
333/// A completed click from the mouse, keyboard or touch.
334///
335/// Click listeners receive this type. [`StatefulInteractiveElement::on_click`](crate::StatefulInteractiveElement::on_click) handles
336/// the primary action, and [`StatefulInteractiveElement::on_aux_click`](crate::StatefulInteractiveElement::on_aux_click) handles the
337/// other mouse buttons. Use the methods on this type, such as
338/// [`ClickEvent::is_secondary`], to tell the cases apart.
339///
340/// # Examples
341///
342/// ```
343/// use gpui::{ClickEvent, KeyboardButton, KeyboardClickEvent, Modifiers, MouseButton,
344///     MouseClickEvent, MouseDownEvent, MouseUpEvent};
345///
346/// let right_click = ClickEvent::Mouse(MouseClickEvent {
347///     down: MouseDownEvent { button: MouseButton::Right, ..Default::default() },
348///     up: MouseUpEvent { button: MouseButton::Right, ..Default::default() },
349/// });
350/// assert!(right_click.is_right_click());
351/// assert!(right_click.is_secondary());
352/// assert!(!right_click.standard_click());
353///
354/// let enter = ClickEvent::Keyboard(KeyboardClickEvent {
355///     button: KeyboardButton::Enter,
356///     ..Default::default()
357/// });
358/// assert!(enter.is_keyboard());
359/// assert_eq!(enter.click_count(), 1);
360/// assert_eq!(enter.modifiers(), Modifiers::default());
361/// ```
362#[derive(Clone, Debug)]
363pub enum ClickEvent {
364    /// A click from a mouse button, a press and a release on the same element.
365    Mouse(MouseClickEvent),
366    /// A click from Enter or Space on a focused element.
367    Keyboard(KeyboardClickEvent),
368    /// A touch tap or long press. GPUI reserves this variant for direct touch
369    /// click delivery; the recognizer currently reports those gestures through
370    /// mouse events instead.
371    Touch(TouchClickEvent),
372}
373
374impl Default for ClickEvent {
375    fn default() -> Self {
376        ClickEvent::Keyboard(KeyboardClickEvent::default())
377    }
378}
379
380impl ClickEvent {
381    /// Returns the modifier keys held during the click.
382    ///
383    /// Keyboard and touch clicks have none, so this returns the default
384    /// [`Modifiers`]. A mouse click returns the modifiers held when the button was
385    /// released.
386    pub fn modifiers(&self) -> Modifiers {
387        match self {
388            // Click events are only generated from keyboard events _without any modifiers_, so we know the modifiers are always Default
389            ClickEvent::Keyboard(_) => Modifiers::default(),
390            // Click events on the web only reflect the modifiers for the keyup event,
391            // tested via observing the behavior of the `ClickEvent.shiftKey` field in Chrome 138
392            // under various combinations of modifiers and keyUp / keyDown events.
393            ClickEvent::Mouse(event) => event.up.modifiers,
394            // Touch screens have no modifier keys.
395            ClickEvent::Touch(_) => Modifiers::default(),
396        }
397    }
398
399    /// Returns the window position of the click, in logical pixels.
400    ///
401    /// For a mouse click this is where the button was released. For a touch it
402    /// is where the tap happened. For a keyboard click, which has no pointer,
403    /// it is the bottom-left corner of the clicked element.
404    pub fn position(&self) -> Point<Pixels> {
405        match self {
406            ClickEvent::Keyboard(event) => event.bounds.bottom_left(),
407            ClickEvent::Mouse(event) => event.up.position,
408            ClickEvent::Touch(event) => event.position,
409        }
410    }
411
412    /// Returns the mouse position of the click, or `None` if it did not come from
413    /// a mouse.
414    ///
415    /// Keyboard and touch clicks return `None`. Use [`ClickEvent::position`] when
416    /// you need a position for every kind.
417    pub fn mouse_position(&self) -> Option<Point<Pixels>> {
418        match self {
419            ClickEvent::Keyboard(_) => None,
420            ClickEvent::Mouse(event) => Some(event.up.position),
421            ClickEvent::Touch(_) => None,
422        }
423    }
424
425    /// Returns `true` if this is a mouse click made with the right button.
426    ///
427    /// Keyboard and touch clicks always return `false`. Use
428    /// [`ClickEvent::is_secondary`] to also match long presses.
429    pub fn is_right_click(&self) -> bool {
430        match self {
431            ClickEvent::Keyboard(_) => false,
432            ClickEvent::Mouse(event) => {
433                event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
434            }
435            ClickEvent::Touch(_) => false,
436        }
437    }
438
439    /// Returns `true` if this is a mouse click made with the middle button.
440    ///
441    /// Keyboard and touch clicks always return `false`.
442    pub fn is_middle_click(&self) -> bool {
443        match self {
444            ClickEvent::Keyboard(_) => false,
445            ClickEvent::Mouse(event) => {
446                event.down.button == MouseButton::Middle && event.up.button == MouseButton::Middle
447            }
448            ClickEvent::Touch(_) => false,
449        }
450    }
451
452    /// Returns `true` if this click is a secondary activation, the kind that
453    /// usually opens a context menu.
454    ///
455    /// That is a right-button mouse click, or a long press when a future direct
456    /// touch click is delivered. On macOS, Control-clicks already arrive as right
457    /// clicks. Keyboard clicks always return `false`.
458    pub fn is_secondary(&self) -> bool {
459        match self {
460            ClickEvent::Keyboard(_) => false,
461            ClickEvent::Mouse(event) => {
462                event.down.button == MouseButton::Right && event.up.button == MouseButton::Right
463            }
464            ClickEvent::Touch(event) => event.long_press,
465        }
466    }
467
468    /// Returns `true` for a primary activation: a left-button click, a keyboard
469    /// click, or a direct touch tap that is not a long press.
470    ///
471    /// Right, middle and navigation button clicks return `false`. Use
472    /// [`ClickEvent::is_secondary`] to test for the right button and long presses.
473    pub fn standard_click(&self) -> bool {
474        match self {
475            ClickEvent::Keyboard(_) => true,
476            ClickEvent::Mouse(event) => {
477                event.down.button == MouseButton::Left && event.up.button == MouseButton::Left
478            }
479            ClickEvent::Touch(event) => !event.long_press,
480        }
481    }
482
483    /// Returns `true` if this click activated the window, as reported by the
484    /// platform in [`MouseDownEvent::first_mouse`].
485    ///
486    /// Keyboard and touch clicks always return `false`. A keyboard click needs
487    /// the element to be focused already.
488    pub fn first_focus(&self) -> bool {
489        match self {
490            ClickEvent::Keyboard(_) => false,
491            ClickEvent::Mouse(event) => event.down.first_mouse,
492            ClickEvent::Touch(_) => false,
493        }
494    }
495
496    /// Returns how many clicks in a row this click completes. A double click is 2.
497    ///
498    /// A keyboard click always returns 1. A mouse click returns the count from its
499    /// release. A touch click returns its tap count.
500    pub fn click_count(&self) -> usize {
501        match self {
502            ClickEvent::Keyboard(_) => 1,
503            ClickEvent::Mouse(event) => event.up.click_count,
504            ClickEvent::Touch(event) => event.tap_count,
505        }
506    }
507
508    /// Returns `true` if this click came from the keyboard.
509    pub fn is_keyboard(&self) -> bool {
510        match self {
511            ClickEvent::Mouse(_) | ClickEvent::Touch(_) => false,
512            ClickEvent::Keyboard(_) => true,
513        }
514    }
515}
516
517/// The key that triggered a keyboard click: Enter or Space.
518#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, Default)]
519pub enum KeyboardButton {
520    /// The Enter key.
521    #[default]
522    Enter,
523    /// The Space bar.
524    Space,
525}
526
527/// A mouse button.
528///
529/// Compare buttons with `==` to choose a handler, as in
530/// `event.button == MouseButton::Right`. The default is [`MouseButton::Left`].
531#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
532pub enum MouseButton {
533    /// The primary button, usually on the left.
534    #[default]
535    Left,
536
537    /// The secondary button, usually on the right.
538    Right,
539
540    /// The middle button, usually the scroll wheel.
541    Middle,
542
543    /// A navigation button, such as back or forward on a side button.
544    Navigate(NavigationDirection),
545}
546
547impl MouseButton {
548    /// Returns every mouse button GPUI knows, in a fixed order.
549    ///
550    /// The order is `Left`, `Right`, `Middle`, `Navigate(Back)` and
551    /// `Navigate(Forward)`. Use it to iterate over all buttons, for example to
552    /// build a table of handlers.
553    ///
554    /// # Examples
555    ///
556    /// ```
557    /// use gpui::{MouseButton, NavigationDirection};
558    ///
559    /// assert_eq!(MouseButton::all().len(), 5);
560    /// assert_eq!(MouseButton::all()[3], MouseButton::Navigate(NavigationDirection::Back));
561    /// ```
562    pub fn all() -> Vec<Self> {
563        vec![
564            MouseButton::Left,
565            MouseButton::Right,
566            MouseButton::Middle,
567            MouseButton::Navigate(NavigationDirection::Back),
568            MouseButton::Navigate(NavigationDirection::Forward),
569        ]
570    }
571}
572
573/// The direction of a navigation button, the side buttons on some mice.
574#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)]
575pub enum NavigationDirection {
576    /// The back button.
577    #[default]
578    Back,
579
580    /// The forward button.
581    Forward,
582}
583
584/// The pointer moved over the window, with or without a button held.
585///
586/// Use [`MouseMoveEvent::dragging`] to tell a left-button drag from a hover.
587#[derive(Clone, Debug, Default)]
588pub struct MouseMoveEvent {
589    /// The pointer position in window coordinates, in logical pixels.
590    pub position: Point<Pixels>,
591
592    /// The button held down during the move, or `None` if no button is held.
593    pub pressed_button: Option<MouseButton>,
594
595    /// The modifier keys held during the move.
596    pub modifiers: Modifiers,
597}
598
599impl Sealed for MouseMoveEvent {}
600impl InputEvent for MouseMoveEvent {
601    fn to_platform_input(self) -> PlatformInput {
602        PlatformInput::MouseMove(self)
603    }
604}
605impl MouseEvent for MouseMoveEvent {}
606
607impl MouseMoveEvent {
608    /// Returns `true` if the left button is held down during this move.
609    ///
610    /// Other buttons, such as the right button, return `false`.
611    pub fn dragging(&self) -> bool {
612        self.pressed_button == Some(MouseButton::Left)
613    }
614}
615
616/// A scroll from a mouse wheel, a trackpad, or a touch pan.
617///
618/// Sent to [`InteractiveElement::on_scroll_wheel`](crate::InteractiveElement::on_scroll_wheel) listeners at the pointer
619/// position. Scroll containers use the `delta` to move their content.
620///
621/// Dereferences to [`Modifiers`], so `event.shift` reads the Shift state.
622#[derive(Clone, Debug, Default)]
623pub struct ScrollWheelEvent {
624    /// The pointer position in window coordinates, in logical pixels. For a
625    /// touch pan, the position where the pan started.
626    pub position: Point<Pixels>,
627
628    /// How far the content should scroll for this event.
629    pub delta: ScrollDelta,
630
631    /// The modifier keys held during the scroll.
632    pub modifiers: Modifiers,
633
634    /// Where this event falls in its scroll gesture. Trackpad and touch scrolls
635    /// report `Started`, `Moved` and `Ended`. Wheels that don't report phases
636    /// leave it at `Moved`.
637    pub touch_phase: TouchPhase,
638}
639
640impl Sealed for ScrollWheelEvent {}
641impl InputEvent for ScrollWheelEvent {
642    fn to_platform_input(self) -> PlatformInput {
643        PlatformInput::ScrollWheel(self)
644    }
645}
646impl MouseEvent for ScrollWheelEvent {}
647
648impl Deref for ScrollWheelEvent {
649    type Target = Modifiers;
650
651    fn deref(&self) -> &Self::Target {
652        &self.modifiers
653    }
654}
655
656/// The amount a scroll event moves the content.
657///
658/// Trackpads and touch pans report [`ScrollDelta::Pixels`]. Line-based mouse
659/// wheels report [`ScrollDelta::Lines`]. Use [`ScrollDelta::pixel_delta`] to get
660/// pixels for either.
661#[derive(Clone, Copy, Debug)]
662pub enum ScrollDelta {
663    /// An exact amount in logical pixels.
664    Pixels(Point<Pixels>),
665    /// An approximate amount in lines of text. Multiply by the line height to get
666    /// pixels.
667    Lines(Point<f32>),
668}
669
670impl Default for ScrollDelta {
671    fn default() -> Self {
672        Self::Lines(Default::default())
673    }
674}
675
676/// A pinch-to-zoom gesture, typically from a trackpad.
677///
678/// Sent to [`InteractiveElement::on_pinch`](crate::InteractiveElement::on_pinch) listeners. Pinch recognition from
679/// touch input is not implemented yet, so pinch events come from the platform.
680///
681/// Dereferences to [`Modifiers`].
682#[derive(Clone, Debug, Default)]
683pub struct PinchEvent {
684    /// The center of the pinch in window coordinates, in logical pixels.
685    pub position: Point<Pixels>,
686
687    /// The change in scale since the previous event. `0.1` is a 10% zoom in and
688    /// `-0.1` is a 10% zoom out.
689    pub delta: f32,
690
691    /// The modifier keys held during the pinch.
692    pub modifiers: Modifiers,
693
694    /// Where this event falls in the pinch gesture.
695    pub phase: TouchPhase,
696}
697
698impl Sealed for PinchEvent {}
699impl InputEvent for PinchEvent {
700    fn to_platform_input(self) -> PlatformInput {
701        PlatformInput::Pinch(self)
702    }
703}
704impl GestureEvent for PinchEvent {}
705impl MouseEvent for PinchEvent {}
706
707impl Deref for PinchEvent {
708    type Target = Modifiers;
709
710    fn deref(&self) -> &Self::Target {
711        &self.modifiers
712    }
713}
714
715impl ScrollDelta {
716    /// Returns `true` if this delta is exact, in pixels, rather than in lines.
717    pub fn precise(&self) -> bool {
718        match self {
719            ScrollDelta::Pixels(_) => true,
720            ScrollDelta::Lines(_) => false,
721        }
722    }
723
724    /// Returns this delta in logical pixels.
725    ///
726    /// A pixel delta is returned unchanged. A line delta is multiplied by
727    /// `line_height` on each axis, so pass the line height of the text you are
728    /// scrolling.
729    ///
730    /// # Examples
731    ///
732    /// ```
733    /// use gpui::{ScrollDelta, point, px};
734    ///
735    /// let lines = ScrollDelta::Lines(point(0., 2.));
736    /// assert_eq!(lines.pixel_delta(px(20.)), point(px(0.), px(40.)));
737    /// ```
738    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
739        match self {
740            ScrollDelta::Pixels(delta) => *delta,
741            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
742        }
743    }
744
745    /// Merges this delta with a later one, as when two scroll events arrive in
746    /// the same frame.
747    ///
748    /// Each axis is handled separately. If both deltas move that axis the same
749    /// way, the amounts add. If they move it in opposite directions, `other`
750    /// wins on that axis. Two deltas of different kinds, one in pixels and one
751    /// in lines, return `other` unchanged.
752    ///
753    /// A component of zero counts as positive, so a zero in `other` replaces a
754    /// negative component of `self`, and a zero in `self` is replaced by a
755    /// negative component of `other`.
756    ///
757    /// # Examples
758    ///
759    /// ```
760    /// use gpui::{ScrollDelta, point, px};
761    ///
762    /// // Same direction on x adds (10 + 5); opposite direction on y takes the later 3.
763    /// let merged = ScrollDelta::Pixels(point(px(10.), px(-5.)))
764    ///     .coalesce(ScrollDelta::Pixels(point(px(5.), px(3.))));
765    /// assert_eq!(merged.pixel_delta(px(16.)), point(px(15.), px(3.)));
766    /// ```
767    pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
768        match (self, other) {
769            (ScrollDelta::Pixels(a), ScrollDelta::Pixels(b)) => {
770                let x = if a.x.signum() == b.x.signum() {
771                    a.x + b.x
772                } else {
773                    b.x
774                };
775
776                let y = if a.y.signum() == b.y.signum() {
777                    a.y + b.y
778                } else {
779                    b.y
780                };
781
782                ScrollDelta::Pixels(point(x, y))
783            }
784
785            (ScrollDelta::Lines(a), ScrollDelta::Lines(b)) => {
786                let x = if a.x.signum() == b.x.signum() {
787                    a.x + b.x
788                } else {
789                    b.x
790                };
791
792                let y = if a.y.signum() == b.y.signum() {
793                    a.y + b.y
794                } else {
795                    b.y
796                };
797
798                ScrollDelta::Lines(point(x, y))
799            }
800
801            _ => other,
802        }
803    }
804}
805
806/// The pointer left the window.
807///
808/// Use it to end hover state. Dereferences to [`Modifiers`].
809#[derive(Clone, Debug, Default)]
810pub struct MouseExitEvent {
811    /// The last pointer position in window coordinates, in logical pixels.
812    pub position: Point<Pixels>,
813    /// The button held when the pointer left, or `None` if none was held.
814    pub pressed_button: Option<MouseButton>,
815    /// The modifier keys held when the pointer left.
816    pub modifiers: Modifiers,
817}
818
819impl Sealed for MouseExitEvent {}
820impl InputEvent for MouseExitEvent {
821    fn to_platform_input(self) -> PlatformInput {
822        PlatformInput::MouseExited(self)
823    }
824}
825
826impl MouseEvent for MouseExitEvent {}
827
828impl Deref for MouseExitEvent {
829    type Target = Modifiers;
830
831    fn deref(&self) -> &Self::Target {
832        &self.modifiers
833    }
834}
835
836/// The file paths carried by a file drop.
837///
838/// Wraps the paths the platform reports when files are dragged onto the window.
839#[derive(Debug, Clone, Default, Eq, PartialEq)]
840pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
841
842impl ExternalPaths {
843    /// Returns the dropped paths in the order the platform reported them.
844    pub fn paths(&self) -> &[PathBuf] {
845        &self.0
846    }
847}
848
849/// The content of a drag that leaves the window and becomes a native drag
850/// session handed to the operating system.
851#[derive(Debug, Clone, Eq, PartialEq)]
852pub enum ExternalDragPayload {
853    /// Files on disk, offered to the platform as an outbound file drag.
854    Files(FileDragPaths),
855}
856
857/// The files for a native file drag, each paired with whether it is a directory.
858///
859/// The caller supplies the directory flags up front, so the platform does not
860/// have to query the file system when the drag starts.
861#[derive(Debug, Clone, Default, Eq, PartialEq)]
862pub struct FileDragPaths(SmallVec<[(PathBuf, bool); 2]>);
863
864impl FileDragPaths {
865    /// Creates a drag payload from paths, each paired with whether it is a directory.
866    pub fn new(entries: impl IntoIterator<Item = (PathBuf, bool)>) -> Self {
867        Self(entries.into_iter().collect())
868    }
869
870    /// Returns the dragged paths, each paired with whether it is a directory.
871    pub fn entries(&self) -> &[(PathBuf, bool)] {
872        &self.0
873    }
874}
875
876impl Render for ExternalPaths {
877    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
878        // the platform will render icons for the dragged files
879        Empty
880    }
881}
882
883/// A stage of a file drag from the operating system into the window.
884///
885/// `Entered` starts a drag, `Pending` reports movement during it, `Submit` drops
886/// the files, and `Exited` ends a drag that did not drop. `Ended` reports that the
887/// platform's drag session has finished.
888#[derive(Debug, Clone)]
889pub enum FileDropEvent {
890    /// The drag entered the window.
891    Entered {
892        /// The pointer position in window coordinates, in logical pixels.
893        position: Point<Pixels>,
894        /// The paths of the files being dragged.
895        paths: ExternalPaths,
896    },
897    /// The drag moved over the window.
898    Pending {
899        /// The pointer position in window coordinates, in logical pixels.
900        position: Point<Pixels>,
901    },
902    /// The user dropped the files on the window.
903    Submit {
904        /// The pointer position in window coordinates, in logical pixels.
905        position: Point<Pixels>,
906    },
907    /// The drag left the window without a drop.
908    Exited,
909    /// The platform's drag session ended.
910    Ended,
911}
912
913impl Sealed for FileDropEvent {}
914impl InputEvent for FileDropEvent {
915    fn to_platform_input(self) -> PlatformInput {
916        PlatformInput::FileDrop(self)
917    }
918}
919impl MouseEvent for FileDropEvent {}
920
921/// Any input event the platform sends to a window.
922///
923/// Platform backends build this enum and hand it to the window, which routes
924/// each variant to the matching listeners. You rarely construct it yourself.
925/// Match on it to see the raw event, or call [`PlatformInput::kind_name`] for a
926/// label.
927#[derive(Clone, Debug)]
928pub enum PlatformInput {
929    /// A key was pressed. See [`KeyDownEvent`].
930    KeyDown(KeyDownEvent),
931    /// A key was released. See [`KeyUpEvent`].
932    KeyUp(KeyUpEvent),
933    /// The modifier keys or Caps Lock changed. See [`ModifiersChangedEvent`].
934    ModifiersChanged(ModifiersChangedEvent),
935    /// A mouse button was pressed. See [`MouseDownEvent`].
936    MouseDown(MouseDownEvent),
937    /// A mouse button was released. See [`MouseUpEvent`].
938    MouseUp(MouseUpEvent),
939    /// A force-sensitive trackpad changed pressure. See [`MousePressureEvent`].
940    MousePressure(MousePressureEvent),
941    /// The pointer moved. See [`MouseMoveEvent`].
942    MouseMove(MouseMoveEvent),
943    /// The pointer left the window. See [`MouseExitEvent`].
944    MouseExited(MouseExitEvent),
945    /// A scroll from a wheel, trackpad or touch pan. See [`ScrollWheelEvent`].
946    ScrollWheel(ScrollWheelEvent),
947    /// A pinch-to-zoom gesture. See [`PinchEvent`].
948    Pinch(PinchEvent),
949    /// A long press recognized from touch input. See [`LongPressEvent`].
950    LongPress(LongPressEvent),
951    /// A touch drag claimed by an element. See [`TouchDragEvent`].
952    TouchDrag(TouchDragEvent),
953    /// Files were dragged onto, or dropped on, the window. See [`FileDropEvent`].
954    FileDrop(FileDropEvent),
955    /// A raw sample from a touch screen, before gesture recognition. See [`TouchEvent`].
956    Touch(TouchEvent),
957}
958
959impl PlatformInput {
960    pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
961        match self {
962            PlatformInput::KeyDown { .. } => None,
963            PlatformInput::KeyUp { .. } => None,
964            PlatformInput::ModifiersChanged { .. } => None,
965            PlatformInput::MouseDown(event) => Some(event),
966            PlatformInput::MouseUp(event) => Some(event),
967            PlatformInput::MouseMove(event) => Some(event),
968            PlatformInput::MousePressure(event) => Some(event),
969            PlatformInput::MouseExited(event) => Some(event),
970            PlatformInput::ScrollWheel(event) => Some(event),
971            PlatformInput::Pinch(event) => Some(event),
972            PlatformInput::LongPress(event) => Some(event),
973            PlatformInput::TouchDrag(event) => Some(event),
974            PlatformInput::FileDrop(event) => Some(event),
975            PlatformInput::Touch(_) => None,
976        }
977    }
978
979    pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
980        match self {
981            PlatformInput::KeyDown(event) => Some(event),
982            PlatformInput::KeyUp(event) => Some(event),
983            PlatformInput::ModifiersChanged(event) => Some(event),
984            PlatformInput::MouseDown(_) => None,
985            PlatformInput::MouseUp(_) => None,
986            PlatformInput::MouseMove(_) => None,
987            PlatformInput::MousePressure(_) => None,
988            PlatformInput::MouseExited(_) => None,
989            PlatformInput::ScrollWheel(_) => None,
990            PlatformInput::Pinch(_) => None,
991            PlatformInput::LongPress(_) => None,
992            PlatformInput::TouchDrag(_) => None,
993            PlatformInput::FileDrop(_) => None,
994            PlatformInput::Touch(_) => None,
995        }
996    }
997
998    /// Returns a short, fixed name for this variant, such as `"mouse_down"`.
999    ///
1000    /// Use it in logs and telemetry. The names are stable snake_case strings.
1001    pub fn kind_name(&self) -> &'static str {
1002        match self {
1003            PlatformInput::KeyDown(_) => "key_down",
1004            PlatformInput::KeyUp(_) => "key_up",
1005            PlatformInput::ModifiersChanged(_) => "modifiers_changed",
1006            PlatformInput::MouseDown(_) => "mouse_down",
1007            PlatformInput::MouseUp(_) => "mouse_up",
1008            PlatformInput::MousePressure(_) => "mouse_pressure",
1009            PlatformInput::MouseMove(_) => "mouse_move",
1010            PlatformInput::MouseExited(_) => "mouse_exited",
1011            PlatformInput::ScrollWheel(_) => "scroll_wheel",
1012            PlatformInput::Pinch(_) => "pinch",
1013            PlatformInput::LongPress(_) => "long_press",
1014            PlatformInput::TouchDrag(_) => "touch_drag",
1015            PlatformInput::FileDrop(_) => "file_drop",
1016            PlatformInput::Touch(_) => "touch",
1017        }
1018    }
1019
1020    /// Returns the raw [`TouchEvent`] if this input is [`PlatformInput::Touch`],
1021    /// or `None` for every other variant.
1022    pub fn touch_event(&self) -> Option<&TouchEvent> {
1023        match self {
1024            PlatformInput::Touch(event) => Some(event),
1025            _ => None,
1026        }
1027    }
1028}
1029
1030#[cfg(test)]
1031mod test {
1032
1033    use crate::{
1034        self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
1035        KeyBinding, Keystroke, Modifiers, ParentElement, Render, TestAppContext, Window, div,
1036    };
1037
1038    struct TestView {
1039        saw_key_down: bool,
1040        saw_action: bool,
1041        focus_handle: FocusHandle,
1042    }
1043
1044    actions!(test_only, [TestAction]);
1045
1046    impl Render for TestView {
1047        fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
1048            div().id("testview").child(
1049                div()
1050                    .key_context("parent")
1051                    .on_key_down(cx.listener(|this, _, _, cx| {
1052                        cx.stop_propagation();
1053                        this.saw_key_down = true
1054                    }))
1055                    .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| {
1056                        this.saw_action = true
1057                    }))
1058                    .child(
1059                        div()
1060                            .key_context("nested")
1061                            .track_focus(&self.focus_handle)
1062                            .into_element(),
1063                    ),
1064            )
1065        }
1066    }
1067
1068    #[gpui::test]
1069    fn test_on_events(cx: &mut TestAppContext) {
1070        let window = cx.update(|cx| {
1071            cx.open_window(Default::default(), |_, cx| {
1072                cx.new(|cx| TestView {
1073                    saw_key_down: false,
1074                    saw_action: false,
1075                    focus_handle: cx.focus_handle(),
1076                })
1077            })
1078            .unwrap()
1079        });
1080
1081        cx.update(|cx| {
1082            cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]);
1083        });
1084
1085        window
1086            .update(cx, |test_view, window, cx| {
1087                window.focus(&test_view.focus_handle, cx)
1088            })
1089            .unwrap();
1090
1091        cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap());
1092        cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap());
1093
1094        window
1095            .update(cx, |test_view, _, _| {
1096                assert!(test_view.saw_key_down || test_view.saw_action);
1097                assert!(test_view.saw_key_down);
1098                assert!(test_view.saw_action);
1099            })
1100            .unwrap();
1101    }
1102
1103    #[gpui::test]
1104    fn test_multi_modifier_gesture_does_not_dispatch_standalone_modifier_binding(
1105        cx: &mut TestAppContext,
1106    ) {
1107        let (test_view, cx) = cx.add_window_view(|_, cx| TestView {
1108            saw_key_down: false,
1109            saw_action: false,
1110            focus_handle: cx.focus_handle(),
1111        });
1112
1113        cx.update(|_, cx| {
1114            cx.bind_keys(vec![KeyBinding::new("shift", TestAction, None)]);
1115        });
1116        test_view.update_in(cx, |test_view, window, cx| {
1117            window.focus(&test_view.focus_handle, cx);
1118        });
1119
1120        cx.simulate_modifiers_change(Modifiers::alt());
1121        cx.simulate_modifiers_change(Modifiers::alt() | Modifiers::shift());
1122        cx.simulate_modifiers_change(Modifiers::shift());
1123        cx.simulate_modifiers_change(Modifiers::none());
1124        assert!(!test_view.read_with(cx, |test_view, _| test_view.saw_action));
1125
1126        cx.simulate_modifiers_change(Modifiers::shift());
1127        cx.simulate_modifiers_change(Modifiers::none());
1128        assert!(test_view.read_with(cx, |test_view, _| test_view.saw_action));
1129    }
1130}