Merge branch 'gpui2' of github.com:zed-industries/zed into gpui2

Marshall Bowers created

Change summary

crates/gpui3/src/app.rs                   |  39 ++
crates/gpui3/src/app/entity_map.rs        |  10 
crates/gpui3/src/element.rs               | 208 ++++++++++------
crates/gpui3/src/elements/div.rs          | 201 ++++++++++++---
crates/gpui3/src/elements/img.rs          | 111 ++++++--
crates/gpui3/src/elements/svg.rs          | 113 ++++++--
crates/gpui3/src/elements/text.rs         |  38 +-
crates/gpui3/src/events.rs                | 131 +++++++++-
crates/gpui3/src/focus.rs                 | 159 +++++++++++++
crates/gpui3/src/gpui3.rs                 |   2 
crates/gpui3/src/interactive.rs           |  76 -----
crates/gpui3/src/platform.rs              |  10 
crates/gpui3/src/platform/mac/events.rs   |   4 
crates/gpui3/src/platform/mac/platform.rs |   8 
crates/gpui3/src/platform/mac/window.rs   |  40 +-
crates/gpui3/src/platform/test.rs         |   2 
crates/gpui3/src/view.rs                  | 168 ++++++++-----
crates/gpui3/src/window.rs                | 303 ++++++++++++++++++++++--
crates/gpui3_macros/src/derive_element.rs |  27 +
crates/ui2/src/theme.rs                   |  23 +
20 files changed, 1,240 insertions(+), 433 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -8,9 +8,10 @@ pub use model_context::*;
 use refineable::Refineable;
 
 use crate::{
-    current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId,
-    MainThread, MainThreadOnly, Platform, SubscriberSet, SvgRenderer, Task, TextStyle,
-    TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
+    current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor,
+    FocusEvent, FocusHandle, FocusId, LayoutId, MainThread, MainThreadOnly, Platform,
+    SubscriberSet, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
+    WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, HashSet, VecDeque};
@@ -54,6 +55,7 @@ impl App {
                 this: this.clone(),
                 text_system: Arc::new(TextSystem::new(platform.text_system())),
                 pending_updates: 0,
+                flushing_effects: false,
                 next_frame_callbacks: Default::default(),
                 platform: MainThreadOnly::new(platform, executor.clone()),
                 executor,
@@ -97,6 +99,7 @@ pub struct AppContext {
     this: Weak<Mutex<AppContext>>,
     pub(crate) platform: MainThreadOnly<dyn Platform>,
     text_system: Arc<TextSystem>,
+    flushing_effects: bool,
     pending_updates: usize,
     pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
     pub(crate) executor: Executor,
@@ -119,8 +122,10 @@ impl AppContext {
     pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
         self.pending_updates += 1;
         let result = update(self);
-        if self.pending_updates == 1 {
+        if !self.flushing_effects && self.pending_updates == 1 {
+            self.flushing_effects = true;
             self.flush_effects();
+            self.flushing_effects = false;
         }
         self.pending_updates -= 1;
         result
@@ -158,6 +163,7 @@ impl AppContext {
                 }
             }
             Effect::Emit { .. } => self.pending_effects.push_back(effect),
+            Effect::FocusChanged { .. } => self.pending_effects.push_back(effect),
         }
     }
 
@@ -168,6 +174,9 @@ impl AppContext {
                 match effect {
                     Effect::Notify { emitter } => self.apply_notify_effect(emitter),
                     Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
+                    Effect::FocusChanged { window_id, focused } => {
+                        self.apply_focus_changed(window_id, focused)
+                    }
                 }
             } else {
                 break;
@@ -222,6 +231,24 @@ impl AppContext {
             .retain(&emitter, |handler| handler(&event, self));
     }
 
+    fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option<FocusId>) {
+        self.update_window(window_id, |cx| {
+            if cx.window.focus == focused {
+                let mut listeners = mem::take(&mut cx.window.focus_listeners);
+                let focused = focused.map(FocusHandle::new);
+                let blurred = cx.window.last_blur.unwrap().map(FocusHandle::new);
+                let event = FocusEvent { focused, blurred };
+                for listener in &listeners {
+                    listener(&event, cx);
+                }
+
+                listeners.extend(cx.window.focus_listeners.drain(..));
+                cx.window.focus_listeners = listeners;
+            }
+        })
+        .ok();
+    }
+
     pub fn to_async(&self) -> AsyncAppContext {
         AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
     }
@@ -426,6 +453,10 @@ pub(crate) enum Effect {
         emitter: EntityId,
         event: Box<dyn Any + Send + Sync + 'static>,
     },
+    FocusChanged {
+        window_id: WindowId,
+        focused: Option<FocusId>,
+    },
 }
 
 #[cfg(test)]

crates/gpui3/src/app/entity_map.rs 🔗

@@ -199,6 +199,16 @@ pub struct WeakHandle<T> {
     entity_map: Weak<RwLock<EntityMapState>>,
 }
 
+impl<T: 'static + Send + Sync> Clone for WeakHandle<T> {
+    fn clone(&self) -> Self {
+        Self {
+            id: self.id,
+            entity_type: self.entity_type,
+            entity_map: self.entity_map.clone(),
+        }
+    }
+}
+
 impl<T: Send + Sync + 'static> WeakHandle<T> {
     pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
         let entity_map = &self.entity_map.upgrade()?;

crates/gpui3/src/element.rs 🔗

@@ -1,6 +1,7 @@
-use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext};
+use crate::{BorrowWindow, Bounds, ElementId, FocusHandle, LayoutId, Pixels, Point, ViewContext};
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
+use std::mem;
 
 pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
     type ViewState: 'static + Send + Sync;
@@ -8,17 +9,24 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
 
     fn id(&self) -> Option<ElementId>;
 
-    fn layout(
+    fn initialize(
         &mut self,
-        state: &mut Self::ViewState,
+        view_state: &mut Self::ViewState,
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState);
+    ) -> Self::ElementState;
+
+    fn layout(
+        &mut self,
+        view_state: &mut Self::ViewState,
+        element_state: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> LayoutId;
 
     fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
-        state: &mut Self::ViewState,
+        view_state: &mut Self::ViewState,
         element_state: &mut Self::ElementState,
         cx: &mut ViewContext<Self::ViewState>,
     );
@@ -31,21 +39,54 @@ pub trait ElementIdentity: 'static + Send + Sync {
     fn id(&self) -> Option<ElementId>;
 }
 
-pub struct IdentifiedElement(pub(crate) ElementId);
-pub struct AnonymousElement;
+pub struct Identified(pub(crate) ElementId);
 
-impl ElementIdentity for IdentifiedElement {
+impl ElementIdentity for Identified {
     fn id(&self) -> Option<ElementId> {
         Some(self.0.clone())
     }
 }
 
-impl ElementIdentity for AnonymousElement {
+pub struct Anonymous;
+
+impl ElementIdentity for Anonymous {
     fn id(&self) -> Option<ElementId> {
         None
     }
 }
 
+pub trait ElementFocusability: 'static + Send + Sync {
+    fn focus_handle(&self) -> Option<&FocusHandle>;
+}
+
+pub struct Focusable(FocusHandle);
+
+impl AsRef<FocusHandle> for Focusable {
+    fn as_ref(&self) -> &FocusHandle {
+        &self.0
+    }
+}
+
+impl ElementFocusability for Focusable {
+    fn focus_handle(&self) -> Option<&FocusHandle> {
+        Some(&self.0)
+    }
+}
+
+impl From<FocusHandle> for Focusable {
+    fn from(value: FocusHandle) -> Self {
+        Self(value)
+    }
+}
+
+pub struct NonFocusable;
+
+impl ElementFocusability for NonFocusable {
+    fn focus_handle(&self) -> Option<&FocusHandle> {
+        None
+    }
+}
+
 pub trait ParentElement: Element {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]>;
 
@@ -70,9 +111,10 @@ pub trait ParentElement: Element {
     }
 }
 
-trait ElementObject<S>: 'static + Send + Sync {
-    fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> LayoutId;
-    fn paint(&mut self, state: &mut S, offset: Option<Point<Pixels>>, cx: &mut ViewContext<S>);
+trait ElementObject<V>: 'static + Send + Sync {
+    fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
+    fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
+    fn paint(&mut self, view_state: &mut V, offset: Option<Point<Pixels>>, cx: &mut ViewContext<V>);
 }
 
 struct RenderedElement<E: Element> {
@@ -81,17 +123,17 @@ struct RenderedElement<E: Element> {
 }
 
 #[derive(Default)]
-enum ElementRenderPhase<S> {
+enum ElementRenderPhase<V> {
     #[default]
-    Rendered,
+    Start,
+    Initialized {
+        frame_state: Option<V>,
+    },
     LayoutRequested {
         layout_id: LayoutId,
-        frame_state: Option<S>,
-    },
-    Painted {
-        bounds: Bounds<Pixels>,
-        frame_state: Option<S>,
+        frame_state: Option<V>,
     },
+    Painted,
 }
 
 /// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
@@ -101,52 +143,57 @@ impl<E: Element> RenderedElement<E> {
     fn new(element: E) -> Self {
         RenderedElement {
             element,
-            phase: ElementRenderPhase::Rendered,
+            phase: ElementRenderPhase::Start,
         }
     }
+}
 
-    fn paint_with_element_state(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        view_state: &mut E::ViewState,
-        frame_state: &mut Option<E::ElementState>,
-        cx: &mut ViewContext<E::ViewState>,
-    ) {
-        if let Some(id) = self.element.id() {
+impl<E> ElementObject<E::ViewState> for RenderedElement<E>
+where
+    E: Element,
+{
+    fn initialize(&mut self, view_state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) {
+        let frame_state = if let Some(id) = self.element.id() {
             cx.with_element_state(id, |element_state, cx| {
-                let mut element_state = element_state.unwrap();
-                self.element
-                    .paint(bounds, view_state, &mut element_state, cx);
+                let element_state = self.element.initialize(view_state, element_state, cx);
                 ((), element_state)
             });
+            None
         } else {
-            self.element
-                .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
-        }
+            let frame_state = self.element.initialize(view_state, None, cx);
+            Some(frame_state)
+        };
+
+        self.phase = ElementRenderPhase::Initialized { frame_state };
     }
-}
 
-impl<E, S> ElementObject<E::ViewState> for RenderedElement<E>
-where
-    E: Element<ElementState = S>,
-    S: 'static + Send + Sync,
-{
     fn layout(&mut self, state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) -> LayoutId {
-        let (layout_id, frame_state) = if let Some(id) = self.element.id() {
-            let layout_id = cx.with_element_state(id, |element_state, cx| {
-                self.element.layout(state, element_state, cx)
-            });
-            (layout_id, None)
-        } else {
-            let (layout_id, frame_state) = self.element.layout(state, None, cx);
-            (layout_id, Some(frame_state))
+        let layout_id;
+        let mut frame_state;
+        match mem::take(&mut self.phase) {
+            ElementRenderPhase::Initialized {
+                frame_state: initial_frame_state,
+            } => {
+                frame_state = initial_frame_state;
+                if let Some(id) = self.element.id() {
+                    layout_id = cx.with_element_state(id, |element_state, cx| {
+                        let mut element_state = element_state.unwrap();
+                        let layout_id = self.element.layout(state, &mut element_state, cx);
+                        (layout_id, element_state)
+                    });
+                } else {
+                    layout_id = self
+                        .element
+                        .layout(state, frame_state.as_mut().unwrap(), cx);
+                }
+            }
+            _ => panic!("must call initialize before layout"),
         };
 
         self.phase = ElementRenderPhase::LayoutRequested {
             layout_id,
             frame_state,
         };
-
         layout_id
     }
 
@@ -156,60 +203,63 @@ where
         offset: Option<Point<Pixels>>,
         cx: &mut ViewContext<E::ViewState>,
     ) {
-        self.phase = match std::mem::take(&mut self.phase) {
-            ElementRenderPhase::Rendered => panic!("must call layout before paint"),
-
+        self.phase = match mem::take(&mut self.phase) {
             ElementRenderPhase::LayoutRequested {
                 layout_id,
                 mut frame_state,
             } => {
                 let mut bounds = cx.layout_bounds(layout_id);
                 offset.map(|offset| bounds.origin += offset);
-                self.paint_with_element_state(bounds, view_state, &mut frame_state, cx);
-                ElementRenderPhase::Painted {
-                    bounds,
-                    frame_state,
+                if let Some(id) = self.element.id() {
+                    cx.with_element_state(id, |element_state, cx| {
+                        let mut element_state = element_state.unwrap();
+                        self.element
+                            .paint(bounds, view_state, &mut element_state, cx);
+                        ((), element_state)
+                    });
+                } else {
+                    self.element
+                        .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
                 }
+                ElementRenderPhase::Painted
             }
 
-            ElementRenderPhase::Painted {
-                bounds,
-                mut frame_state,
-            } => {
-                self.paint_with_element_state(bounds, view_state, &mut frame_state, cx);
-                ElementRenderPhase::Painted {
-                    bounds,
-                    frame_state,
-                }
-            }
+            _ => panic!("must call layout before paint"),
         };
     }
 }
 
-pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
+pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
 
-impl<S: 'static + Send + Sync> AnyElement<S> {
-    pub fn new<E: Element<ViewState = S>>(element: E) -> Self {
+impl<V: 'static + Send + Sync> AnyElement<V> {
+    pub fn new<E: Element<ViewState = V>>(element: E) -> Self {
         AnyElement(Box::new(RenderedElement::new(element)))
     }
-}
 
-impl<S: 'static + Send + Sync> AnyElement<S> {
-    pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> LayoutId {
-        self.0.layout(state, cx)
+    pub fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
+        self.0.initialize(view_state, cx);
+    }
+
+    pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
+        self.0.layout(view_state, cx)
     }
 
-    pub fn paint(&mut self, state: &mut S, offset: Option<Point<Pixels>>, cx: &mut ViewContext<S>) {
-        self.0.paint(state, offset, cx)
+    pub fn paint(
+        &mut self,
+        view_state: &mut V,
+        offset: Option<Point<Pixels>>,
+        cx: &mut ViewContext<V>,
+    ) {
+        self.0.paint(view_state, offset, cx)
     }
 }
 
-pub trait IntoAnyElement<S> {
-    fn into_any(self) -> AnyElement<S>;
+pub trait IntoAnyElement<V> {
+    fn into_any(self) -> AnyElement<V>;
 }
 
-impl<S> IntoAnyElement<S> for AnyElement<S> {
-    fn into_any(self) -> AnyElement<S> {
+impl<V> IntoAnyElement<V> for AnyElement<V> {
+    fn into_any(self) -> AnyElement<V> {
         self
     }
 }

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

@@ -1,15 +1,15 @@
 use crate::{
-    Active, AnonymousElement, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase,
-    Element, ElementId, ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement,
-    LayoutId, MouseClickEvent, MouseDownEvent, MouseEventListeners, MouseMoveEvent, MouseUpEvent,
-    Overflow, ParentElement, Pixels, Point, ScrollWheelEvent, SharedString, Style, StyleRefinement,
-    Styled, ViewContext,
+    Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
+    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusHandle, Focusable,
+    Hover, Identified, Interactive, IntoAnyElement, LayoutId, MouseClickEvent, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point,
+    ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, ViewContext,
 };
 use collections::HashMap;
 use parking_lot::Mutex;
 use refineable::Refineable;
 use smallvec::SmallVec;
-use std::sync::Arc;
+use std::{mem, sync::Arc};
 
 #[derive(Default)]
 pub struct DivState {
@@ -60,12 +60,13 @@ impl ScrollState {
     }
 }
 
-pub fn div<S>() -> Div<S, AnonymousElement>
+pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
 where
-    S: 'static + Send + Sync,
+    V: 'static + Send + Sync,
 {
     Div {
-        kind: AnonymousElement,
+        identity: Anonymous,
+        focusability: NonFocusable,
         children: SmallVec::new(),
         group: None,
         base_style: StyleRefinement::default(),
@@ -73,12 +74,20 @@ where
         group_hover: None,
         active_style: StyleRefinement::default(),
         group_active: None,
-        listeners: MouseEventListeners::default(),
+        focus_style: StyleRefinement::default(),
+        focus_in_style: StyleRefinement::default(),
+        in_focus_style: StyleRefinement::default(),
+        listeners: EventListeners::default(),
     }
 }
 
-pub struct Div<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
-    kind: K,
+pub struct Div<
+    V: 'static + Send + Sync,
+    I: ElementIdentity = Anonymous,
+    F: ElementFocusability = NonFocusable,
+> {
+    identity: I,
+    focusability: F,
     children: SmallVec<[AnyElement<V>; 2]>,
     group: Option<SharedString>,
     base_style: StyleRefinement,
@@ -86,7 +95,10 @@ pub struct Div<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement>
     group_hover: Option<GroupStyle>,
     active_style: StyleRefinement,
     group_active: Option<GroupStyle>,
-    listeners: MouseEventListeners<V>,
+    focus_style: StyleRefinement,
+    focus_in_style: StyleRefinement,
+    in_focus_style: StyleRefinement,
+    listeners: EventListeners<V>,
 }
 
 struct GroupStyle {
@@ -94,13 +106,15 @@ struct GroupStyle {
     style: StyleRefinement,
 }
 
-impl<V> Div<V, AnonymousElement>
+impl<V, F> Div<V, Anonymous, F>
 where
+    F: ElementFocusability,
     V: 'static + Send + Sync,
 {
-    pub fn id(self, id: impl Into<ElementId>) -> Div<V, IdentifiedElement> {
+    pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified, F> {
         Div {
-            kind: IdentifiedElement(id.into()),
+            identity: Identified(id.into()),
+            focusability: self.focusability,
             children: self.children,
             group: self.group,
             base_style: self.base_style,
@@ -108,15 +122,19 @@ where
             group_hover: self.group_hover,
             active_style: self.active_style,
             group_active: self.group_active,
+            focus_style: self.focus_style,
+            focus_in_style: self.focus_in_style,
+            in_focus_style: self.in_focus_style,
             listeners: self.listeners,
         }
     }
 }
 
-impl<V, K> Div<V, K>
+impl<V, I, F> Div<V, I, F>
 where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
     pub fn group(mut self, group: impl Into<SharedString>) -> Self {
         self.group = Some(group.into());
@@ -187,6 +205,20 @@ where
         let mut computed_style = Style::default();
         computed_style.refine(&self.base_style);
 
+        if let Some(handle) = self.focusability.focus_handle() {
+            if handle.contains_focused(cx) {
+                computed_style.refine(&self.focus_in_style);
+            }
+
+            if handle.within_focused(cx) {
+                computed_style.refine(&self.in_focus_style);
+            }
+
+            if handle.is_focused(cx) {
+                computed_style.refine(&self.focus_style);
+            }
+        }
+
         let mouse_position = cx.mouse_position();
 
         if let Some(group_hover) = self.group_hover.as_ref() {
@@ -258,12 +290,12 @@ where
     }
 
     fn paint_event_listeners(
-        &self,
+        &mut self,
         bounds: Bounds<Pixels>,
         pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
         cx: &mut ViewContext<V>,
     ) {
-        let click_listeners = self.listeners.mouse_click.clone();
+        let click_listeners = mem::take(&mut self.listeners.mouse_click);
         let mouse_down = pending_click.lock().clone();
         if let Some(mouse_down) = mouse_down {
             cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
@@ -287,25 +319,25 @@ where
             });
         }
 
-        for listener in self.listeners.mouse_down.iter().cloned() {
+        for listener in mem::take(&mut self.listeners.mouse_down) {
             cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in self.listeners.mouse_up.iter().cloned() {
+        for listener in mem::take(&mut self.listeners.mouse_up) {
             cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in self.listeners.mouse_move.iter().cloned() {
+        for listener in mem::take(&mut self.listeners.mouse_move) {
             cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in self.listeners.scroll_wheel.iter().cloned() {
+        for listener in mem::take(&mut self.listeners.scroll_wheel) {
             cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
@@ -313,26 +345,92 @@ where
     }
 }
 
-impl<V, K> Element for Div<V, K>
+impl<V, I> Div<V, I, NonFocusable>
+where
+    I: ElementIdentity,
+    V: 'static + Send + Sync,
+{
+    pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable> {
+        Div {
+            identity: self.identity,
+            focusability: handle.clone().into(),
+            children: self.children,
+            group: self.group,
+            base_style: self.base_style,
+            hover_style: self.hover_style,
+            group_hover: self.group_hover,
+            active_style: self.active_style,
+            group_active: self.group_active,
+            focus_style: self.focus_style,
+            focus_in_style: self.focus_in_style,
+            in_focus_style: self.in_focus_style,
+            listeners: self.listeners,
+        }
+    }
+}
+
+impl<V, I> Focus for Div<V, I, Focusable>
 where
+    I: ElementIdentity,
+    V: 'static + Send + Sync,
+{
+    fn handle(&self) -> &FocusHandle {
+        self.focusability.as_ref()
+    }
+
+    fn set_focus_style(&mut self, style: StyleRefinement) {
+        self.focus_style = style;
+    }
+
+    fn set_focus_in_style(&mut self, style: StyleRefinement) {
+        self.focus_in_style = style;
+    }
+
+    fn set_in_focus_style(&mut self, style: StyleRefinement) {
+        self.in_focus_style = style;
+    }
+}
+
+impl<V, I, F> Element for Div<V, I, F>
+where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
     type ViewState = V;
     type ElementState = DivState;
 
     fn id(&self) -> Option<ElementId> {
-        self.kind.id()
+        self.identity.id()
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
         view_state: &mut Self::ViewState,
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState) {
-        let element_state = element_state.unwrap_or_default();
-        let style = self.compute_style(Bounds::default(), &element_state, cx);
+    ) -> Self::ElementState {
+        cx.with_focus(
+            self.focusability.focus_handle().cloned(),
+            mem::take(&mut self.listeners.key_down),
+            mem::take(&mut self.listeners.key_up),
+            mem::take(&mut self.listeners.focus),
+            |cx| {
+                for child in &mut self.children {
+                    child.initialize(view_state, cx);
+                }
+                element_state.unwrap_or_default()
+            },
+        )
+    }
+
+    fn layout(
+        &mut self,
+        view_state: &mut Self::ViewState,
+        element_state: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> LayoutId {
+        let style = self.compute_style(Bounds::default(), element_state, cx);
         style.apply_text_style(cx, |cx| {
             self.with_element_id(cx, |this, cx| {
                 let layout_ids = this
@@ -340,9 +438,7 @@ where
                     .iter_mut()
                     .map(|child| child.layout(view_state, cx))
                     .collect::<Vec<_>>();
-
-                let layout_id = cx.request_layout(&style, layout_ids);
-                (layout_id, element_state)
+                cx.request_layout(&style, layout_ids)
             })
         })
     }
@@ -410,50 +506,55 @@ where
     }
 }
 
-impl<V, K> IntoAnyElement<V> for Div<V, K>
+impl<V, I, F> IntoAnyElement<V> for Div<V, I, F>
 where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
     fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<V, K> ParentElement for Div<V, K>
+impl<V, I, F> ParentElement for Div<V, I, F>
 where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
         &mut self.children
     }
 }
 
-impl<V, K> Styled for Div<V, K>
+impl<V, I, F> Styled for Div<V, I, F>
 where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
     fn style(&mut self) -> &mut StyleRefinement {
         &mut self.base_style
     }
 }
 
-impl<V, K> Interactive for Div<V, K>
+impl<V, I, F> Interactive for Div<V, I, F>
 where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
-    fn listeners(&mut self) -> &mut MouseEventListeners<V> {
+    fn listeners(&mut self) -> &mut EventListeners<V> {
         &mut self.listeners
     }
 }
 
-impl<V, K> Hover for Div<V, K>
+impl<V, I, F> Hover for Div<V, I, F>
 where
+    I: ElementIdentity,
+    F: ElementFocusability,
     V: 'static + Send + Sync,
-    K: ElementIdentity,
 {
     fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         if let Some(group) = group {
@@ -464,10 +565,16 @@ where
     }
 }
 
-impl<V> Click for Div<V, IdentifiedElement> where V: 'static + Send + Sync {}
+impl<V, F> Click for Div<V, Identified, F>
+where
+    F: ElementFocusability,
+    V: 'static + Send + Sync,
+{
+}
 
-impl<V> Active for Div<V, IdentifiedElement>
+impl<V, F> Active for Div<V, Identified, F>
 where
+    F: ElementFocusability,
     V: 'static + Send + Sync,
 {
     fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {

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

@@ -1,18 +1,23 @@
 use crate::{
-    div, Active, AnonymousElement, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
-    ElementId, ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement, LayoutId,
-    MouseEventListeners, Pixels, SharedString, StyleRefinement, Styled, ViewContext,
+    div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
+    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover,
+    Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
+    StyleRefinement, Styled, ViewContext,
 };
 use futures::FutureExt;
 use util::ResultExt;
 
-pub struct Img<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
-    base: Div<V, K>,
+pub struct Img<
+    V: 'static + Send + Sync,
+    I: ElementIdentity = Anonymous,
+    F: ElementFocusability = NonFocusable,
+> {
+    base: Div<V, I, F>,
     uri: Option<SharedString>,
     grayscale: bool,
 }
 
-pub fn img<V>() -> Img<V, AnonymousElement>
+pub fn img<V>() -> Img<V, Anonymous, NonFocusable>
 where
     V: 'static + Send + Sync,
 {
@@ -23,10 +28,11 @@ where
     }
 }
 
-impl<V, K> Img<V, K>
+impl<V, I, F> Img<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
         self.uri = Some(uri.into());
@@ -39,8 +45,12 @@ where
     }
 }
 
-impl<V: 'static + Send + Sync> Img<V, AnonymousElement> {
-    pub fn id(self, id: impl Into<ElementId>) -> Img<V, IdentifiedElement> {
+impl<V, F> Img<V, Anonymous, F>
+where
+    V: 'static + Send + Sync,
+    F: ElementFocusability,
+{
+    pub fn id(self, id: impl Into<ElementId>) -> Img<V, Identified, F> {
         Img {
             base: self.base.id(id),
             uri: self.uri,
@@ -49,20 +59,22 @@ impl<V: 'static + Send + Sync> Img<V, AnonymousElement> {
     }
 }
 
-impl<V, K> IntoAnyElement<V> for Img<V, K>
+impl<V, I, F> IntoAnyElement<V> for Img<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<V, K> Element for Img<V, K>
+impl<V, I, F> Element for Img<V, I, F>
 where
     V: Send + Sync + 'static,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     type ViewState = V;
     type ElementState = DivState;
@@ -71,24 +83,30 @@ where
         self.base.id()
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
-        view_state: &mut Self::ViewState,
+        view_state: &mut V,
         element_state: Option<Self::ElementState>,
+        cx: &mut ViewContext<V>,
+    ) -> Self::ElementState {
+        self.base.initialize(view_state, element_state, cx)
+    }
+
+    fn layout(
+        &mut self,
+        view_state: &mut V,
+        element_state: &mut Self::ElementState,
         cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState)
-    where
-        Self: Sized,
-    {
+    ) -> LayoutId {
         self.base.layout(view_state, element_state, cx)
     }
 
     fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
-        view: &mut Self::ViewState,
+        view: &mut V,
         element_state: &mut Self::ElementState,
-        cx: &mut ViewContext<Self::ViewState>,
+        cx: &mut ViewContext<V>,
     ) {
         cx.stack(0, |cx| {
             self.base.paint(bounds, view, element_state, cx);
@@ -121,43 +139,74 @@ where
     }
 }
 
-impl<V, K> Styled for Img<V, K>
+impl<V, I, F> Styled for Img<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     fn style(&mut self) -> &mut StyleRefinement {
         self.base.style()
     }
 }
 
-impl<V, K> Interactive for Img<V, K>
+impl<V, I, F> Interactive for Img<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
-    fn listeners(&mut self) -> &mut MouseEventListeners<V> {
+    fn listeners(&mut self) -> &mut EventListeners<V> {
         self.base.listeners()
     }
 }
 
-impl<V, K> Hover for Img<V, K>
+impl<V, I, F> Hover for Img<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_hover_style(group, style);
     }
 }
 
-impl<V> Click for Img<V, IdentifiedElement> where V: 'static + Send + Sync {}
+impl<V, F> Click for Img<V, Identified, F>
+where
+    V: 'static + Send + Sync,
+    F: ElementFocusability,
+{
+}
 
-impl<V> Active for Img<V, IdentifiedElement>
+impl<V, F> Active for Img<V, Identified, F>
 where
     V: 'static + Send + Sync,
+    F: ElementFocusability,
 {
     fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_active_style(group, style)
     }
 }
+
+impl<V, I> Focus for Img<V, I, Focusable>
+where
+    V: 'static + Send + Sync,
+    I: ElementIdentity,
+{
+    fn set_focus_style(&mut self, style: StyleRefinement) {
+        self.base.set_focus_style(style)
+    }
+
+    fn set_focus_in_style(&mut self, style: StyleRefinement) {
+        self.base.set_focus_in_style(style)
+    }
+
+    fn set_in_focus_style(&mut self, style: StyleRefinement) {
+        self.base.set_in_focus_style(style)
+    }
+
+    fn handle(&self) -> &crate::FocusHandle {
+        self.base.handle()
+    }
+}

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

@@ -1,16 +1,21 @@
 use crate::{
-    div, Active, AnonymousElement, AnyElement, Bounds, Click, Div, DivState, Element, ElementId,
-    ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement, LayoutId,
-    MouseEventListeners, Pixels, SharedString, StyleRefinement, Styled,
+    div, Active, Anonymous, AnyElement, Bounds, Click, Div, DivState, Element, ElementFocusability,
+    ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover, Identified, Interactive,
+    IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, StyleRefinement, Styled,
+    ViewContext,
 };
 use util::ResultExt;
 
-pub struct Svg<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
-    base: Div<V, K>,
+pub struct Svg<
+    V: 'static + Send + Sync,
+    I: ElementIdentity = Anonymous,
+    F: ElementFocusability = NonFocusable,
+> {
+    base: Div<V, I, F>,
     path: Option<SharedString>,
 }
 
-pub fn svg<V>() -> Svg<V, AnonymousElement>
+pub fn svg<V>() -> Svg<V, Anonymous, NonFocusable>
 where
     V: 'static + Send + Sync,
 {
@@ -20,10 +25,11 @@ where
     }
 }
 
-impl<V, K> Svg<V, K>
+impl<V, I, F> Svg<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     pub fn path(mut self, path: impl Into<SharedString>) -> Self {
         self.path = Some(path.into());
@@ -31,8 +37,12 @@ where
     }
 }
 
-impl<V: 'static + Send + Sync> Svg<V, AnonymousElement> {
-    pub fn id(self, id: impl Into<ElementId>) -> Svg<V, IdentifiedElement> {
+impl<V, F> Svg<V, Anonymous, F>
+where
+    V: 'static + Send + Sync,
+    F: ElementFocusability,
+{
+    pub fn id(self, id: impl Into<ElementId>) -> Svg<V, Identified, F> {
         Svg {
             base: self.base.id(id),
             path: self.path,
@@ -40,20 +50,22 @@ impl<V: 'static + Send + Sync> Svg<V, AnonymousElement> {
     }
 }
 
-impl<V, K> IntoAnyElement<V> for Svg<V, K>
+impl<V, I, F> IntoAnyElement<V> for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<V, K> Element for Svg<V, K>
+impl<V, I, F> Element for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     type ViewState = V;
     type ElementState = DivState;
@@ -62,16 +74,22 @@ where
         self.base.id()
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
-        view: &mut V,
+        view_state: &mut V,
         element_state: Option<Self::ElementState>,
-        cx: &mut crate::ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState)
-    where
-        Self: Sized,
-    {
-        self.base.layout(view, element_state, cx)
+        cx: &mut ViewContext<V>,
+    ) -> Self::ElementState {
+        self.base.initialize(view_state, element_state, cx)
+    }
+
+    fn layout(
+        &mut self,
+        view_state: &mut V,
+        element_state: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> LayoutId {
+        self.base.layout(view_state, element_state, cx)
     }
 
     fn paint(
@@ -79,7 +97,7 @@ where
         bounds: Bounds<Pixels>,
         view: &mut Self::ViewState,
         element_state: &mut Self::ElementState,
-        cx: &mut crate::ViewContext<V>,
+        cx: &mut ViewContext<V>,
     ) where
         Self: Sized,
     {
@@ -95,43 +113,74 @@ where
     }
 }
 
-impl<V, K> Styled for Svg<V, K>
+impl<V, I, F> Styled for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     fn style(&mut self) -> &mut StyleRefinement {
         self.base.style()
     }
 }
 
-impl<V, K> Interactive for Svg<V, K>
+impl<V, I, F> Interactive for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
-    fn listeners(&mut self) -> &mut MouseEventListeners<V> {
+    fn listeners(&mut self) -> &mut EventListeners<V> {
         self.base.listeners()
     }
 }
 
-impl<V, K> Hover for Svg<V, K>
+impl<V, I, F> Hover for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
-    K: ElementIdentity,
+    I: ElementIdentity,
+    F: ElementFocusability,
 {
     fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_hover_style(group, style);
     }
 }
 
-impl<V> Click for Svg<V, IdentifiedElement> where V: 'static + Send + Sync {}
+impl<V, F> Click for Svg<V, Identified, F>
+where
+    V: 'static + Send + Sync,
+    F: ElementFocusability,
+{
+}
 
-impl<V> Active for Svg<V, IdentifiedElement>
+impl<V, F> Active for Svg<V, Identified, F>
 where
     V: 'static + Send + Sync,
+    F: ElementFocusability,
 {
     fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_active_style(group, style)
     }
 }
+
+impl<V, I> Focus for Svg<V, I, Focusable>
+where
+    V: 'static + Send + Sync,
+    I: ElementIdentity,
+{
+    fn set_focus_style(&mut self, style: StyleRefinement) {
+        self.base.set_focus_style(style)
+    }
+
+    fn set_focus_in_style(&mut self, style: StyleRefinement) {
+        self.base.set_focus_in_style(style)
+    }
+
+    fn set_in_focus_style(&mut self, style: StyleRefinement) {
+        self.base.set_in_focus_style(style)
+    }
+
+    fn handle(&self) -> &crate::FocusHandle {
+        self.base.handle()
+    }
+}

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

@@ -39,31 +39,40 @@ impl<S: 'static + Send + Sync> IntoAnyElement<S> for String {
     }
 }
 
-pub struct Text<S> {
+pub struct Text<V> {
     text: SharedString,
-    state_type: PhantomData<S>,
+    state_type: PhantomData<V>,
 }
 
-impl<S: 'static + Send + Sync> IntoAnyElement<S> for Text<S> {
-    fn into_any(self) -> AnyElement<S> {
+impl<V: 'static + Send + Sync> IntoAnyElement<V> for Text<V> {
+    fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<S: 'static + Send + Sync> Element for Text<S> {
-    type ViewState = S;
+impl<V: 'static + Send + Sync> Element for Text<V> {
+    type ViewState = V;
     type ElementState = Arc<Mutex<Option<TextElementState>>>;
 
     fn id(&self) -> Option<crate::ElementId> {
         None
     }
 
+    fn initialize(
+        &mut self,
+        _view_state: &mut V,
+        element_state: Option<Self::ElementState>,
+        _cx: &mut ViewContext<V>,
+    ) -> Self::ElementState {
+        element_state.unwrap_or_default()
+    }
+
     fn layout(
         &mut self,
-        _view: &mut S,
-        _element_state: Option<Self::ElementState>,
-        cx: &mut ViewContext<S>,
-    ) -> (LayoutId, Self::ElementState) {
+        _view: &mut V,
+        element_state: &mut Self::ElementState,
+        cx: &mut ViewContext<V>,
+    ) -> LayoutId {
         let text_system = cx.text_system().clone();
         let text_style = cx.text_style();
         let font_size = text_style.font_size * cx.rem_size();
@@ -71,7 +80,6 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
             .line_height
             .to_pixels(font_size.into(), cx.rem_size());
         let text = self.text.clone();
-        let element_state = Arc::new(Mutex::new(None));
 
         let rem_size = cx.rem_size();
         let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
@@ -102,15 +110,15 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
             }
         });
 
-        (layout_id, element_state)
+        layout_id
     }
 
-    fn paint<'a>(
+    fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
-        _: &mut Self::ViewState,
+        _: &mut V,
         element_state: &mut Self::ElementState,
-        cx: &mut ViewContext<S>,
+        cx: &mut ViewContext<V>,
     ) {
         let element_state = element_state.lock();
         let element_state = element_state

crates/gpui3/src/events.rs 🔗

@@ -1,4 +1,7 @@
-use crate::{point, Keystroke, Modifiers, Pixels, Point};
+use crate::{
+    point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext,
+};
+use smallvec::SmallVec;
 use std::{any::Any, ops::Deref};
 
 #[derive(Clone, Debug, Eq, PartialEq)]
@@ -26,7 +29,7 @@ impl Deref for ModifiersChangedEvent {
 }
 
 /// The phase of a touch motion event.
-/// Based on the winit enum of the same name,
+/// Based on the winit enum of the same name.
 #[derive(Clone, Copy, Debug)]
 pub enum TouchPhase {
     Started,
@@ -50,6 +53,12 @@ pub struct MouseUpEvent {
     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,
@@ -155,7 +164,7 @@ impl Deref for MouseExitEvent {
 }
 
 #[derive(Clone, Debug)]
-pub enum Event {
+pub enum InputEvent {
     KeyDown(KeyDownEvent),
     KeyUp(KeyUpEvent),
     ModifiersChanged(ModifiersChangedEvent),
@@ -166,30 +175,112 @@ pub enum Event {
     ScrollWheel(ScrollWheelEvent),
 }
 
-impl Event {
+impl InputEvent {
     pub fn position(&self) -> Option<Point<Pixels>> {
         match self {
-            Event::KeyDown { .. } => None,
-            Event::KeyUp { .. } => None,
-            Event::ModifiersChanged { .. } => None,
-            Event::MouseDown(event) => Some(event.position),
-            Event::MouseUp(event) => Some(event.position),
-            Event::MouseMoved(event) => Some(event.position),
-            Event::MouseExited(event) => Some(event.position),
-            Event::ScrollWheel(event) => Some(event.position),
+            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 {
-            Event::KeyDown { .. } => None,
-            Event::KeyUp { .. } => None,
-            Event::ModifiersChanged { .. } => None,
-            Event::MouseDown(event) => Some(event),
-            Event::MouseUp(event) => Some(event),
-            Event::MouseMoved(event) => Some(event),
-            Event::MouseExited(event) => Some(event),
-            Event::ScrollWheel(event) => Some(event),
+            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> = Box<
+    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+pub type MouseUpListener<V> = Box<
+    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+pub type MouseClickListener<V> =
+    Box<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type MouseMoveListener<V> = Box<
+    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub type ScrollWheelListener<V> = Box<
+    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub type KeyDownListener<V> =
+    Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type KeyUpListener<V> =
+    Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type FocusListener<V> =
+    Box<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 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_down: SmallVec<[KeyDownListener<V>; 2]>,
+    pub key_up: SmallVec<[KeyUpListener<V>; 2]>,
+    pub focus: SmallVec<[FocusListener<V>; 2]>,
+}
+
+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_down: SmallVec::new(),
+            key_up: SmallVec::new(),
+            focus: SmallVec::new(),
         }
     }
 }

crates/gpui3/src/focus.rs 🔗

@@ -0,0 +1,159 @@
+use crate::{
+    DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, StyleRefinement,
+    ViewContext,
+};
+
+pub trait Focus: Interactive {
+    fn set_focus_style(&mut self, style: StyleRefinement);
+    fn set_focus_in_style(&mut self, style: StyleRefinement);
+    fn set_in_focus_style(&mut self, style: StyleRefinement);
+    fn handle(&self) -> &FocusHandle;
+
+    fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
+    where
+        Self: Sized,
+    {
+        self.set_focus_style(f(StyleRefinement::default()));
+        self
+    }
+
+    fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
+    where
+        Self: Sized,
+    {
+        self.set_focus_in_style(f(StyleRefinement::default()));
+        self
+    }
+
+    fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
+    where
+        Self: Sized,
+    {
+        self.set_in_focus_style(f(StyleRefinement::default()));
+        self
+    }
+
+    fn on_focus(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let handle = self.handle().clone();
+        self.listeners()
+            .focus
+            .push(Box::new(move |view, event, cx| {
+                if event.focused.as_ref() == Some(&handle) {
+                    listener(view, event, cx)
+                }
+            }));
+        self
+    }
+
+    fn on_blur(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let handle = self.handle().clone();
+        self.listeners()
+            .focus
+            .push(Box::new(move |view, event, cx| {
+                if event.blurred.as_ref() == Some(&handle) {
+                    listener(view, event, cx)
+                }
+            }));
+        self
+    }
+
+    fn on_focus_in(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let handle = self.handle().clone();
+        self.listeners()
+            .focus
+            .push(Box::new(move |view, event, cx| {
+                if event
+                    .focused
+                    .as_ref()
+                    .map_or(false, |focused| focused.contains(&handle, cx))
+                {
+                    listener(view, event, cx)
+                }
+            }));
+        self
+    }
+
+    fn on_focus_out(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let handle = self.handle().clone();
+        self.listeners()
+            .focus
+            .push(Box::new(move |view, event, cx| {
+                if event
+                    .blurred
+                    .as_ref()
+                    .map_or(false, |blurred| handle.contains(&blurred, cx))
+                {
+                    listener(view, event, cx)
+                }
+            }));
+        self
+    }
+
+    fn on_key_down(
+        mut self,
+        listener: impl Fn(
+                &mut Self::ViewState,
+                &KeyDownEvent,
+                DispatchPhase,
+                &mut ViewContext<Self::ViewState>,
+            ) + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.listeners().key_down.push(Box::new(listener));
+        self
+    }
+
+    fn on_key_up(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.listeners().key_up.push(Box::new(listener));
+        self
+    }
+}

crates/gpui3/src/gpui3.rs 🔗

@@ -6,6 +6,7 @@ mod element;
 mod elements;
 mod events;
 mod executor;
+mod focus;
 mod geometry;
 mod hover;
 mod image_cache;
@@ -31,6 +32,7 @@ pub use element::*;
 pub use elements::*;
 pub use events::*;
 pub use executor::*;
+pub use focus::*;
 pub use geometry::*;
 pub use gpui3_macros::*;
 pub use hover::*;

crates/gpui3/src/interactive.rs 🔗

@@ -1,13 +1,10 @@
-use smallvec::SmallVec;
-
 use crate::{
-    Bounds, DispatchPhase, Element, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, ScrollWheelEvent, ViewContext,
+    DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext,
 };
-use std::sync::Arc;
 
 pub trait Interactive: Element {
-    fn listeners(&mut self) -> &mut MouseEventListeners<Self::ViewState>;
+    fn listeners(&mut self) -> &mut EventListeners<Self::ViewState>;
 
     fn on_mouse_down(
         mut self,
@@ -22,7 +19,7 @@ pub trait Interactive: Element {
     {
         self.listeners()
             .mouse_down
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+            .push(Box::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == button
                     && bounds.contains_point(&event.position)
@@ -46,7 +43,7 @@ pub trait Interactive: Element {
     {
         self.listeners()
             .mouse_up
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+            .push(Box::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == button
                     && bounds.contains_point(&event.position)
@@ -70,7 +67,7 @@ pub trait Interactive: Element {
     {
         self.listeners()
             .mouse_down
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+            .push(Box::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
                     && !bounds.contains_point(&event.position)
@@ -94,7 +91,7 @@ pub trait Interactive: Element {
     {
         self.listeners()
             .mouse_up
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+            .push(Box::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
                     && !bounds.contains_point(&event.position)
@@ -117,7 +114,7 @@ pub trait Interactive: Element {
     {
         self.listeners()
             .mouse_move
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+            .push(Box::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
                 }
@@ -137,7 +134,7 @@ pub trait Interactive: Element {
     {
         self.listeners()
             .scroll_wheel
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+            .push(Box::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
                 }
@@ -159,60 +156,7 @@ pub trait Click: Interactive {
     {
         self.listeners()
             .mouse_click
-            .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
+            .push(Box::new(move |view, event, cx| handler(view, event, cx)));
         self
     }
 }
-
-type MouseDownHandler<V> = Arc<
-    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-type MouseUpHandler<V> = Arc<
-    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-type MouseClickHandler<V> =
-    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
-
-type MouseMoveHandler<V> = Arc<
-    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-type ScrollWheelHandler<V> = Arc<
-    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub struct MouseEventListeners<V: 'static> {
-    pub mouse_down: SmallVec<[MouseDownHandler<V>; 2]>,
-    pub mouse_up: SmallVec<[MouseUpHandler<V>; 2]>,
-    pub mouse_click: SmallVec<[MouseClickHandler<V>; 2]>,
-    pub mouse_move: SmallVec<[MouseMoveHandler<V>; 2]>,
-    pub scroll_wheel: SmallVec<[ScrollWheelHandler<V>; 2]>,
-}
-
-impl<V> Default for MouseEventListeners<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(),
-        }
-    }
-}
-
-pub struct MouseClickEvent {
-    pub down: MouseDownEvent,
-    pub up: MouseUpEvent,
-}

crates/gpui3/src/platform.rs 🔗

@@ -5,9 +5,9 @@ mod mac;
 mod test;
 
 use crate::{
-    AnyWindowHandle, Bounds, DevicePixels, Event, Executor, Font, FontId, FontMetrics, FontRun,
-    GlobalPixels, GlyphId, LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams,
-    RenderSvgParams, Result, Scene, SharedString, Size,
+    AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, FontRun,
+    GlobalPixels, GlyphId, InputEvent, LineLayout, Pixels, Point, RenderGlyphParams,
+    RenderImageParams, RenderSvgParams, Result, Scene, SharedString, Size,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -81,7 +81,7 @@ pub(crate) trait Platform: 'static {
     fn on_resign_active(&self, callback: Box<dyn FnMut()>);
     fn on_quit(&self, callback: Box<dyn FnMut()>);
     fn on_reopen(&self, callback: Box<dyn FnMut()>);
-    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
+    fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
 
     fn os_name(&self) -> &'static str;
     fn os_version(&self) -> Result<SemanticVersion>;
@@ -141,7 +141,7 @@ pub(crate) trait PlatformWindow {
     fn minimize(&self);
     fn zoom(&self);
     fn toggle_full_screen(&self);
-    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
+    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
     fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
     fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
     fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>);

crates/gpui3/src/platform/mac/events.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    point, px, Event, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
+    point, px, InputEvent, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
     MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection,
     Pixels, ScrollDelta, ScrollWheelEvent, TouchPhase,
 };
@@ -84,7 +84,7 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
     }
 }
 
-impl Event {
+impl InputEvent {
     pub unsafe fn from_native(native_event: id, window_height: Option<Pixels>) -> Option<Self> {
         let event_type = native_event.eventType();
 

crates/gpui3/src/platform/mac/platform.rs 🔗

@@ -1,6 +1,6 @@
 use super::BoolExt;
 use crate::{
-    AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Event, Executor, MacDispatcher,
+    AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Executor, InputEvent, MacDispatcher,
     MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, PathPromptOptions, Platform,
     PlatformDisplay, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp,
     WindowOptions,
@@ -153,7 +153,7 @@ pub struct MacPlatformState {
     resign_active: Option<Box<dyn FnMut()>>,
     reopen: Option<Box<dyn FnMut()>>,
     quit: Option<Box<dyn FnMut()>>,
-    event: Option<Box<dyn FnMut(Event) -> bool>>,
+    event: Option<Box<dyn FnMut(InputEvent) -> bool>>,
     // menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
     // validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
     will_open_menu: Option<Box<dyn FnMut()>>,
@@ -621,7 +621,7 @@ impl Platform for MacPlatform {
         self.0.lock().reopen = Some(callback);
     }
 
-    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
+    fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
         self.0.lock().event = Some(callback);
     }
 
@@ -937,7 +937,7 @@ unsafe fn get_foreground_platform(object: &mut Object) -> &MacPlatform {
 
 extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
     unsafe {
-        if let Some(event) = Event::from_native(native_event, None) {
+        if let Some(event) = InputEvent::from_native(native_event, None) {
             let platform = get_foreground_platform(this);
             if let Some(callback) = platform.0.lock().event.as_mut() {
                 if !callback(event) {

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -1,7 +1,7 @@
 use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
 use crate::{
-    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Event, Executor,
-    GlobalPixels, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
+    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, GlobalPixels,
+    InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
     MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
     PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance,
     WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
@@ -286,7 +286,7 @@ struct MacWindowState {
     renderer: MetalRenderer,
     scene_to_render: Option<Scene>,
     kind: WindowKind,
-    event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
+    event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
     activate_callback: Option<Box<dyn FnMut(bool)>>,
     resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
     fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
@@ -300,7 +300,7 @@ struct MacWindowState {
     synthetic_drag_counter: usize,
     last_fresh_keydown: Option<Keystroke>,
     traffic_light_position: Option<Point<Pixels>>,
-    previous_modifiers_changed_event: Option<Event>,
+    previous_modifiers_changed_event: Option<InputEvent>,
     // State tracking what the IME did after the last request
     ime_state: ImeState,
     // Retains the last IME Text
@@ -854,7 +854,7 @@ impl PlatformWindow for MacWindow {
             .detach();
     }
 
-    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
+    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
         self.0.as_ref().lock().event_callback = Some(callback);
     }
 
@@ -975,9 +975,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
     let mut lock = window_state.as_ref().lock();
 
     let window_height = lock.content_size().height;
-    let event = unsafe { Event::from_native(native_event, Some(window_height)) };
+    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
 
-    if let Some(Event::KeyDown(event)) = event {
+    if let Some(InputEvent::KeyDown(event)) = event {
         // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
         // If that event isn't handled, it will then dispatch a "key down" event. GPUI
         // makes no distinction between these two types of events, so we need to ignore
@@ -1045,13 +1045,13 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
                                 key: ime_text.clone().unwrap(),
                             },
                         };
-                        handled = callback(Event::KeyDown(event_with_ime_text));
+                        handled = callback(InputEvent::KeyDown(event_with_ime_text));
                     }
                     if !handled {
                         // empty key happens when you type a deadkey in input composition.
                         // (e.g. on a brazillian keyboard typing quote is a deadkey)
                         if !event.keystroke.key.is_empty() {
-                            handled = callback(Event::KeyDown(event));
+                            handled = callback(InputEvent::KeyDown(event));
                         }
                     }
                 }
@@ -1097,11 +1097,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
     let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
 
     let window_height = lock.content_size().height;
-    let event = unsafe { Event::from_native(native_event, Some(window_height)) };
+    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
 
     if let Some(mut event) = event {
         let synthesized_second_event = match &mut event {
-            Event::MouseDown(
+            InputEvent::MouseDown(
                 event @ MouseDownEvent {
                     button: MouseButton::Left,
                     modifiers: Modifiers { control: true, .. },
@@ -1118,7 +1118,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                     ..*event
                 };
 
-                Some(Event::MouseDown(MouseDownEvent {
+                Some(InputEvent::MouseDown(MouseDownEvent {
                     button: MouseButton::Right,
                     ..*event
                 }))
@@ -1127,7 +1127,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
             // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
             // the ctrl-left_up to avoid having a mismatch in button down/up events if the
             // user is still holding ctrl when releasing the left mouse button
-            Event::MouseUp(MouseUpEvent {
+            InputEvent::MouseUp(MouseUpEvent {
                 button: MouseButton::Left,
                 modifiers: Modifiers { control: true, .. },
                 ..
@@ -1140,7 +1140,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
         };
 
         match &event {
-            Event::MouseMoved(
+            InputEvent::MouseMoved(
                 event @ MouseMoveEvent {
                     pressed_button: Some(_),
                     ..
@@ -1157,18 +1157,18 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                     .detach();
             }
 
-            Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
+            InputEvent::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
 
-            Event::MouseUp(MouseUpEvent {
+            InputEvent::MouseUp(MouseUpEvent {
                 button: MouseButton::Left,
                 ..
             }) => {
                 lock.synthetic_drag_counter += 1;
             }
 
-            Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
+            InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
                 // Only raise modifiers changed event when they have actually changed
-                if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
+                if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent {
                     modifiers: prev_modifiers,
                 })) = &lock.previous_modifiers_changed_event
                 {
@@ -1204,7 +1204,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
         modifiers: Default::default(),
         key: ".".into(),
     };
-    let event = Event::KeyDown(KeyDownEvent {
+    let event = InputEvent::KeyDown(KeyDownEvent {
         keystroke: keystroke.clone(),
         is_held: false,
     });
@@ -1605,7 +1605,7 @@ async fn synthetic_drag(
             if lock.synthetic_drag_counter == drag_id {
                 if let Some(mut callback) = lock.event_callback.take() {
                     drop(lock);
-                    callback(Event::MouseMoved(event.clone()));
+                    callback(InputEvent::MouseMoved(event.clone()));
                     window_state.lock().event_callback = Some(callback);
                 }
             } else {

crates/gpui3/src/platform/test.rs 🔗

@@ -125,7 +125,7 @@ impl Platform for TestPlatform {
         unimplemented!()
     }
 
-    fn on_event(&self, _callback: Box<dyn FnMut(crate::Event) -> bool>) {
+    fn on_event(&self, _callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
         unimplemented!()
     }
 

crates/gpui3/src/view.rs 🔗

@@ -6,12 +6,12 @@ use crate::{
 };
 use std::{marker::PhantomData, sync::Arc};
 
-pub struct View<S: Send + Sync> {
-    state: Handle<S>,
-    render: Arc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S> + Send + Sync + 'static>,
+pub struct View<V: Send + Sync> {
+    state: Handle<V>,
+    render: Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + Sync + 'static>,
 }
 
-impl<S: 'static + Send + Sync> View<S> {
+impl<V: 'static + Send + Sync> View<V> {
     pub fn into_any(self) -> AnyView {
         AnyView {
             view: Arc::new(Mutex::new(self)),
@@ -19,7 +19,7 @@ impl<S: 'static + Send + Sync> View<S> {
     }
 }
 
-impl<S: Send + Sync> Clone for View<S> {
+impl<V: Send + Sync> Clone for View<V> {
     fn clone(&self) -> Self {
         Self {
             state: self.state.clone(),
@@ -28,13 +28,13 @@ impl<S: Send + Sync> Clone for View<S> {
     }
 }
 
-pub fn view<S, E>(
-    state: Handle<S>,
-    render: impl Fn(&mut S, &mut ViewContext<S>) -> E + Send + Sync + 'static,
-) -> View<S>
+pub fn view<V, E>(
+    state: Handle<V>,
+    render: impl Fn(&mut V, &mut ViewContext<V>) -> E + Send + Sync + 'static,
+) -> View<V>
 where
-    E: IntoAnyElement<S>,
-    S: 'static + Send + Sync,
+    E: IntoAnyElement<V>,
+    V: 'static + Send + Sync,
 {
     View {
         state,
@@ -42,8 +42,8 @@ where
     }
 }
 
-impl<S: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
-    IntoAnyElement<ParentViewState> for View<S>
+impl<V: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
+    IntoAnyElement<ParentViewState> for View<V>
 {
     fn into_any(self) -> AnyElement<ParentViewState> {
         AnyElement::new(EraseViewState {
@@ -53,74 +53,91 @@ impl<S: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
     }
 }
 
-impl<S: 'static + Send + Sync> Element for View<S> {
+impl<V: 'static + Send + Sync> Element for View<V> {
     type ViewState = ();
-    type ElementState = AnyElement<S>;
+    type ElementState = AnyElement<V>;
 
     fn id(&self) -> Option<crate::ElementId> {
         Some(ElementId::View(self.state.id))
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
-        _: &mut Self::ViewState,
+        _: &mut (),
         _: Option<Self::ElementState>,
-        cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState) {
+        cx: &mut ViewContext<()>,
+    ) -> Self::ElementState {
         self.state.update(cx, |state, cx| {
-            let mut element = (self.render)(state, cx);
-            let layout_id = element.layout(state, cx);
-            (layout_id, element)
+            let mut any_element = (self.render)(state, cx);
+            any_element.initialize(state, cx);
+            any_element
         })
     }
 
+    fn layout(
+        &mut self,
+        _: &mut (),
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<()>,
+    ) -> LayoutId {
+        self.state.update(cx, |state, cx| element.layout(state, cx))
+    }
+
     fn paint(
         &mut self,
         _: Bounds<Pixels>,
-        _: &mut Self::ViewState,
+        _: &mut (),
         element: &mut Self::ElementState,
-        cx: &mut ViewContext<Self::ViewState>,
+        cx: &mut ViewContext<()>,
     ) {
         self.state
             .update(cx, |state, cx| element.paint(state, None, cx))
     }
 }
 
-struct EraseViewState<ViewState: 'static + Send + Sync, ParentViewState> {
-    view: View<ViewState>,
-    parent_view_state_type: PhantomData<ParentViewState>,
+struct EraseViewState<V: 'static + Send + Sync, ParentV> {
+    view: View<V>,
+    parent_view_state_type: PhantomData<ParentV>,
 }
 
-impl<ViewState, ParentViewState> IntoAnyElement<ParentViewState>
-    for EraseViewState<ViewState, ParentViewState>
+impl<V, ParentV> IntoAnyElement<ParentV> for EraseViewState<V, ParentV>
 where
-    ViewState: 'static + Send + Sync,
-    ParentViewState: 'static + Send + Sync,
+    V: 'static + Send + Sync,
+    ParentV: 'static + Send + Sync,
 {
-    fn into_any(self) -> AnyElement<ParentViewState> {
+    fn into_any(self) -> AnyElement<ParentV> {
         AnyElement::new(self)
     }
 }
 
-impl<ViewState, ParentViewState> Element for EraseViewState<ViewState, ParentViewState>
+impl<V, ParentV> Element for EraseViewState<V, ParentV>
 where
-    ViewState: 'static + Send + Sync,
-    ParentViewState: 'static + Send + Sync,
+    V: 'static + Send + Sync,
+    ParentV: 'static + Send + Sync,
 {
-    type ViewState = ParentViewState;
+    type ViewState = ParentV;
     type ElementState = AnyBox;
 
     fn id(&self) -> Option<crate::ElementId> {
         Element::id(&self.view)
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
         _: &mut Self::ViewState,
         _: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState) {
-        ViewObject::layout(&mut self.view, cx)
+    ) -> Self::ElementState {
+        ViewObject::initialize(&mut self.view, cx)
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut Self::ViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> LayoutId {
+        ViewObject::layout(&mut self.view, element, cx)
     }
 
     fn paint(
@@ -136,22 +153,31 @@ where
 
 trait ViewObject: 'static + Send + Sync {
     fn entity_id(&self) -> EntityId;
-    fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox);
+    fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox;
+    fn layout(&mut self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId;
     fn paint(&mut self, bounds: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext);
 }
 
-impl<S: Send + Sync + 'static> ViewObject for View<S> {
+impl<V: Send + Sync + 'static> ViewObject for View<V> {
     fn entity_id(&self) -> EntityId {
         self.state.id
     }
 
-    fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox) {
+    fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox {
         cx.with_element_id(self.entity_id(), |cx| {
             self.state.update(cx, |state, cx| {
-                let mut element = (self.render)(state, cx);
-                let layout_id = element.layout(state, cx);
-                let element = Box::new(element) as AnyBox;
-                (layout_id, element)
+                let mut any_element = Box::new((self.render)(state, cx));
+                any_element.initialize(state, cx);
+                any_element as AnyBox
+            })
+        })
+    }
+
+    fn layout(&mut self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId {
+        cx.with_element_id(self.entity_id(), |cx| {
+            self.state.update(cx, |state, cx| {
+                let element = element.downcast_mut::<AnyElement<V>>().unwrap();
+                element.layout(state, cx)
             })
         })
     }
@@ -159,7 +185,7 @@ impl<S: Send + Sync + 'static> ViewObject for View<S> {
     fn paint(&mut self, _: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext) {
         cx.with_element_id(self.entity_id(), |cx| {
             self.state.update(cx, |state, cx| {
-                let element = element.downcast_mut::<AnyElement<S>>().unwrap();
+                let element = element.downcast_mut::<AnyElement<V>>().unwrap();
                 element.paint(state, None, cx);
             });
         });
@@ -170,11 +196,11 @@ pub struct AnyView {
     view: Arc<Mutex<dyn ViewObject>>,
 }
 
-impl<ParentViewState> IntoAnyElement<ParentViewState> for AnyView
+impl<ParentV> IntoAnyElement<ParentV> for AnyView
 where
-    ParentViewState: 'static + Send + Sync,
+    ParentV: 'static + Send + Sync,
 {
-    fn into_any(self) -> AnyElement<ParentViewState> {
+    fn into_any(self) -> AnyElement<ParentV> {
         AnyElement::new(EraseAnyViewState {
             view: self,
             parent_view_state_type: PhantomData,
@@ -190,13 +216,22 @@ impl Element for AnyView {
         Some(ElementId::View(self.view.lock().entity_id()))
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
         _: &mut Self::ViewState,
         _: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState) {
-        self.view.lock().layout(cx)
+    ) -> Self::ElementState {
+        self.view.lock().initialize(cx)
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut Self::ViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> LayoutId {
+        self.view.lock().layout(element, cx)
     }
 
     fn paint(
@@ -215,33 +250,42 @@ struct EraseAnyViewState<ParentViewState> {
     parent_view_state_type: PhantomData<ParentViewState>,
 }
 
-impl<ParentViewState> IntoAnyElement<ParentViewState> for EraseAnyViewState<ParentViewState>
+impl<ParentV> IntoAnyElement<ParentV> for EraseAnyViewState<ParentV>
 where
-    ParentViewState: 'static + Send + Sync,
+    ParentV: 'static + Send + Sync,
 {
-    fn into_any(self) -> AnyElement<ParentViewState> {
+    fn into_any(self) -> AnyElement<ParentV> {
         AnyElement::new(self)
     }
 }
 
-impl<ParentViewState> Element for EraseAnyViewState<ParentViewState>
+impl<ParentV> Element for EraseAnyViewState<ParentV>
 where
-    ParentViewState: 'static + Send + Sync,
+    ParentV: 'static + Send + Sync,
 {
-    type ViewState = ParentViewState;
+    type ViewState = ParentV;
     type ElementState = AnyBox;
 
     fn id(&self) -> Option<crate::ElementId> {
         Element::id(&self.view)
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
         _: &mut Self::ViewState,
         _: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
-    ) -> (LayoutId, Self::ElementState) {
-        self.view.view.lock().layout(cx)
+    ) -> Self::ElementState {
+        self.view.view.lock().initialize(cx)
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut Self::ViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> LayoutId {
+        self.view.view.lock().layout(element, cx)
     }
 
     fn paint(

crates/gpui3/src/window.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{
     px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext,
     Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId,
-    Event, EventEmitter, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, IsZero,
+    EventEmitter, FocusEvent, FocusListener, FontId, GlobalElementId, GlyphId, Handle, Hsla,
+    ImageData, InputEvent, IsZero, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener,
     LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform,
     PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
     RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
@@ -21,7 +22,7 @@ use std::{
     mem,
     sync::Arc,
 };
-use util::ResultExt;
+use util::{post_inc, ResultExt};
 
 #[derive(Deref, DerefMut, Ord, PartialOrd, Eq, PartialEq, Clone, Default)]
 pub struct StackingOrder(pub(crate) SmallVec<[u32; 16]>);
@@ -39,8 +40,55 @@ pub enum DispatchPhase {
     Capture,
 }
 
-type MouseEventHandler =
-    Arc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyMouseEventListener =
+    Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyKeyboardEventListener =
+    Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyFocusListener = Box<dyn Fn(&FocusEvent, &mut WindowContext) + Send + Sync + 'static>;
+type AnyKeyDownListener =
+    Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyKeyUpListener =
+    Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct FocusId(usize);
+
+#[derive(Clone, PartialEq, Eq)]
+pub struct FocusHandle {
+    pub(crate) id: FocusId,
+}
+
+impl FocusHandle {
+    pub(crate) fn new(id: FocusId) -> Self {
+        Self { id }
+    }
+
+    pub fn is_focused(&self, cx: &WindowContext) -> bool {
+        cx.window.focus == Some(self.id)
+    }
+
+    pub fn contains_focused(&self, cx: &WindowContext) -> bool {
+        cx.focused()
+            .map_or(false, |focused| self.contains(&focused, cx))
+    }
+
+    pub fn within_focused(&self, cx: &WindowContext) -> bool {
+        let focused = cx.focused();
+        focused.map_or(false, |focused| focused.contains(self, cx))
+    }
+
+    pub(crate) fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
+        let mut ancestor = Some(other.id);
+        while let Some(ancestor_id) = ancestor {
+            if self.id == ancestor_id {
+                return true;
+            } else {
+                ancestor = cx.window.focus_parents_by_child.get(&ancestor_id).copied();
+            }
+        }
+        false
+    }
+}
 
 pub struct Window {
     handle: AnyWindowHandle,
@@ -56,12 +104,19 @@ pub struct Window {
     element_states: HashMap<GlobalElementId, AnyBox>,
     z_index_stack: StackingOrder,
     content_mask_stack: Vec<ContentMask<Pixels>>,
-    mouse_event_handlers: HashMap<TypeId, Vec<(StackingOrder, MouseEventHandler)>>,
+    mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseEventListener)>>,
+    keyboard_listeners: HashMap<TypeId, Vec<AnyKeyboardEventListener>>,
+    focus_stack: Vec<FocusStackFrame>,
+    focus_parents_by_child: HashMap<FocusId, FocusId>,
+    pub(crate) focus_listeners: Vec<AnyFocusListener>,
     propagate_event: bool,
     mouse_position: Point<Pixels>,
     scale_factor: f32,
     pub(crate) scene_builder: SceneBuilder,
     pub(crate) dirty: bool,
+    pub(crate) last_blur: Option<Option<FocusId>>,
+    pub(crate) focus: Option<FocusId>,
+    next_focus_id: FocusId,
 }
 
 impl Window {
@@ -95,7 +150,7 @@ impl Window {
             }
         }));
 
-        platform_window.on_event({
+        platform_window.on_input({
             let cx = cx.to_async();
             Box::new(move |event| {
                 cx.update_window(handle, |cx| cx.dispatch_event(event))
@@ -120,12 +175,19 @@ impl Window {
             element_states: HashMap::default(),
             z_index_stack: StackingOrder(SmallVec::new()),
             content_mask_stack: Vec::new(),
-            mouse_event_handlers: HashMap::default(),
+            mouse_listeners: HashMap::default(),
+            keyboard_listeners: HashMap::default(),
+            focus_stack: Vec::new(),
+            focus_parents_by_child: HashMap::default(),
+            focus_listeners: Vec::new(),
             propagate_event: true,
             mouse_position,
             scale_factor,
             scene_builder: SceneBuilder::new(),
             dirty: true,
+            last_blur: None,
+            focus: None,
+            next_focus_id: FocusId(0),
         }
     }
 }
@@ -149,6 +211,12 @@ impl ContentMask<Pixels> {
     }
 }
 
+struct FocusStackFrame {
+    handle: FocusHandle,
+    key_down_listeners: SmallVec<[AnyKeyDownListener; 2]>,
+    key_up_listeners: SmallVec<[AnyKeyUpListener; 2]>,
+}
+
 pub struct WindowContext<'a, 'w> {
     app: Reference<'a, AppContext>,
     pub(crate) window: Reference<'w, Window>,
@@ -166,6 +234,43 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.dirty = true;
     }
 
+    pub fn focus_handle(&mut self) -> FocusHandle {
+        let id = FocusId(post_inc(&mut self.window.next_focus_id.0));
+        FocusHandle { id }
+    }
+
+    pub fn focused(&self) -> Option<FocusHandle> {
+        self.window.focus.map(|id| FocusHandle::new(id))
+    }
+
+    pub fn focus(&mut self, handle: &FocusHandle) {
+        if self.window.last_blur.is_none() {
+            self.window.last_blur = Some(self.window.focus);
+        }
+
+        let window_id = self.window.handle.id;
+        self.window.focus = Some(handle.id);
+        self.push_effect(Effect::FocusChanged {
+            window_id,
+            focused: Some(handle.id),
+        });
+        self.notify();
+    }
+
+    pub fn blur(&mut self) {
+        if self.window.last_blur.is_none() {
+            self.window.last_blur = Some(self.window.focus);
+        }
+
+        let window_id = self.window.handle.id;
+        self.window.focus = None;
+        self.push_effect(Effect::FocusChanged {
+            window_id,
+            focused: None,
+        });
+        self.notify();
+    }
+
     pub fn run_on_main<R>(
         &mut self,
         f: impl FnOnce(&mut MainThread<WindowContext<'_, '_>>) -> R + Send + 'static,
@@ -298,17 +403,30 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     ) {
         let order = self.window.z_index_stack.clone();
         self.window
-            .mouse_event_handlers
+            .mouse_listeners
             .entry(TypeId::of::<Event>())
             .or_default()
             .push((
                 order,
-                Arc::new(move |event: &dyn Any, phase, cx| {
+                Box::new(move |event: &dyn Any, phase, cx| {
                     handler(event.downcast_ref().unwrap(), phase, cx)
                 }),
             ))
     }
 
+    pub fn on_keyboard_event<Event: 'static>(
+        &mut self,
+        handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static,
+    ) {
+        self.window
+            .keyboard_listeners
+            .entry(TypeId::of::<Event>())
+            .or_default()
+            .push(Box::new(move |event: &dyn Any, phase, cx| {
+                handler(event.downcast_ref().unwrap(), phase, cx)
+            }))
+    }
+
     pub fn mouse_position(&self) -> Point<Pixels> {
         self.window.mouse_position
     }
@@ -628,7 +746,8 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             element_state: Option<AnyBox>,
             cx: &mut ViewContext<()>,
         ) -> AnyBox {
-            let (layout_id, mut element_state) = root_view.layout(&mut (), element_state, cx);
+            let mut element_state = root_view.initialize(&mut (), element_state, cx);
+            let layout_id = root_view.layout(&mut (), &mut element_state, cx);
             let available_space = cx.window.content_size.map(Into::into);
             cx.window
                 .layout_engine
@@ -645,21 +764,24 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         // reference during the upcoming frame.
         let window = &mut *self.window;
         mem::swap(&mut window.element_states, &mut window.prev_element_states);
-        self.window.element_states.clear();
+        window.element_states.clear();
 
         // Clear mouse event listeners, because elements add new element listeners
         // when the upcoming frame is painted.
-        self.window
-            .mouse_event_handlers
-            .values_mut()
-            .for_each(Vec::clear);
+        window.mouse_listeners.values_mut().for_each(Vec::clear);
+
+        // Clear focus state, because we determine what is focused when the new elements
+        // in the upcoming frame are initialized.
+        window.focus_listeners.clear();
+        window.keyboard_listeners.values_mut().for_each(Vec::clear);
+        window.focus_parents_by_child.clear();
     }
 
     fn end_frame(&mut self) {
         self.text_system().end_frame();
     }
 
-    fn dispatch_event(&mut self, event: Event) -> bool {
+    fn dispatch_event(&mut self, event: InputEvent) -> bool {
         if let Some(any_mouse_event) = event.mouse_event() {
             if let Some(MouseMoveEvent { position, .. }) = any_mouse_event.downcast_ref() {
                 self.window.mouse_position = *position;
@@ -667,7 +789,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
             if let Some(mut handlers) = self
                 .window
-                .mouse_event_handlers
+                .mouse_listeners
                 .remove(&any_mouse_event.type_id())
             {
                 // Because handlers may add other handlers, we sort every time.
@@ -698,15 +820,48 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 // Just in case any handlers added new handlers, which is weird, but possible.
                 handlers.extend(
                     self.window
-                        .mouse_event_handlers
+                        .mouse_listeners
                         .get_mut(&any_mouse_event.type_id())
                         .into_iter()
                         .flat_map(|handlers| handlers.drain(..)),
                 );
                 self.window
-                    .mouse_event_handlers
+                    .mouse_listeners
                     .insert(any_mouse_event.type_id(), handlers);
             }
+        } else if let Some(any_keyboard_event) = event.keyboard_event() {
+            if let Some(mut handlers) = self
+                .window
+                .keyboard_listeners
+                .remove(&any_keyboard_event.type_id())
+            {
+                for handler in &handlers {
+                    handler(any_keyboard_event, DispatchPhase::Capture, self);
+                    if !self.window.propagate_event {
+                        break;
+                    }
+                }
+
+                if self.window.propagate_event {
+                    for handler in handlers.iter().rev() {
+                        handler(any_keyboard_event, DispatchPhase::Bubble, self);
+                        if !self.window.propagate_event {
+                            break;
+                        }
+                    }
+                }
+
+                handlers.extend(
+                    self.window
+                        .keyboard_listeners
+                        .get_mut(&any_keyboard_event.type_id())
+                        .into_iter()
+                        .flat_map(|handlers| handlers.drain(..)),
+                );
+                self.window
+                    .keyboard_listeners
+                    .insert(any_keyboard_event.type_id(), handlers);
+            }
         }
 
         true
@@ -882,7 +1037,7 @@ impl<S> BorrowWindow for ViewContext<'_, '_, S> {
     }
 }
 
-impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
+impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
     fn mutable(app: &'a mut AppContext, window: &'w mut Window, entity_id: EntityId) -> Self {
         Self {
             window_cx: WindowContext::mutable(app, window),
@@ -891,7 +1046,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
         }
     }
 
-    pub fn handle(&self) -> WeakHandle<S> {
+    pub fn handle(&self) -> WeakHandle<V> {
         self.entities.weak_handle(self.entity_id)
     }
 
@@ -902,7 +1057,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
         result
     }
 
-    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut S, &mut ViewContext<S>) + Send + 'static) {
+    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut V, &mut ViewContext<V>) + Send + 'static) {
         let entity = self.handle();
         self.window_cx.on_next_frame(move |cx| {
             entity.update(cx, f).ok();
@@ -912,7 +1067,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
     pub fn observe<E: Send + Sync + 'static>(
         &mut self,
         handle: &Handle<E>,
-        on_notify: impl Fn(&mut S, Handle<E>, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
+        on_notify: impl Fn(&mut V, Handle<E>, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
     ) -> Subscription {
         let this = self.handle();
         let handle = handle.downgrade();
@@ -936,7 +1091,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
     pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
         &mut self,
         handle: &Handle<E>,
-        on_event: impl Fn(&mut S, Handle<E>, &E::Event, &mut ViewContext<'_, '_, S>)
+        on_event: impl Fn(&mut V, Handle<E>, &E::Event, &mut ViewContext<'_, '_, V>)
             + Send
             + Sync
             + 'static,
@@ -963,7 +1118,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
 
     pub fn on_release(
         &mut self,
-        on_release: impl Fn(&mut S, &mut WindowContext) + Send + Sync + 'static,
+        on_release: impl Fn(&mut V, &mut WindowContext) + Send + Sync + 'static,
     ) -> Subscription {
         let window_handle = self.window.handle;
         self.app.release_handlers.insert(
@@ -979,7 +1134,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
     pub fn observe_release<E: Send + Sync + 'static>(
         &mut self,
         handle: &Handle<E>,
-        on_release: impl Fn(&mut S, &mut E, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
+        on_release: impl Fn(&mut V, &mut E, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
     ) -> Subscription {
         let this = self.handle();
         let window_handle = self.window.handle;
@@ -1002,10 +1157,86 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
         });
     }
 
+    pub fn with_focus<R>(
+        &mut self,
+        focus_handle: Option<FocusHandle>,
+        key_down: impl IntoIterator<Item = KeyDownListener<V>>,
+        key_up: impl IntoIterator<Item = KeyUpListener<V>>,
+        focus: impl IntoIterator<Item = FocusListener<V>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        let Some(focus_handle) = focus_handle else {
+            return f(self);
+        };
+
+        let handle = self.handle();
+        let window = &mut *self.window;
+
+        for listener in focus {
+            let handle = handle.clone();
+            window.focus_listeners.push(Box::new(move |event, cx| {
+                handle
+                    .update(cx, |view, cx| listener(view, event, cx))
+                    .log_err();
+            }));
+        }
+
+        let mut focus_stack = mem::take(&mut window.focus_stack);
+        if let Some(parent_frame) = focus_stack.last() {
+            window
+                .focus_parents_by_child
+                .insert(focus_handle.id, parent_frame.handle.id);
+        }
+
+        let mut frame = FocusStackFrame {
+            handle: focus_handle.clone(),
+            key_down_listeners: SmallVec::new(),
+            key_up_listeners: SmallVec::new(),
+        };
+
+        for listener in key_down {
+            let handle = handle.clone();
+            frame
+                .key_down_listeners
+                .push(Box::new(move |event, phase, cx| {
+                    handle
+                        .update(cx, |view, cx| listener(view, event, phase, cx))
+                        .log_err();
+                }));
+        }
+        for listener in key_up {
+            let handle = handle.clone();
+            frame
+                .key_up_listeners
+                .push(Box::new(move |event, phase, cx| {
+                    handle
+                        .update(cx, |view, cx| listener(view, event, phase, cx))
+                        .log_err();
+                }));
+        }
+        focus_stack.push(frame);
+
+        if Some(focus_handle.id) == window.focus {
+            for focus_frame in &mut focus_stack {
+                for listener in focus_frame.key_down_listeners.drain(..) {
+                    self.window_cx.on_keyboard_event(listener);
+                }
+                for listener in focus_frame.key_up_listeners.drain(..) {
+                    self.window_cx.on_keyboard_event(listener);
+                }
+            }
+        }
+
+        self.window.focus_stack = focus_stack;
+        let result = f(self);
+        self.window.focus_stack.pop();
+        result
+    }
+
     pub fn run_on_main<R>(
         &mut self,
-        view: &mut S,
-        f: impl FnOnce(&mut S, &mut MainThread<ViewContext<'_, '_, S>>) -> R + Send + 'static,
+        view: &mut V,
+        f: impl FnOnce(&mut V, &mut MainThread<ViewContext<'_, '_, V>>) -> R + Send + 'static,
     ) -> Task<Result<R>>
     where
         R: Send + 'static,
@@ -1021,7 +1252,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
 
     pub fn spawn<Fut, R>(
         &mut self,
-        f: impl FnOnce(WeakHandle<S>, AsyncWindowContext) -> Fut + Send + 'static,
+        f: impl FnOnce(WeakHandle<V>, AsyncWindowContext) -> Fut + Send + 'static,
     ) -> Task<R>
     where
         R: Send + 'static,
@@ -1036,7 +1267,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
 
     pub fn on_mouse_event<Event: 'static>(
         &mut self,
-        handler: impl Fn(&mut S, &Event, DispatchPhase, &mut ViewContext<S>) + Send + Sync + 'static,
+        handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
     ) {
         let handle = self.handle().upgrade(self).unwrap();
         self.window_cx.on_mouse_event(move |event, phase, cx| {
@@ -1045,6 +1276,18 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
             })
         });
     }
+
+    pub fn on_keyboard_event<Event: 'static>(
+        &mut self,
+        handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
+    ) {
+        let handle = self.handle().upgrade(self).unwrap();
+        self.window_cx.on_keyboard_event(move |event, phase, cx| {
+            handle.update(cx, |view, cx| {
+                handler(view, event, phase, cx);
+            })
+        });
+    }
 }
 
 impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {

crates/gpui3_macros/src/derive_element.rs 🔗

@@ -57,27 +57,36 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
                 None
             }
 
-            fn layout(
+            fn initialize(
                 &mut self,
                 view_state: &mut Self::ViewState,
-                element_state: Option<Self::ElementState>,
-                cx: &mut gpui3::ViewContext<Self::ViewState>,
-            ) -> (gpui3::LayoutId, Self::ElementState) {
+                _: Option<Self::ElementState>,
+                cx: &mut gpui3::ViewContext<Self::ViewState>
+            ) -> Self::ElementState {
                 use gpui3::IntoAnyElement;
 
-                let mut rendered_element = self.render(view_state, cx).into_any();
-                let layout_id = rendered_element.layout(view_state, cx);
-                (layout_id, rendered_element)
+                let mut element = self.render(view_state, cx).into_any();
+                element.initialize(view_state, cx);
+                element
+            }
+
+            fn layout(
+                &mut self,
+                view_state: &mut Self::ViewState,
+                rendered_element: &mut Self::ElementState,
+                cx: &mut gpui3::ViewContext<Self::ViewState>,
+            ) -> gpui3::LayoutId {
+                rendered_element.layout(view_state, cx)
             }
 
             fn paint(
                 &mut self,
                 bounds: gpui3::Bounds<gpui3::Pixels>,
                 view_state: &mut Self::ViewState,
-                element_state: &mut Self::ElementState,
+                rendered_element: &mut Self::ElementState,
                 cx: &mut gpui3::ViewContext<Self::ViewState>,
             ) {
-                element_state.paint(view_state, None, cx)
+                rendered_element.paint(view_state, None, cx)
             }
         }
     };

crates/ui2/src/theme.rs 🔗

@@ -164,31 +164,42 @@ impl<E: Element> Element for Themed<E> {
         None
     }
 
-    fn layout(
+    fn initialize(
         &mut self,
-        state: &mut E::ViewState,
+        view_state: &mut Self::ViewState,
         element_state: Option<Self::ElementState>,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> Self::ElementState {
+        cx.with_global(self.theme.clone(), |cx| {
+            self.child.initialize(view_state, element_state, cx)
+        })
+    }
+
+    fn layout(
+        &mut self,
+        view_state: &mut E::ViewState,
+        element_state: &mut Self::ElementState,
         cx: &mut ViewContext<E::ViewState>,
-    ) -> (LayoutId, Self::ElementState)
+    ) -> LayoutId
     where
         Self: Sized,
     {
         cx.with_global(self.theme.clone(), |cx| {
-            self.child.layout(state, element_state, cx)
+            self.child.layout(view_state, element_state, cx)
         })
     }
 
     fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
-        state: &mut Self::ViewState,
+        view_state: &mut Self::ViewState,
         frame_state: &mut Self::ElementState,
         cx: &mut ViewContext<Self::ViewState>,
     ) where
         Self: Sized,
     {
         cx.with_global(self.theme.clone(), |cx| {
-            self.child.paint(bounds, state, frame_state, cx);
+            self.child.paint(bounds, view_state, frame_state, cx);
         });
     }
 }