Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/elements/div.rs |  30 +-
crates/gpui3/src/elements/img.rs |   8 
crates/gpui3/src/elements/svg.rs |   8 
crates/gpui3/src/events.rs       | 291 -----------------------------
crates/gpui3/src/gpui3.rs        |   2 
crates/gpui3/src/interactive.rs  | 338 +++++++++++++++++++++++++++++++--
6 files changed, 335 insertions(+), 342 deletions(-)

Detailed changes

crates/gpui3/src/elements/div.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
-    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusHandle,
-    FocusListeners, Focusable, GlobalElementId, Hover, Identified, Interactive, IntoAnyElement,
+    ElementFocusability, ElementId, ElementIdentity, Focus, FocusHandle, FocusListeners, Focusable,
+    GlobalElementId, Hover, Identified, Interactive, InteractiveState, IntoAnyElement,
     KeyDownEvent, KeyMatch, LayoutId, MouseClickEvent, MouseDownEvent, MouseMoveEvent,
     MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point, ScrollWheelEvent,
     SharedString, Style, StyleRefinement, Styled, ViewContext,
@@ -78,7 +78,7 @@ pub struct Div<
     focus_style: StyleRefinement,
     focus_in_style: StyleRefinement,
     in_focus_style: StyleRefinement,
-    listeners: EventListeners<V>,
+    interactive_state: InteractiveState<V>,
 }
 
 pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
@@ -98,7 +98,7 @@ where
         focus_style: StyleRefinement::default(),
         focus_in_style: StyleRefinement::default(),
         in_focus_style: StyleRefinement::default(),
-        listeners: EventListeners::default(),
+        interactive_state: InteractiveState::default(),
     }
 }
 
@@ -126,7 +126,7 @@ where
             focus_style: self.focus_style,
             focus_in_style: self.focus_in_style,
             in_focus_style: self.in_focus_style,
-            listeners: self.listeners,
+            interactive_state: self.interactive_state,
         }
     }
 }
@@ -296,7 +296,7 @@ where
         pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
         cx: &mut ViewContext<V>,
     ) {
-        let click_listeners = mem::take(&mut self.listeners.mouse_click);
+        let click_listeners = mem::take(&mut self.interactive_state.mouse_click);
 
         let mouse_down = pending_click.lock().clone();
         if let Some(mouse_down) = mouse_down {
@@ -333,25 +333,25 @@ where
             })
         }
 
-        for listener in mem::take(&mut self.listeners.mouse_down) {
+        for listener in mem::take(&mut self.interactive_state.mouse_down) {
             cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in mem::take(&mut self.listeners.mouse_up) {
+        for listener in mem::take(&mut self.interactive_state.mouse_up) {
             cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in mem::take(&mut self.listeners.mouse_move) {
+        for listener in mem::take(&mut self.interactive_state.mouse_move) {
             cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in mem::take(&mut self.listeners.scroll_wheel) {
+        for listener in mem::take(&mut self.interactive_state.scroll_wheel) {
             cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
@@ -378,7 +378,7 @@ where
             focus_style: self.focus_style,
             focus_in_style: self.focus_in_style,
             in_focus_style: self.in_focus_style,
-            listeners: self.listeners,
+            interactive_state: self.interactive_state,
         }
     }
 }
@@ -431,7 +431,7 @@ where
         self.with_element_id(cx, |this, global_id, cx| {
             let element_state = element_state.unwrap_or_default();
 
-            let mut key_listeners = mem::take(&mut this.listeners.key);
+            let mut key_listeners = mem::take(&mut this.interactive_state.key);
             if let Some(global_id) = global_id {
                 key_listeners.push((
                     TypeId::of::<KeyDownEvent>(),
@@ -457,7 +457,7 @@ where
                     }
                 });
             });
-            this.listeners.key = key_listeners;
+            this.interactive_state.key = key_listeners;
 
             element_state
         })
@@ -584,8 +584,8 @@ where
     F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
-    fn listeners(&mut self) -> &mut EventListeners<V> {
-        &mut self.listeners
+    fn interactive_state(&mut self) -> &mut InteractiveState<V> {
+        &mut self.interactive_state
     }
 }
 

crates/gpui3/src/elements/img.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
-    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusListeners,
-    Focusable, Hover, Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels,
+    ElementFocusability, ElementId, ElementIdentity, Focus, FocusListeners, Focusable, Hover,
+    Identified, Interactive, InteractiveState, IntoAnyElement, LayoutId, NonFocusable, Pixels,
     SharedString, StyleRefinement, Styled, ViewContext,
 };
 use futures::FutureExt;
@@ -156,8 +156,8 @@ where
     I: ElementIdentity,
     F: ElementFocusability<V>,
 {
-    fn listeners(&mut self) -> &mut EventListeners<V> {
-        self.base.listeners()
+    fn interactive_state(&mut self) -> &mut InteractiveState<V> {
+        self.base.interactive_state()
     }
 }
 

crates/gpui3/src/elements/svg.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     div, Active, Anonymous, AnyElement, Bounds, Click, Div, DivState, Element, ElementFocusability,
-    ElementId, ElementIdentity, EventListeners, Focus, FocusListeners, Focusable, Hover,
-    Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
+    ElementId, ElementIdentity, Focus, FocusListeners, Focusable, Hover, Identified, Interactive,
+    InteractiveState, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
     StyleRefinement, Styled, ViewContext,
 };
 use util::ResultExt;
@@ -130,8 +130,8 @@ where
     I: ElementIdentity,
     F: ElementFocusability<V>,
 {
-    fn listeners(&mut self) -> &mut EventListeners<V> {
-        self.base.listeners()
+    fn interactive_state(&mut self) -> &mut InteractiveState<V> {
+        self.base.interactive_state()
     }
 }
 

crates/gpui3/src/events.rs 🔗

@@ -1,291 +0,0 @@
-use crate::{
-    point, Action, Bounds, DispatchContext, DispatchPhase, FocusHandle, Keystroke, Modifiers,
-    Pixels, Point, ViewContext,
-};
-use smallvec::SmallVec;
-use std::{
-    any::{Any, TypeId},
-    ops::Deref,
-    sync::Arc,
-};
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct KeyDownEvent {
-    pub keystroke: Keystroke,
-    pub is_held: bool,
-}
-
-#[derive(Clone, Debug)]
-pub struct KeyUpEvent {
-    pub keystroke: Keystroke,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct ModifiersChangedEvent {
-    pub modifiers: Modifiers,
-}
-
-impl Deref for ModifiersChangedEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-/// The phase of a touch motion event.
-/// Based on the winit enum of the same name.
-#[derive(Clone, Copy, Debug)]
-pub enum TouchPhase {
-    Started,
-    Moved,
-    Ended,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseDownEvent {
-    pub button: MouseButton,
-    pub position: Point<Pixels>,
-    pub modifiers: Modifiers,
-    pub click_count: usize,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseUpEvent {
-    pub button: MouseButton,
-    pub position: Point<Pixels>,
-    pub modifiers: Modifiers,
-    pub click_count: usize,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseClickEvent {
-    pub down: MouseDownEvent,
-    pub up: MouseUpEvent,
-}
-
-#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
-pub enum MouseButton {
-    Left,
-    Right,
-    Middle,
-    Navigate(NavigationDirection),
-}
-
-impl MouseButton {
-    pub fn all() -> Vec<Self> {
-        vec![
-            MouseButton::Left,
-            MouseButton::Right,
-            MouseButton::Middle,
-            MouseButton::Navigate(NavigationDirection::Back),
-            MouseButton::Navigate(NavigationDirection::Forward),
-        ]
-    }
-}
-
-impl Default for MouseButton {
-    fn default() -> Self {
-        Self::Left
-    }
-}
-
-#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
-pub enum NavigationDirection {
-    Back,
-    Forward,
-}
-
-impl Default for NavigationDirection {
-    fn default() -> Self {
-        Self::Back
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseMoveEvent {
-    pub position: Point<Pixels>,
-    pub pressed_button: Option<MouseButton>,
-    pub modifiers: Modifiers,
-}
-
-#[derive(Clone, Debug)]
-pub struct ScrollWheelEvent {
-    pub position: Point<Pixels>,
-    pub delta: ScrollDelta,
-    pub modifiers: Modifiers,
-    pub touch_phase: TouchPhase,
-}
-
-impl Deref for ScrollWheelEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum ScrollDelta {
-    Pixels(Point<Pixels>),
-    Lines(Point<f32>),
-}
-
-impl Default for ScrollDelta {
-    fn default() -> Self {
-        Self::Lines(Default::default())
-    }
-}
-
-impl ScrollDelta {
-    pub fn precise(&self) -> bool {
-        match self {
-            ScrollDelta::Pixels(_) => true,
-            ScrollDelta::Lines(_) => false,
-        }
-    }
-
-    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
-        match self {
-            ScrollDelta::Pixels(delta) => *delta,
-            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
-        }
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseExitEvent {
-    pub position: Point<Pixels>,
-    pub pressed_button: Option<MouseButton>,
-    pub modifiers: Modifiers,
-}
-
-impl Deref for MouseExitEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum InputEvent {
-    KeyDown(KeyDownEvent),
-    KeyUp(KeyUpEvent),
-    ModifiersChanged(ModifiersChangedEvent),
-    MouseDown(MouseDownEvent),
-    MouseUp(MouseUpEvent),
-    MouseMoved(MouseMoveEvent),
-    MouseExited(MouseExitEvent),
-    ScrollWheel(ScrollWheelEvent),
-}
-
-impl InputEvent {
-    pub fn position(&self) -> Option<Point<Pixels>> {
-        match self {
-            InputEvent::KeyDown { .. } => None,
-            InputEvent::KeyUp { .. } => None,
-            InputEvent::ModifiersChanged { .. } => None,
-            InputEvent::MouseDown(event) => Some(event.position),
-            InputEvent::MouseUp(event) => Some(event.position),
-            InputEvent::MouseMoved(event) => Some(event.position),
-            InputEvent::MouseExited(event) => Some(event.position),
-            InputEvent::ScrollWheel(event) => Some(event.position),
-        }
-    }
-
-    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
-        match self {
-            InputEvent::KeyDown { .. } => None,
-            InputEvent::KeyUp { .. } => None,
-            InputEvent::ModifiersChanged { .. } => None,
-            InputEvent::MouseDown(event) => Some(event),
-            InputEvent::MouseUp(event) => Some(event),
-            InputEvent::MouseMoved(event) => Some(event),
-            InputEvent::MouseExited(event) => Some(event),
-            InputEvent::ScrollWheel(event) => Some(event),
-        }
-    }
-
-    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
-        match self {
-            InputEvent::KeyDown(event) => Some(event),
-            InputEvent::KeyUp(event) => Some(event),
-            InputEvent::ModifiersChanged(event) => Some(event),
-            InputEvent::MouseDown(_) => None,
-            InputEvent::MouseUp(_) => None,
-            InputEvent::MouseMoved(_) => None,
-            InputEvent::MouseExited(_) => None,
-            InputEvent::ScrollWheel(_) => None,
-        }
-    }
-}
-
-pub struct FocusEvent {
-    pub blurred: Option<FocusHandle>,
-    pub focused: Option<FocusHandle>,
-}
-
-pub type MouseDownListener<V> = Arc<
-    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-pub type MouseUpListener<V> = Arc<
-    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-pub type MouseClickListener<V> =
-    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
-
-pub type MouseMoveListener<V> = Arc<
-    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub type ScrollWheelListener<V> = Arc<
-    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub type KeyListener<V> = Arc<
-    dyn Fn(
-            &mut V,
-            &dyn Any,
-            &[&DispatchContext],
-            DispatchPhase,
-            &mut ViewContext<V>,
-        ) -> Option<Box<dyn Action>>
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub struct EventListeners<V: 'static> {
-    pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
-    pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
-    pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
-    pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
-    pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
-    pub key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
-}
-
-impl<V> Default for EventListeners<V> {
-    fn default() -> Self {
-        Self {
-            mouse_down: SmallVec::new(),
-            mouse_up: SmallVec::new(),
-            mouse_click: SmallVec::new(),
-            mouse_move: SmallVec::new(),
-            scroll_wheel: SmallVec::new(),
-            key: SmallVec::new(),
-        }
-    }
-}

crates/gpui3/src/gpui3.rs 🔗

@@ -5,7 +5,6 @@ mod assets;
 mod color;
 mod element;
 mod elements;
-mod events;
 mod executor;
 mod focus;
 mod geometry;
@@ -33,7 +32,6 @@ pub use assets::*;
 pub use color::*;
 pub use element::*;
 pub use elements::*;
-pub use events::*;
 pub use executor::*;
 pub use focus::*;
 pub use geometry::*;

crates/gpui3/src/interactive.rs 🔗

@@ -1,12 +1,17 @@
-use std::{any::TypeId, sync::Arc};
+use smallvec::SmallVec;
 
 use crate::{
-    DispatchPhase, Element, EventListeners, KeyDownEvent, KeyUpEvent, MouseButton, MouseClickEvent,
-    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext,
+    point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke,
+    Modifiers, Pixels, Point, ViewContext,
+};
+use std::{
+    any::{Any, TypeId},
+    ops::Deref,
+    sync::Arc,
 };
 
 pub trait Interactive: Element {
-    fn listeners(&mut self) -> &mut EventListeners<Self::ViewState>;
+    fn interactive_state(&mut self) -> &mut InteractiveState<Self::ViewState>;
 
     fn on_mouse_down(
         mut self,
@@ -19,16 +24,16 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .mouse_down
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().mouse_down.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == button
                     && bounds.contains_point(&event.position)
                 {
                     handler(view, event, cx)
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -43,7 +48,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
+        self.interactive_state()
             .mouse_up
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
@@ -67,16 +72,16 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .mouse_down
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().mouse_down.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
                     && !bounds.contains_point(&event.position)
                 {
                     handler(view, event, cx)
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -91,7 +96,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
+        self.interactive_state()
             .mouse_up
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
@@ -114,13 +119,13 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .mouse_move
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().mouse_move.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -134,13 +139,13 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .scroll_wheel
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().scroll_wheel.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -158,7 +163,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners().key.push((
+        self.interactive_state().key.push((
             TypeId::of::<KeyDownEvent>(),
             Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
@@ -179,7 +184,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners().key.push((
+        self.interactive_state().key.push((
             TypeId::of::<KeyUpEvent>(),
             Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
@@ -200,7 +205,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners().key.push((
+        self.interactive_state().key.push((
             TypeId::of::<A>(),
             Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
@@ -223,9 +228,290 @@ pub trait Click: Interactive {
     where
         Self: Sized,
     {
-        self.listeners()
+        self.interactive_state()
             .mouse_click
             .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
         self
     }
 }
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct KeyDownEvent {
+    pub keystroke: Keystroke,
+    pub is_held: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct KeyUpEvent {
+    pub keystroke: Keystroke,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct ModifiersChangedEvent {
+    pub modifiers: Modifiers,
+}
+
+impl Deref for ModifiersChangedEvent {
+    type Target = Modifiers;
+
+    fn deref(&self) -> &Self::Target {
+        &self.modifiers
+    }
+}
+
+/// The phase of a touch motion event.
+/// Based on the winit enum of the same name.
+#[derive(Clone, Copy, Debug)]
+pub enum TouchPhase {
+    Started,
+    Moved,
+    Ended,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseDownEvent {
+    pub button: MouseButton,
+    pub position: Point<Pixels>,
+    pub modifiers: Modifiers,
+    pub click_count: usize,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseUpEvent {
+    pub button: MouseButton,
+    pub position: Point<Pixels>,
+    pub modifiers: Modifiers,
+    pub click_count: usize,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseClickEvent {
+    pub down: MouseDownEvent,
+    pub up: MouseUpEvent,
+}
+
+#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
+pub enum MouseButton {
+    Left,
+    Right,
+    Middle,
+    Navigate(NavigationDirection),
+}
+
+impl MouseButton {
+    pub fn all() -> Vec<Self> {
+        vec![
+            MouseButton::Left,
+            MouseButton::Right,
+            MouseButton::Middle,
+            MouseButton::Navigate(NavigationDirection::Back),
+            MouseButton::Navigate(NavigationDirection::Forward),
+        ]
+    }
+}
+
+impl Default for MouseButton {
+    fn default() -> Self {
+        Self::Left
+    }
+}
+
+#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
+pub enum NavigationDirection {
+    Back,
+    Forward,
+}
+
+impl Default for NavigationDirection {
+    fn default() -> Self {
+        Self::Back
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseMoveEvent {
+    pub position: Point<Pixels>,
+    pub pressed_button: Option<MouseButton>,
+    pub modifiers: Modifiers,
+}
+
+#[derive(Clone, Debug)]
+pub struct ScrollWheelEvent {
+    pub position: Point<Pixels>,
+    pub delta: ScrollDelta,
+    pub modifiers: Modifiers,
+    pub touch_phase: TouchPhase,
+}
+
+impl Deref for ScrollWheelEvent {
+    type Target = Modifiers;
+
+    fn deref(&self) -> &Self::Target {
+        &self.modifiers
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum ScrollDelta {
+    Pixels(Point<Pixels>),
+    Lines(Point<f32>),
+}
+
+impl Default for ScrollDelta {
+    fn default() -> Self {
+        Self::Lines(Default::default())
+    }
+}
+
+impl ScrollDelta {
+    pub fn precise(&self) -> bool {
+        match self {
+            ScrollDelta::Pixels(_) => true,
+            ScrollDelta::Lines(_) => false,
+        }
+    }
+
+    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
+        match self {
+            ScrollDelta::Pixels(delta) => *delta,
+            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseExitEvent {
+    pub position: Point<Pixels>,
+    pub pressed_button: Option<MouseButton>,
+    pub modifiers: Modifiers,
+}
+
+impl Deref for MouseExitEvent {
+    type Target = Modifiers;
+
+    fn deref(&self) -> &Self::Target {
+        &self.modifiers
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum InputEvent {
+    KeyDown(KeyDownEvent),
+    KeyUp(KeyUpEvent),
+    ModifiersChanged(ModifiersChangedEvent),
+    MouseDown(MouseDownEvent),
+    MouseUp(MouseUpEvent),
+    MouseMoved(MouseMoveEvent),
+    MouseExited(MouseExitEvent),
+    ScrollWheel(ScrollWheelEvent),
+}
+
+impl InputEvent {
+    pub fn position(&self) -> Option<Point<Pixels>> {
+        match self {
+            InputEvent::KeyDown { .. } => None,
+            InputEvent::KeyUp { .. } => None,
+            InputEvent::ModifiersChanged { .. } => None,
+            InputEvent::MouseDown(event) => Some(event.position),
+            InputEvent::MouseUp(event) => Some(event.position),
+            InputEvent::MouseMoved(event) => Some(event.position),
+            InputEvent::MouseExited(event) => Some(event.position),
+            InputEvent::ScrollWheel(event) => Some(event.position),
+        }
+    }
+
+    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
+        match self {
+            InputEvent::KeyDown { .. } => None,
+            InputEvent::KeyUp { .. } => None,
+            InputEvent::ModifiersChanged { .. } => None,
+            InputEvent::MouseDown(event) => Some(event),
+            InputEvent::MouseUp(event) => Some(event),
+            InputEvent::MouseMoved(event) => Some(event),
+            InputEvent::MouseExited(event) => Some(event),
+            InputEvent::ScrollWheel(event) => Some(event),
+        }
+    }
+
+    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
+        match self {
+            InputEvent::KeyDown(event) => Some(event),
+            InputEvent::KeyUp(event) => Some(event),
+            InputEvent::ModifiersChanged(event) => Some(event),
+            InputEvent::MouseDown(_) => None,
+            InputEvent::MouseUp(_) => None,
+            InputEvent::MouseMoved(_) => None,
+            InputEvent::MouseExited(_) => None,
+            InputEvent::ScrollWheel(_) => None,
+        }
+    }
+}
+
+pub struct FocusEvent {
+    pub blurred: Option<FocusHandle>,
+    pub focused: Option<FocusHandle>,
+}
+
+pub type MouseDownListener<V> = Arc<
+    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+pub type MouseUpListener<V> = Arc<
+    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+pub type MouseClickListener<V> =
+    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type MouseMoveListener<V> = Arc<
+    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub type ScrollWheelListener<V> = Arc<
+    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub type KeyListener<V> = Arc<
+    dyn Fn(
+            &mut V,
+            &dyn Any,
+            &[&DispatchContext],
+            DispatchPhase,
+            &mut ViewContext<V>,
+        ) -> Option<Box<dyn Action>>
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub struct InteractiveState<V: 'static> {
+    pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
+    pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
+    pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
+    pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
+    pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
+    pub key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
+}
+
+impl<V> Default for InteractiveState<V> {
+    fn default() -> Self {
+        Self {
+            mouse_down: SmallVec::new(),
+            mouse_up: SmallVec::new(),
+            mouse_click: SmallVec::new(),
+            mouse_move: SmallVec::new(),
+            scroll_wheel: SmallVec::new(),
+            key: SmallVec::new(),
+        }
+    }
+}