From ad1b96720a24fd3fc5b5fa0632c19f40bfcf5654 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 13:36:26 +0200 Subject: [PATCH 01/15] Checkpoint --- crates/gpui3/src/app/entity_map.rs | 10 ++ crates/gpui3/src/element.rs | 37 ++++- crates/gpui3/src/elements/div.rs | 232 ++++++++++++++++++----------- crates/gpui3/src/elements/img.rs | 22 +-- crates/gpui3/src/elements/svg.rs | 22 +-- crates/gpui3/src/interactive.rs | 41 +++-- crates/gpui3/src/window.rs | 122 ++++++++++++--- 7 files changed, 342 insertions(+), 144 deletions(-) diff --git a/crates/gpui3/src/app/entity_map.rs b/crates/gpui3/src/app/entity_map.rs index ce111c019debd614eafad6ff4620f10868dbbd9f..a756fe0bc86082b890ac5b4629d1aabae2fc3cb5 100644 --- a/crates/gpui3/src/app/entity_map.rs +++ b/crates/gpui3/src/app/entity_map.rs @@ -199,6 +199,16 @@ pub struct WeakHandle { entity_map: Weak>, } +impl Clone for WeakHandle { + fn clone(&self) -> Self { + Self { + id: self.id, + entity_type: self.entity_type, + entity_map: self.entity_map.clone(), + } + } +} + impl WeakHandle { pub fn upgrade(&self, _: &impl Context) -> Option> { let entity_map = &self.entity_map.upgrade()?; diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index b9c42721d17c7c24e3842ab5e48bc54edd5424f8..888010dc065efd91cb03c860c27b48c3794086fb 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,4 +1,4 @@ -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; @@ -31,21 +31,48 @@ pub trait ElementIdentity: 'static + Send + Sync { fn id(&self) -> Option; } -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 { Some(self.0.clone()) } } -impl ElementIdentity for AnonymousElement { +pub struct Anonymous; + +impl ElementIdentity for Anonymous { fn id(&self) -> Option { None } } +pub trait ElementFocusability: 'static + Send + Sync { + fn focus_handle(&self) -> Option<&FocusHandle>; +} + +pub struct Focusable(FocusHandle); + +impl ElementFocusability for Focusable { + fn focus_handle(&self) -> Option<&FocusHandle> { + Some(&self.0) + } +} + +impl From 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; 2]>; diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index d110d811320e1b462259b3bb4e99209ea1deeec5..78743f70891578444db82baff8c223bd07087bb0 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,9 +1,9 @@ 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, FocusHandle, Focusable, Hover, + Identified, Interactive, IntoAnyElement, KeyDownEvent, LayoutId, MouseClickEvent, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, + Point, ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, ViewContext, }; use collections::HashMap; use parking_lot::Mutex; @@ -60,12 +60,13 @@ impl ScrollState { } } -pub fn div() -> Div +pub fn div() -> Div 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,13 @@ where group_hover: None, active_style: StyleRefinement::default(), group_active: None, - listeners: MouseEventListeners::default(), + listeners: EventListeners::default(), } } -pub struct Div { - kind: K, +pub struct Div { + identity: I, + focusability: F, children: SmallVec<[AnyElement; 2]>, group: Option, base_style: StyleRefinement, @@ -86,7 +88,7 @@ pub struct Div group_hover: Option, active_style: StyleRefinement, group_active: Option, - listeners: MouseEventListeners, + listeners: EventListeners, } struct GroupStyle { @@ -94,13 +96,15 @@ struct GroupStyle { style: StyleRefinement, } -impl Div +impl Div where + F: ElementFocusability, V: 'static + Send + Sync, { - pub fn id(self, id: impl Into) -> Div { + pub fn id(self, id: impl Into) -> Div { Div { - kind: IdentifiedElement(id.into()), + identity: Identified(id.into()), + focusability: self.focusability, children: self.children, group: self.group, base_style: self.base_style, @@ -113,10 +117,11 @@ where } } -impl Div +impl Div where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { pub fn group(mut self, group: impl Into) -> Self { self.group = Some(group.into()); @@ -313,16 +318,55 @@ where } } -impl Element for Div +impl Div +where + I: ElementIdentity, + V: 'static + Send + Sync, +{ + pub fn focusable(self, handle: &FocusHandle) -> Div { + 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, + listeners: self.listeners, + } + } +} + +impl Div where + I: ElementIdentity, + V: 'static + Send + Sync, +{ + pub fn on_key_down( + mut self, + listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self { + self.listeners.key_down.push(Arc::new(listener)); + self + } +} + +impl Element for Div +where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { type ViewState = V; type ElementState = DivState; fn id(&self) -> Option { - self.kind.id() + self.identity.id() } fn layout( @@ -355,105 +399,121 @@ where cx: &mut ViewContext, ) { self.with_element_id(cx, |this, cx| { - if let Some(group) = this.group.clone() { - cx.default_global::() - .0 - .entry(group) - .or_default() - .push(bounds); - } - - let hover_group_bounds = this - .group_hover - .as_ref() - .and_then(|group_hover| group_bounds(&group_hover.group, cx)); - let active_group_bounds = this - .group_active - .as_ref() - .and_then(|group_active| group_bounds(&group_active.group, cx)); - let style = this.compute_style(bounds, element_state, cx); - let z_index = style.z_index.unwrap_or(0); - - // Paint background and event handlers. - cx.stack(z_index, |cx| { - cx.stack(0, |cx| { - style.paint(bounds, cx); - this.paint_hover_listeners(bounds, hover_group_bounds, cx); - this.paint_active_listener( - bounds, - active_group_bounds, - element_state.active_state.clone(), - cx, - ); - this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx); - }); - - cx.stack(1, |cx| { - style.apply_text_style(cx, |cx| { - style.apply_overflow(bounds, cx, |cx| { - for child in &mut this.children { - child.paint(view_state, None, cx); - } - }) - }) - }); - }); + cx.with_key_listeners( + this.focusability.focus_handle().cloned(), + this.listeners.key_down.clone(), + this.listeners.key_up.clone(), + |cx| { + if let Some(group) = this.group.clone() { + cx.default_global::() + .0 + .entry(group) + .or_default() + .push(bounds); + } - if let Some(group) = this.group.as_ref() { - cx.default_global::() - .0 - .get_mut(group) - .unwrap() - .pop(); - } + let hover_group_bounds = this + .group_hover + .as_ref() + .and_then(|group_hover| group_bounds(&group_hover.group, cx)); + let active_group_bounds = this + .group_active + .as_ref() + .and_then(|group_active| group_bounds(&group_active.group, cx)); + let style = this.compute_style(bounds, element_state, cx); + let z_index = style.z_index.unwrap_or(0); + + // Paint background and event handlers. + cx.stack(z_index, |cx| { + cx.stack(0, |cx| { + style.paint(bounds, cx); + this.paint_hover_listeners(bounds, hover_group_bounds, cx); + this.paint_active_listener( + bounds, + active_group_bounds, + element_state.active_state.clone(), + cx, + ); + this.paint_event_listeners( + bounds, + element_state.pending_click.clone(), + cx, + ); + }); + + cx.stack(1, |cx| { + style.apply_text_style(cx, |cx| { + style.apply_overflow(bounds, cx, |cx| { + for child in &mut this.children { + child.paint(view_state, None, cx); + } + }) + }) + }); + }); + + if let Some(group) = this.group.as_ref() { + cx.default_global::() + .0 + .get_mut(group) + .unwrap() + .pop(); + } + }, + ) }) } } -impl IntoAnyElement for Div +impl IntoAnyElement for Div where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl ParentElement for Div +impl ParentElement for Div where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { &mut self.children } } -impl Styled for Div +impl Styled for Div where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { fn style(&mut self) -> &mut StyleRefinement { &mut self.base_style } } -impl Interactive for Div +impl Interactive for Div where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { - fn listeners(&mut self) -> &mut MouseEventListeners { + fn listeners(&mut self) -> &mut EventListeners { &mut self.listeners } } -impl Hover for Div +impl Hover for Div where + I: ElementIdentity, + F: ElementFocusability, V: 'static + Send + Sync, - K: ElementIdentity, { fn set_hover_style(&mut self, group: Option, style: StyleRefinement) { if let Some(group) = group { @@ -464,10 +524,16 @@ where } } -impl Click for Div where V: 'static + Send + Sync {} +impl Click for Div +where + F: ElementFocusability, + V: 'static + Send + Sync, +{ +} -impl Active for Div +impl Active for Div where + F: ElementFocusability, V: 'static + Send + Sync, { fn set_active_style(&mut self, group: Option, style: StyleRefinement) { diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index e7775fc4347d1df49995af5affd06e7cecda4c08..e1e93fdd947a16deaafa7da82a748333d2579259 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -1,18 +1,18 @@ 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, + ElementId, ElementIdentity, EventListeners, Hover, Identified, Interactive, IntoAnyElement, + LayoutId, NonFocusable, Pixels, SharedString, StyleRefinement, Styled, ViewContext, }; use futures::FutureExt; use util::ResultExt; -pub struct Img { - base: Div, +pub struct Img { + base: Div, uri: Option, grayscale: bool, } -pub fn img() -> Img +pub fn img() -> Img where V: 'static + Send + Sync, { @@ -39,8 +39,8 @@ where } } -impl Img { - pub fn id(self, id: impl Into) -> Img { +impl Img { + pub fn id(self, id: impl Into) -> Img { Img { base: self.base.id(id), uri: self.uri, @@ -136,7 +136,7 @@ where V: 'static + Send + Sync, K: ElementIdentity, { - fn listeners(&mut self) -> &mut MouseEventListeners { + fn listeners(&mut self) -> &mut EventListeners { self.base.listeners() } } @@ -151,9 +151,9 @@ where } } -impl Click for Img where V: 'static + Send + Sync {} +impl Click for Img where V: 'static + Send + Sync {} -impl Active for Img +impl Active for Img where V: 'static + Send + Sync, { diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index 7904c2c7299d05d80ae95a3e52cff7bf2216c87f..aec1164295c83f8dfbe9398282e83aec83d76b69 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -1,16 +1,16 @@ 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, ElementId, + ElementIdentity, EventListeners, Hover, Identified, Interactive, IntoAnyElement, LayoutId, + NonFocusable, Pixels, SharedString, StyleRefinement, Styled, }; use util::ResultExt; -pub struct Svg { - base: Div, +pub struct Svg { + base: Div, path: Option, } -pub fn svg() -> Svg +pub fn svg() -> Svg where V: 'static + Send + Sync, { @@ -31,8 +31,8 @@ where } } -impl Svg { - pub fn id(self, id: impl Into) -> Svg { +impl Svg { + pub fn id(self, id: impl Into) -> Svg { Svg { base: self.base.id(id), path: self.path, @@ -110,7 +110,7 @@ where V: 'static + Send + Sync, K: ElementIdentity, { - fn listeners(&mut self) -> &mut MouseEventListeners { + fn listeners(&mut self) -> &mut EventListeners { self.base.listeners() } } @@ -125,9 +125,9 @@ where } } -impl Click for Svg where V: 'static + Send + Sync {} +impl Click for Svg where V: 'static + Send + Sync {} -impl Active for Svg +impl Active for Svg where V: 'static + Send + Sync, { diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index bc8976439a0e6e8f18915bd6c6c9eb9f873920d5..35bcab15a22ba94539ff1609a2e136163231398b 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,13 +1,13 @@ use smallvec::SmallVec; use crate::{ - Bounds, DispatchPhase, Element, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, - Pixels, ScrollWheelEvent, ViewContext, + Bounds, DispatchPhase, Element, KeyDownEvent, KeyUpEvent, MouseButton, MouseDownEvent, + MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ViewContext, }; use std::sync::Arc; pub trait Interactive: Element { - fn listeners(&mut self) -> &mut MouseEventListeners; + fn listeners(&mut self) -> &mut EventListeners; fn on_mouse_down( mut self, @@ -164,43 +164,52 @@ pub trait Click: Interactive { } } -type MouseDownHandler = Arc< +type MouseDownListener = Arc< dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -type MouseUpHandler = Arc< +type MouseUpListener = Arc< dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -type MouseClickHandler = +type MouseClickListener = Arc) + Send + Sync + 'static>; -type MouseMoveHandler = Arc< +type MouseMoveListener = Arc< dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -type ScrollWheelHandler = Arc< + +type ScrollWheelListener = Arc< dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -pub struct MouseEventListeners { - pub mouse_down: SmallVec<[MouseDownHandler; 2]>, - pub mouse_up: SmallVec<[MouseUpHandler; 2]>, - pub mouse_click: SmallVec<[MouseClickHandler; 2]>, - pub mouse_move: SmallVec<[MouseMoveHandler; 2]>, - pub scroll_wheel: SmallVec<[ScrollWheelHandler; 2]>, +pub type KeyDownListener = + Arc) + Send + Sync + 'static>; + +pub type KeyUpListener = + Arc) + Send + Sync + 'static>; + +pub struct EventListeners { + pub mouse_down: SmallVec<[MouseDownListener; 2]>, + pub mouse_up: SmallVec<[MouseUpListener; 2]>, + pub mouse_click: SmallVec<[MouseClickListener; 2]>, + pub mouse_move: SmallVec<[MouseMoveListener; 2]>, + pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, + pub key_down: SmallVec<[KeyDownListener; 2]>, + pub key_up: SmallVec<[KeyUpListener; 2]>, } -impl Default for MouseEventListeners { +impl Default for EventListeners { fn default() -> Self { Self { mouse_down: SmallVec::new(), @@ -208,6 +217,8 @@ impl Default for MouseEventListeners { mouse_click: SmallVec::new(), mouse_move: SmallVec::new(), scroll_wheel: SmallVec::new(), + key_down: SmallVec::new(), + key_up: SmallVec::new(), } } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 7ca09e3698887725b1312badaef9dcc570126c2b..17687f3597eb24fdb1fe0e0ab62571b51355247e 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -2,11 +2,11 @@ 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, - LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, - PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, - RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, - WindowOptions, SUBPIXEL_VARIANTS, + 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, Style, Subscription, TaffyLayoutEngine, + Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use collections::HashMap; @@ -21,7 +21,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]>); @@ -42,6 +42,14 @@ pub enum DispatchPhase { type MouseEventHandler = Arc; +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct FocusId(usize); + +#[derive(Clone)] +pub struct FocusHandle { + id: FocusId, +} + pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, @@ -57,11 +65,18 @@ pub struct Window { z_index_stack: StackingOrder, content_mask_stack: Vec>, mouse_event_handlers: HashMap>, + key_down_listener_stack: + Vec>, + key_up_listener_stack: + Vec>, propagate_event: bool, mouse_position: Point, scale_factor: f32, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, + focus: Option, + next_focus_id: FocusId, + painted_focused_element: bool, } impl Window { @@ -121,11 +136,16 @@ impl Window { z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), mouse_event_handlers: HashMap::default(), + key_down_listener_stack: Vec::new(), + key_up_listener_stack: Vec::new(), propagate_event: true, mouse_position, scale_factor, scene_builder: SceneBuilder::new(), dirty: true, + focus: None, + next_focus_id: FocusId(0), + painted_focused_element: false, } } } @@ -166,6 +186,11 @@ 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 run_on_main( &mut self, f: impl FnOnce(&mut MainThread>) -> R + Send + 'static, @@ -645,14 +670,16 @@ 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 + window .mouse_event_handlers .values_mut() .for_each(Vec::clear); + + window.painted_focused_element = false; } fn end_frame(&mut self) { @@ -882,7 +909,7 @@ impl 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 +918,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { } } - pub fn handle(&self) -> WeakHandle { + pub fn handle(&self) -> WeakHandle { self.entities.weak_handle(self.entity_id) } @@ -902,7 +929,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) + Send + 'static) { + pub fn on_next_frame(&mut self, f: impl FnOnce(&mut V, &mut ViewContext) + Send + 'static) { let entity = self.handle(); self.window_cx.on_next_frame(move |cx| { entity.update(cx, f).ok(); @@ -912,7 +939,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { pub fn observe( &mut self, handle: &Handle, - on_notify: impl Fn(&mut S, Handle, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static, + on_notify: impl Fn(&mut V, Handle, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static, ) -> Subscription { let this = self.handle(); let handle = handle.downgrade(); @@ -936,7 +963,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { pub fn subscribe( &mut self, handle: &Handle, - on_event: impl Fn(&mut S, Handle, &E::Event, &mut ViewContext<'_, '_, S>) + on_event: impl Fn(&mut V, Handle, &E::Event, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static, @@ -963,7 +990,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 +1006,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { pub fn observe_release( &mut self, handle: &Handle, - 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 +1029,67 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { }); } + pub fn with_key_listeners( + &mut self, + focus_handle: Option, + key_down: impl IntoIterator>, + key_up: impl IntoIterator>, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + let Some(focus_handle) = focus_handle else { + return f(self); + }; + let Some(focused_id) = self.window.focus else { + return f(self); + }; + if self.window.painted_focused_element { + return f(self); + } + + let prev_key_down_len = self.window.key_down_listener_stack.len(); + let prev_key_up_len = self.window.key_up_listener_stack.len(); + + for listener in key_down { + let handle = self.handle(); + self.window + .key_down_listener_stack + .push(Arc::new(move |event, phase, cx| { + handle + .update(cx, |view, cx| listener(view, event, phase, cx)) + .log_err(); + })); + } + for listener in key_up { + let handle = self.handle(); + self.window + .key_up_listener_stack + .push(Arc::new(move |event, phase, cx| { + handle + .update(cx, |view, cx| listener(view, event, phase, cx)) + .log_err(); + })); + } + + if focus_handle.id == focused_id { + self.window.painted_focused_element = true; + } + + let result = f(self); + + if focus_handle.id != focused_id { + self.window + .key_down_listener_stack + .truncate(prev_key_down_len); + self.window.key_up_listener_stack.truncate(prev_key_up_len); + } + + result + } + pub fn run_on_main( &mut self, - view: &mut S, - f: impl FnOnce(&mut S, &mut MainThread>) -> R + Send + 'static, + view: &mut V, + f: impl FnOnce(&mut V, &mut MainThread>) -> R + Send + 'static, ) -> Task> where R: Send + 'static, @@ -1021,7 +1105,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { pub fn spawn( &mut self, - f: impl FnOnce(WeakHandle, AsyncWindowContext) -> Fut + Send + 'static, + f: impl FnOnce(WeakHandle, AsyncWindowContext) -> Fut + Send + 'static, ) -> Task where R: Send + 'static, @@ -1036,7 +1120,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { pub fn on_mouse_event( &mut self, - handler: impl Fn(&mut S, &Event, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, + handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, ) { let handle = self.handle().upgrade(self).unwrap(); self.window_cx.on_mouse_event(move |event, phase, cx| { From a61b34cab5f71342f15e069a5bc31126d39bd2d4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 14:12:50 +0200 Subject: [PATCH 02/15] Checkpoint --- crates/gpui3/src/element.rs | 165 ++++++++++++++++-------------- crates/gpui3/src/elements/div.rs | 23 +++-- crates/gpui3/src/elements/img.rs | 22 ++-- crates/gpui3/src/elements/svg.rs | 26 +++-- crates/gpui3/src/elements/text.rs | 38 ++++--- crates/gpui3/src/view.rs | 165 ++++++++++++++++++------------ crates/gpui3/src/window.rs | 3 +- 7 files changed, 263 insertions(+), 179 deletions(-) diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 888010dc065efd91cb03c860c27b48c3794086fb..2d81e406913351bbfd968ed9a1516fa11885cec6 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,6 +1,7 @@ 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 { type ViewState: 'static + Send + Sync; @@ -8,17 +9,24 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement { fn id(&self) -> Option; - fn layout( + fn initialize( &mut self, - state: &mut Self::ViewState, + view_state: &mut Self::ViewState, element_state: Option, cx: &mut ViewContext, - ) -> (LayoutId, Self::ElementState); + ) -> Self::ElementState; + + fn layout( + &mut self, + view_state: &mut Self::ViewState, + element_state: &mut Self::ElementState, + cx: &mut ViewContext, + ) -> LayoutId; fn paint( &mut self, bounds: Bounds, - state: &mut Self::ViewState, + view_state: &mut Self::ViewState, element_state: &mut Self::ElementState, cx: &mut ViewContext, ); @@ -97,9 +105,10 @@ pub trait ParentElement: Element { } } -trait ElementObject: 'static + Send + Sync { - fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> LayoutId; - fn paint(&mut self, state: &mut S, offset: Option>, cx: &mut ViewContext); +trait ElementObject: 'static + Send + Sync { + fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext); + fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext) -> LayoutId; + fn paint(&mut self, view_state: &mut V, offset: Option>, cx: &mut ViewContext); } struct RenderedElement { @@ -108,17 +117,17 @@ struct RenderedElement { } #[derive(Default)] -enum ElementRenderPhase { +enum ElementRenderPhase { #[default] - Rendered, + Start, + Initialized { + frame_state: Option, + }, LayoutRequested { layout_id: LayoutId, - frame_state: Option, - }, - Painted { - bounds: Bounds, - frame_state: Option, + frame_state: Option, }, + Painted, } /// Internal struct that wraps an element to store Layout and ElementState after the element is rendered. @@ -128,52 +137,57 @@ impl RenderedElement { fn new(element: E) -> Self { RenderedElement { element, - phase: ElementRenderPhase::Rendered, + phase: ElementRenderPhase::Start, } } +} - fn paint_with_element_state( - &mut self, - bounds: Bounds, - view_state: &mut E::ViewState, - frame_state: &mut Option, - cx: &mut ViewContext, - ) { - if let Some(id) = self.element.id() { +impl ElementObject for RenderedElement +where + E: Element, +{ + fn initialize(&mut self, view_state: &mut E::ViewState, cx: &mut ViewContext) { + 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 ElementObject for RenderedElement -where - E: Element, - S: 'static + Send + Sync, -{ fn layout(&mut self, state: &mut E::ViewState, cx: &mut ViewContext) -> 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 } @@ -183,60 +197,63 @@ where offset: Option>, cx: &mut ViewContext, ) { - 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(Box>); +pub struct AnyElement(Box>); -impl AnyElement { - pub fn new>(element: E) -> Self { +impl AnyElement { + pub fn new>(element: E) -> Self { AnyElement(Box::new(RenderedElement::new(element))) } -} -impl AnyElement { - pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> LayoutId { - self.0.layout(state, cx) + pub fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext) { + self.0.initialize(view_state, cx); } - pub fn paint(&mut self, state: &mut S, offset: Option>, cx: &mut ViewContext) { - self.0.paint(state, offset, cx) + pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext) -> LayoutId { + self.0.layout(view_state, cx) + } + + pub fn paint( + &mut self, + view_state: &mut V, + offset: Option>, + cx: &mut ViewContext, + ) { + self.0.paint(view_state, offset, cx) } } -pub trait IntoAnyElement { - fn into_any(self) -> AnyElement; +pub trait IntoAnyElement { + fn into_any(self) -> AnyElement; } -impl IntoAnyElement for AnyElement { - fn into_any(self) -> AnyElement { +impl IntoAnyElement for AnyElement { + fn into_any(self) -> AnyElement { self } } diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 78743f70891578444db82baff8c223bd07087bb0..552c27686ed3e15488f04439c226080a7db17050 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -369,14 +369,25 @@ where self.identity.id() } - fn layout( + fn initialize( &mut self, view_state: &mut Self::ViewState, element_state: Option, cx: &mut ViewContext, - ) -> (LayoutId, Self::ElementState) { - let element_state = element_state.unwrap_or_default(); - let style = self.compute_style(Bounds::default(), &element_state, cx); + ) -> Self::ElementState { + 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, + ) -> 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 @@ -384,9 +395,7 @@ where .iter_mut() .map(|child| child.layout(view_state, cx)) .collect::>(); - - let layout_id = cx.request_layout(&style, layout_ids); - (layout_id, element_state) + cx.request_layout(&style, layout_ids) }) }) } diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index e1e93fdd947a16deaafa7da82a748333d2579259..261062a5b942035c0ae3b0694791c00f2de6ab71 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -71,24 +71,30 @@ where self.base.id() } - fn layout( + fn initialize( &mut self, - view_state: &mut Self::ViewState, + view_state: &mut V, element_state: Option, + cx: &mut ViewContext, + ) -> 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, - ) -> (LayoutId, Self::ElementState) - where - Self: Sized, - { + ) -> LayoutId { self.base.layout(view_state, element_state, cx) } fn paint( &mut self, bounds: Bounds, - view: &mut Self::ViewState, + view: &mut V, element_state: &mut Self::ElementState, - cx: &mut ViewContext, + cx: &mut ViewContext, ) { cx.stack(0, |cx| { self.base.paint(bounds, view, element_state, cx); diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index aec1164295c83f8dfbe9398282e83aec83d76b69..f0d6956503aee723f4c6c9c51c675c61d41f313e 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -1,7 +1,7 @@ use crate::{ div, Active, Anonymous, AnyElement, Bounds, Click, Div, DivState, Element, ElementId, ElementIdentity, EventListeners, Hover, Identified, Interactive, IntoAnyElement, LayoutId, - NonFocusable, Pixels, SharedString, StyleRefinement, Styled, + NonFocusable, Pixels, SharedString, StyleRefinement, Styled, ViewContext, }; use util::ResultExt; @@ -62,16 +62,22 @@ where self.base.id() } - fn layout( + fn initialize( &mut self, - view: &mut V, + view_state: &mut V, element_state: Option, - cx: &mut crate::ViewContext, - ) -> (LayoutId, Self::ElementState) - where - Self: Sized, - { - self.base.layout(view, element_state, cx) + cx: &mut ViewContext, + ) -> 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, + ) -> LayoutId { + self.base.layout(view_state, element_state, cx) } fn paint( @@ -79,7 +85,7 @@ where bounds: Bounds, view: &mut Self::ViewState, element_state: &mut Self::ElementState, - cx: &mut crate::ViewContext, + cx: &mut ViewContext, ) where Self: Sized, { diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index 47203aaa6052f8037e05e5ab57d5d3c18a719ce1..70d642baebc13126c9951f0c76f521a07de61e0f 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -39,31 +39,40 @@ impl IntoAnyElement for String { } } -pub struct Text { +pub struct Text { text: SharedString, - state_type: PhantomData, + state_type: PhantomData, } -impl IntoAnyElement for Text { - fn into_any(self) -> AnyElement { +impl IntoAnyElement for Text { + fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl Element for Text { - type ViewState = S; +impl Element for Text { + type ViewState = V; type ElementState = Arc>>; fn id(&self) -> Option { None } + fn initialize( + &mut self, + _view_state: &mut V, + element_state: Option, + _cx: &mut ViewContext, + ) -> Self::ElementState { + element_state.unwrap_or_default() + } + fn layout( &mut self, - _view: &mut S, - _element_state: Option, - cx: &mut ViewContext, - ) -> (LayoutId, Self::ElementState) { + _view: &mut V, + element_state: &mut Self::ElementState, + cx: &mut ViewContext, + ) -> 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 Element for Text { .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 Element for Text { } }); - (layout_id, element_state) + layout_id } - fn paint<'a>( + fn paint( &mut self, bounds: Bounds, - _: &mut Self::ViewState, + _: &mut V, element_state: &mut Self::ElementState, - cx: &mut ViewContext, + cx: &mut ViewContext, ) { let element_state = element_state.lock(); let element_state = element_state diff --git a/crates/gpui3/src/view.rs b/crates/gpui3/src/view.rs index 99e8c58b645ba08e34529b57b672d2bfc50ef8f7..8d4b72aa438c6270dc6223e1b2476c1efb37bd49 100644 --- a/crates/gpui3/src/view.rs +++ b/crates/gpui3/src/view.rs @@ -6,12 +6,12 @@ use crate::{ }; use std::{marker::PhantomData, sync::Arc}; -pub struct View { - state: Handle, - render: Arc) -> AnyElement + Send + Sync + 'static>, +pub struct View { + state: Handle, + render: Arc) -> AnyElement + Send + Sync + 'static>, } -impl View { +impl View { pub fn into_any(self) -> AnyView { AnyView { view: Arc::new(Mutex::new(self)), @@ -19,7 +19,7 @@ impl View { } } -impl Clone for View { +impl Clone for View { fn clone(&self) -> Self { Self { state: self.state.clone(), @@ -28,13 +28,13 @@ impl Clone for View { } } -pub fn view( - state: Handle, - render: impl Fn(&mut S, &mut ViewContext) -> E + Send + Sync + 'static, -) -> View +pub fn view( + state: Handle, + render: impl Fn(&mut V, &mut ViewContext) -> E + Send + Sync + 'static, +) -> View where - E: IntoAnyElement, - S: 'static + Send + Sync, + E: IntoAnyElement, + V: 'static + Send + Sync, { View { state, @@ -42,8 +42,8 @@ where } } -impl - IntoAnyElement for View +impl + IntoAnyElement for View { fn into_any(self) -> AnyElement { AnyElement::new(EraseViewState { @@ -53,74 +53,87 @@ impl } } -impl Element for View { +impl Element for View { type ViewState = (); - type ElementState = AnyElement; + type ElementState = AnyElement; fn id(&self) -> Option { Some(ElementId::View(self.state.id)) } - fn layout( + fn initialize( &mut self, - _: &mut Self::ViewState, + _: &mut (), _: Option, - cx: &mut ViewContext, - ) -> (LayoutId, 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) - }) + cx: &mut ViewContext<()>, + ) -> Self::ElementState { + self.state.update(cx, |state, cx| (self.render)(state, cx)) + } + + 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, - _: &mut Self::ViewState, + _: &mut (), element: &mut Self::ElementState, - cx: &mut ViewContext, + cx: &mut ViewContext<()>, ) { self.state .update(cx, |state, cx| element.paint(state, None, cx)) } } -struct EraseViewState { - view: View, - parent_view_state_type: PhantomData, +struct EraseViewState { + view: View, + parent_view_state_type: PhantomData, } -impl IntoAnyElement - for EraseViewState +impl IntoAnyElement for EraseViewState where - ViewState: 'static + Send + Sync, - ParentViewState: 'static + Send + Sync, + V: 'static + Send + Sync, + ParentV: 'static + Send + Sync, { - fn into_any(self) -> AnyElement { + fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl Element for EraseViewState +impl Element for EraseViewState 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 { Element::id(&self.view) } - fn layout( + fn initialize( &mut self, _: &mut Self::ViewState, _: Option, cx: &mut ViewContext, - ) -> (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, + ) -> LayoutId { + ViewObject::layout(&mut self.view, element, cx) } fn paint( @@ -136,22 +149,28 @@ 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, element: &mut AnyBox, cx: &mut WindowContext); } -impl ViewObject for View { +impl ViewObject for View { 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| Box::new((self.render)(state, cx)) 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 mut element = (self.render)(state, cx); - let layout_id = element.layout(state, cx); - let element = Box::new(element) as AnyBox; - (layout_id, element) + let element = element.downcast_mut::>().unwrap(); + element.layout(state, cx) }) }) } @@ -159,7 +178,7 @@ impl ViewObject for View { fn paint(&mut self, _: Bounds, 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::>().unwrap(); + let element = element.downcast_mut::>().unwrap(); element.paint(state, None, cx); }); }); @@ -170,11 +189,11 @@ pub struct AnyView { view: Arc>, } -impl IntoAnyElement for AnyView +impl IntoAnyElement for AnyView where - ParentViewState: 'static + Send + Sync, + ParentV: 'static + Send + Sync, { - fn into_any(self) -> AnyElement { + fn into_any(self) -> AnyElement { AnyElement::new(EraseAnyViewState { view: self, parent_view_state_type: PhantomData, @@ -190,13 +209,22 @@ impl Element for AnyView { Some(ElementId::View(self.view.lock().entity_id())) } - fn layout( + fn initialize( &mut self, _: &mut Self::ViewState, _: Option, cx: &mut ViewContext, - ) -> (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, + ) -> LayoutId { + self.view.lock().layout(element, cx) } fn paint( @@ -215,33 +243,42 @@ struct EraseAnyViewState { parent_view_state_type: PhantomData, } -impl IntoAnyElement for EraseAnyViewState +impl IntoAnyElement for EraseAnyViewState where - ParentViewState: 'static + Send + Sync, + ParentV: 'static + Send + Sync, { - fn into_any(self) -> AnyElement { + fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl Element for EraseAnyViewState +impl Element for EraseAnyViewState where - ParentViewState: 'static + Send + Sync, + ParentV: 'static + Send + Sync, { - type ViewState = ParentViewState; + type ViewState = ParentV; type ElementState = AnyBox; fn id(&self) -> Option { Element::id(&self.view) } - fn layout( + fn initialize( &mut self, _: &mut Self::ViewState, _: Option, cx: &mut ViewContext, - ) -> (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, + ) -> LayoutId { + self.view.view.lock().layout(element, cx) } fn paint( diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 17687f3597eb24fdb1fe0e0ab62571b51355247e..e491913b29bba06a631ec5b353ade39223aa5568 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -653,7 +653,8 @@ impl<'a, 'w> WindowContext<'a, 'w> { element_state: Option, 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 From 5afd83c883ea361557220fcb3492290e9f073063 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 14:36:16 +0200 Subject: [PATCH 03/15] Checkpoint --- crates/gpui3/src/elements/div.rs | 126 +++++++++++++++---------------- crates/gpui3/src/window.rs | 110 ++++++++++++++++----------- 2 files changed, 128 insertions(+), 108 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 552c27686ed3e15488f04439c226080a7db17050..3623e44a921bb323fbc4e7f8829f8a0e2917d64c 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -375,10 +375,17 @@ where element_state: Option, cx: &mut ViewContext, ) -> Self::ElementState { - for child in &mut self.children { - child.initialize(view_state, cx); - } - element_state.unwrap_or_default() + cx.with_focus( + self.focusability.focus_handle().cloned(), + self.listeners.key_down.clone(), + self.listeners.key_up.clone(), + |cx| { + for child in &mut self.children { + child.initialize(view_state, cx); + } + element_state.unwrap_or_default() + }, + ) } fn layout( @@ -408,68 +415,57 @@ where cx: &mut ViewContext, ) { self.with_element_id(cx, |this, cx| { - cx.with_key_listeners( - this.focusability.focus_handle().cloned(), - this.listeners.key_down.clone(), - this.listeners.key_up.clone(), - |cx| { - if let Some(group) = this.group.clone() { - cx.default_global::() - .0 - .entry(group) - .or_default() - .push(bounds); - } + if let Some(group) = this.group.clone() { + cx.default_global::() + .0 + .entry(group) + .or_default() + .push(bounds); + } - let hover_group_bounds = this - .group_hover - .as_ref() - .and_then(|group_hover| group_bounds(&group_hover.group, cx)); - let active_group_bounds = this - .group_active - .as_ref() - .and_then(|group_active| group_bounds(&group_active.group, cx)); - let style = this.compute_style(bounds, element_state, cx); - let z_index = style.z_index.unwrap_or(0); - - // Paint background and event handlers. - cx.stack(z_index, |cx| { - cx.stack(0, |cx| { - style.paint(bounds, cx); - this.paint_hover_listeners(bounds, hover_group_bounds, cx); - this.paint_active_listener( - bounds, - active_group_bounds, - element_state.active_state.clone(), - cx, - ); - this.paint_event_listeners( - bounds, - element_state.pending_click.clone(), - cx, - ); - }); - - cx.stack(1, |cx| { - style.apply_text_style(cx, |cx| { - style.apply_overflow(bounds, cx, |cx| { - for child in &mut this.children { - child.paint(view_state, None, cx); - } - }) - }) - }); - }); - - if let Some(group) = this.group.as_ref() { - cx.default_global::() - .0 - .get_mut(group) - .unwrap() - .pop(); - } - }, - ) + let hover_group_bounds = this + .group_hover + .as_ref() + .and_then(|group_hover| group_bounds(&group_hover.group, cx)); + let active_group_bounds = this + .group_active + .as_ref() + .and_then(|group_active| group_bounds(&group_active.group, cx)); + let style = this.compute_style(bounds, element_state, cx); + let z_index = style.z_index.unwrap_or(0); + + // Paint background and event handlers. + cx.stack(z_index, |cx| { + cx.stack(0, |cx| { + style.paint(bounds, cx); + this.paint_hover_listeners(bounds, hover_group_bounds, cx); + this.paint_active_listener( + bounds, + active_group_bounds, + element_state.active_state.clone(), + cx, + ); + this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx); + }); + + cx.stack(1, |cx| { + style.apply_text_style(cx, |cx| { + style.apply_overflow(bounds, cx, |cx| { + for child in &mut this.children { + child.paint(view_state, None, cx); + } + }) + }) + }); + }); + + if let Some(group) = this.group.as_ref() { + cx.default_global::() + .0 + .get_mut(group) + .unwrap() + .pop(); + } }) } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index e491913b29bba06a631ec5b353ade39223aa5568..c0744532ee70be4918773e8bb7433e903ccecf19 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -9,7 +9,7 @@ use crate::{ Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; -use collections::HashMap; +use collections::{HashMap, HashSet}; use derive_more::{Deref, DerefMut}; use smallvec::SmallVec; use std::{ @@ -42,7 +42,7 @@ pub enum DispatchPhase { type MouseEventHandler = Arc; -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FocusId(usize); #[derive(Clone)] @@ -64,10 +64,13 @@ pub struct Window { element_states: HashMap, z_index_stack: StackingOrder, content_mask_stack: Vec>, - mouse_event_handlers: HashMap>, - key_down_listener_stack: + mouse_listeners: HashMap>, + focus_stack: Vec, + focus_parents_by_child: HashMap, + focused_in: HashSet, + key_down_listeners: Vec>, - key_up_listener_stack: + key_up_listeners: Vec>, propagate_event: bool, mouse_position: Point, @@ -76,7 +79,6 @@ pub struct Window { pub(crate) dirty: bool, focus: Option, next_focus_id: FocusId, - painted_focused_element: bool, } impl Window { @@ -135,9 +137,12 @@ impl Window { element_states: HashMap::default(), z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), - mouse_event_handlers: HashMap::default(), - key_down_listener_stack: Vec::new(), - key_up_listener_stack: Vec::new(), + focus_stack: Vec::new(), + focus_parents_by_child: HashMap::default(), + focused_in: HashSet::default(), + mouse_listeners: HashMap::default(), + key_down_listeners: Vec::new(), + key_up_listeners: Vec::new(), propagate_event: true, mouse_position, scale_factor, @@ -145,7 +150,6 @@ impl Window { dirty: true, focus: None, next_focus_id: FocusId(0), - painted_focused_element: false, } } } @@ -169,6 +173,16 @@ impl ContentMask { } } +struct FocusStackFrame { + handle: FocusHandle, + key_down_listeners: SmallVec< + [Arc; 2], + >, + key_up_listeners: SmallVec< + [Arc; 2], + >, +} + pub struct WindowContext<'a, 'w> { app: Reference<'a, AppContext>, pub(crate) window: Reference<'w, Window>, @@ -323,7 +337,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { ) { let order = self.window.z_index_stack.clone(); self.window - .mouse_event_handlers + .mouse_listeners .entry(TypeId::of::()) .or_default() .push(( @@ -675,12 +689,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { // Clear mouse event listeners, because elements add new element listeners // when the upcoming frame is painted. - window - .mouse_event_handlers - .values_mut() - .for_each(Vec::clear); + window.mouse_listeners.values_mut().for_each(Vec::clear); - window.painted_focused_element = false; + // Clear focus state, because we determine what is focused when the new elements + // in the upcoming frame are initialized. + window.key_down_listeners.clear(); + window.key_up_listeners.clear(); + window.focused_in.clear(); + window.focus_parents_by_child.clear(); } fn end_frame(&mut self) { @@ -695,7 +711,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. @@ -726,13 +742,13 @@ 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); } } @@ -1030,7 +1046,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { }); } - pub fn with_key_listeners( + pub fn with_focus( &mut self, focus_handle: Option, key_down: impl IntoIterator>, @@ -1040,20 +1056,25 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let Some(focus_handle) = focus_handle else { return f(self); }; - let Some(focused_id) = self.window.focus else { - return f(self); - }; - if self.window.painted_focused_element { - return f(self); + + let handle = self.handle(); + let window = &mut *self.window; + if let Some(parent_frame) = window.focus_stack.last() { + window + .focus_parents_by_child + .insert(focus_handle.id, parent_frame.handle.id); } - let prev_key_down_len = self.window.key_down_listener_stack.len(); - let prev_key_up_len = self.window.key_up_listener_stack.len(); + 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 = self.handle(); - self.window - .key_down_listener_stack + let handle = handle.clone(); + frame + .key_down_listeners .push(Arc::new(move |event, phase, cx| { handle .update(cx, |view, cx| listener(view, event, phase, cx)) @@ -1061,9 +1082,9 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { })); } for listener in key_up { - let handle = self.handle(); - self.window - .key_up_listener_stack + let handle = handle.clone(); + frame + .key_up_listeners .push(Arc::new(move |event, phase, cx| { handle .update(cx, |view, cx| listener(view, event, phase, cx)) @@ -1071,19 +1092,22 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { })); } - if focus_handle.id == focused_id { - self.window.painted_focused_element = true; + window.focus_stack.push(frame); + + if Some(focus_handle.id) == window.focus { + for frame in &window.focus_stack { + window.focused_in.insert(frame.handle.id); + window + .key_down_listeners + .extend_from_slice(&frame.key_down_listeners); + window + .key_up_listeners + .extend_from_slice(&frame.key_up_listeners); + } } let result = f(self); - - if focus_handle.id != focused_id { - self.window - .key_down_listener_stack - .truncate(prev_key_down_len); - self.window.key_up_listener_stack.truncate(prev_key_up_len); - } - + self.window.focus_stack.pop(); result } From 0dfe70125bca712156e322cd285472b2e863aedf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 14:41:46 +0200 Subject: [PATCH 04/15] Checkpoint --- crates/gpui3/src/window.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index c0744532ee70be4918773e8bb7433e903ccecf19..f5fafbb0b45abb440d359dd59508b0892da25d90 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -50,6 +50,33 @@ pub struct FocusHandle { id: FocusId, } +impl FocusHandle { + pub fn focus(&self, cx: &mut WindowContext) { + cx.window.focus = Some(self.id); + cx.notify(); + } + + pub fn is_focused(&self, cx: &WindowContext) -> bool { + cx.window.focus == Some(self.id) + } + + pub fn contains_focused(&self, cx: &WindowContext) -> bool { + cx.window.containing_focus.contains(&self.id) + } + + pub fn within_focused(&self, cx: &WindowContext) -> bool { + let mut ancestor = Some(self.id); + while let Some(ancestor_id) = ancestor { + if cx.window.focus == Some(ancestor_id) { + return true; + } else { + ancestor = cx.window.focus_parents_by_child.get(&ancestor_id).copied(); + } + } + false + } +} + pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, @@ -67,7 +94,7 @@ pub struct Window { mouse_listeners: HashMap>, focus_stack: Vec, focus_parents_by_child: HashMap, - focused_in: HashSet, + containing_focus: HashSet, key_down_listeners: Vec>, key_up_listeners: @@ -139,7 +166,7 @@ impl Window { content_mask_stack: Vec::new(), focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), - focused_in: HashSet::default(), + containing_focus: HashSet::default(), mouse_listeners: HashMap::default(), key_down_listeners: Vec::new(), key_up_listeners: Vec::new(), @@ -695,7 +722,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { // in the upcoming frame are initialized. window.key_down_listeners.clear(); window.key_up_listeners.clear(); - window.focused_in.clear(); + window.containing_focus.clear(); window.focus_parents_by_child.clear(); } @@ -1096,7 +1123,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { if Some(focus_handle.id) == window.focus { for frame in &window.focus_stack { - window.focused_in.insert(frame.handle.id); + window.containing_focus.insert(frame.handle.id); window .key_down_listeners .extend_from_slice(&frame.key_down_listeners); From eaef1c8b8ee325af4f8a399b8ac3f726b4a58b70 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 15:17:22 +0200 Subject: [PATCH 05/15] Checkpoint --- crates/gpui3/src/app.rs | 39 +++++++- crates/gpui3/src/events.rs | 116 ++++++++++++++++++---- crates/gpui3/src/interactive.rs | 70 +------------ 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/window.rs | 58 ++++++++--- 9 files changed, 210 insertions(+), 137 deletions(-) diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index fdb96902e64bb9c8542ea3b1ae51afe460316dad..e871a7fad6fda76e0c5181b7c58c739f0ec0bdd0 100644 --- a/crates/gpui3/src/app.rs +++ b/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>, pub(crate) platform: MainThreadOnly, text_system: Arc, + flushing_effects: bool, pending_updates: usize, pub(crate) next_frame_callbacks: HashMap>, pub(crate) executor: Executor, @@ -119,8 +122,10 @@ impl AppContext { pub(crate) fn update(&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) { + self.update_window(window_id, |cx| { + if cx.window.focus == focused { + let mut listeners = mem::take(&mut cx.window.focus_change_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_change_listeners.drain(..)); + cx.window.focus_change_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, }, + FocusChanged { + window_id: WindowId, + focused: Option, + }, } #[cfg(test)] diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 52719ae066e509292ea047a908229fedad9feaaa..ee5a1f4fdd5d8763ab878cba9be05f88e36f87ce 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -1,5 +1,9 @@ -use crate::{point, Keystroke, Modifiers, Pixels, Point}; -use std::{any::Any, ops::Deref}; +use smallvec::SmallVec; + +use crate::{ + point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext, +}; +use std::{any::Any, ops::Deref, sync::Arc}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct KeyDownEvent { @@ -26,7 +30,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 +54,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 +165,7 @@ impl Deref for MouseExitEvent { } #[derive(Clone, Debug)] -pub enum Event { +pub enum InputEvent { KeyDown(KeyDownEvent), KeyUp(KeyUpEvent), ModifiersChanged(ModifiersChangedEvent), @@ -166,30 +176,94 @@ pub enum Event { ScrollWheel(ScrollWheelEvent), } -impl Event { +impl InputEvent { pub fn position(&self) -> Option> { 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 struct FocusEvent { + pub blurred: Option, + pub focused: Option, +} + +pub type MouseDownListener = Arc< + dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +pub type MouseUpListener = Arc< + dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +pub type MouseClickListener = + Arc) + Send + Sync + 'static>; + +pub type MouseMoveListener = Arc< + dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; + +pub type ScrollWheelListener = Arc< + dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; + +pub type KeyDownListener = + Arc) + Send + Sync + 'static>; + +pub type KeyUpListener = + Arc) + Send + Sync + 'static>; + +pub struct EventListeners { + pub mouse_down: SmallVec<[MouseDownListener; 2]>, + pub mouse_up: SmallVec<[MouseUpListener; 2]>, + pub mouse_click: SmallVec<[MouseClickListener; 2]>, + pub mouse_move: SmallVec<[MouseMoveListener; 2]>, + pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, + pub key_down: SmallVec<[KeyDownListener; 2]>, + pub key_up: SmallVec<[KeyUpListener; 2]>, +} + +impl Default for EventListeners { + 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(), } } } diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index 35bcab15a22ba94539ff1609a2e136163231398b..ecaebe7681774f451ce6d5f4b7c9c44ce0683b5b 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,8 +1,6 @@ -use smallvec::SmallVec; - use crate::{ - Bounds, DispatchPhase, Element, KeyDownEvent, KeyUpEvent, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ViewContext, + DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent, + MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext, }; use std::sync::Arc; @@ -163,67 +161,3 @@ pub trait Click: Interactive { self } } - -type MouseDownListener = Arc< - dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type MouseUpListener = Arc< - dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type MouseClickListener = - Arc) + Send + Sync + 'static>; - -type MouseMoveListener = Arc< - dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; - -type ScrollWheelListener = Arc< - dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; - -pub type KeyDownListener = - Arc) + Send + Sync + 'static>; - -pub type KeyUpListener = - Arc) + Send + Sync + 'static>; - -pub struct EventListeners { - pub mouse_down: SmallVec<[MouseDownListener; 2]>, - pub mouse_up: SmallVec<[MouseUpListener; 2]>, - pub mouse_click: SmallVec<[MouseClickListener; 2]>, - pub mouse_move: SmallVec<[MouseMoveListener; 2]>, - pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, - pub key_down: SmallVec<[KeyDownListener; 2]>, - pub key_up: SmallVec<[KeyUpListener; 2]>, -} - -impl Default for EventListeners { - 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(), - } - } -} - -pub struct MouseClickEvent { - pub down: MouseDownEvent, - pub up: MouseUpEvent, -} diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 8bfdfbccfa216c8bb0278c795da1565bfcb3a2b7..1706ec14fe9bff02b0def88a71561742c3399503 100644 --- a/crates/gpui3/src/platform.rs +++ b/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); fn on_quit(&self, callback: Box); fn on_reopen(&self, callback: Box); - fn on_event(&self, callback: Box bool>); + fn on_event(&self, callback: Box bool>); fn os_name(&self) -> &'static str; fn os_version(&self) -> Result; @@ -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 bool>); + fn on_event(&self, callback: Box bool>); fn on_active_status_change(&self, callback: Box); fn on_resize(&self, callback: Box, f32)>); fn on_fullscreen(&self, callback: Box); diff --git a/crates/gpui3/src/platform/mac/events.rs b/crates/gpui3/src/platform/mac/events.rs index d776d7ab02c3cac07589b29506cf04b7bc43df64..fa8277baf477299d9a9eb6b991c28a5f03ca7ceb 100644 --- a/crates/gpui3/src/platform/mac/events.rs +++ b/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) -> Option { let event_type = native_event.eventType(); diff --git a/crates/gpui3/src/platform/mac/platform.rs b/crates/gpui3/src/platform/mac/platform.rs index 9feb0a9546bd406839abb5aeb9cb6f61ed053076..28be8f9d2e68504265c362097a9f6cb885c27376 100644 --- a/crates/gpui3/src/platform/mac/platform.rs +++ b/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>, reopen: Option>, quit: Option>, - event: Option bool>>, + event: Option bool>>, // menu_command: Option>, // validate_menu_command: Option bool>>, will_open_menu: Option>, @@ -621,7 +621,7 @@ impl Platform for MacPlatform { self.0.lock().reopen = Some(callback); } - fn on_event(&self, callback: Box bool>) { + fn on_event(&self, callback: Box 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) { diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 03ce03b34b96ecf90cbd91b8640487a1ca4e6eb3..c73d59edde992b9031847721338a7e8867136007 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/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, kind: WindowKind, - event_callback: Option bool>>, + event_callback: Option bool>>, activate_callback: Option>, resize_callback: Option, f32)>>, fullscreen_callback: Option>, @@ -300,7 +300,7 @@ struct MacWindowState { synthetic_drag_counter: usize, last_fresh_keydown: Option, traffic_light_position: Option>, - previous_modifiers_changed_event: Option, + previous_modifiers_changed_event: Option, // 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 bool>) { + fn on_event(&self, callback: Box 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 { diff --git a/crates/gpui3/src/platform/test.rs b/crates/gpui3/src/platform/test.rs index de836f125d11bfc9b38178cf13f35a18578b9d32..4d63bb415af369eb2f1158ed80397e9a7bb7c868 100644 --- a/crates/gpui3/src/platform/test.rs +++ b/crates/gpui3/src/platform/test.rs @@ -125,7 +125,7 @@ impl Platform for TestPlatform { unimplemented!() } - fn on_event(&self, _callback: Box bool>) { + fn on_event(&self, _callback: Box bool>) { unimplemented!() } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index f5fafbb0b45abb440d359dd59508b0892da25d90..e420a28813c73430c3109c76d5ba992665b8a588 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,12 +1,13 @@ 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, - 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, Style, Subscription, TaffyLayoutEngine, - Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + EventEmitter, FocusEvent, 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, + Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, + WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use collections::{HashMap, HashSet}; @@ -47,13 +48,12 @@ pub struct FocusId(usize); #[derive(Clone)] pub struct FocusHandle { - id: FocusId, + pub(crate) id: FocusId, } impl FocusHandle { - pub fn focus(&self, cx: &mut WindowContext) { - cx.window.focus = Some(self.id); - cx.notify(); + pub(crate) fn new(id: FocusId) -> Self { + Self { id } } pub fn is_focused(&self, cx: &WindowContext) -> bool { @@ -95,6 +95,8 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, containing_focus: HashSet, + pub(crate) focus_change_listeners: + Vec>, key_down_listeners: Vec>, key_up_listeners: @@ -104,7 +106,8 @@ pub struct Window { scale_factor: f32, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, - focus: Option, + pub(crate) last_blur: Option>, + pub(crate) focus: Option, next_focus_id: FocusId, } @@ -168,6 +171,7 @@ impl Window { focus_parents_by_child: HashMap::default(), containing_focus: HashSet::default(), mouse_listeners: HashMap::default(), + focus_change_listeners: Vec::new(), key_down_listeners: Vec::new(), key_up_listeners: Vec::new(), propagate_event: true, @@ -175,6 +179,7 @@ impl Window { scale_factor, scene_builder: SceneBuilder::new(), dirty: true, + last_blur: None, focus: None, next_focus_id: FocusId(0), } @@ -232,6 +237,34 @@ impl<'a, 'w> WindowContext<'a, 'w> { FocusHandle { 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( &mut self, f: impl FnOnce(&mut MainThread>) -> R + Send + 'static, @@ -720,6 +753,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { // Clear focus state, because we determine what is focused when the new elements // in the upcoming frame are initialized. + window.focus_change_listeners.clear(); window.key_down_listeners.clear(); window.key_up_listeners.clear(); window.containing_focus.clear(); @@ -730,7 +764,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { 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; From acca8ea786536d3f5a7eac22bcfe3a105505b8f0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 15:24:35 +0200 Subject: [PATCH 06/15] Checkpoint --- crates/gpui3/src/elements/div.rs | 20 ++++++++-------- crates/gpui3/src/events.rs | 19 +++++++-------- crates/gpui3/src/interactive.rs | 15 ++++++------ crates/gpui3/src/window.rs | 40 +++++++++++++++----------------- 4 files changed, 45 insertions(+), 49 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 3623e44a921bb323fbc4e7f8829f8a0e2917d64c..8bf748568e3e4aa4b248414dba31b5572bd6a86d 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -9,7 +9,7 @@ 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 { @@ -263,12 +263,12 @@ where } fn paint_event_listeners( - &self, + &mut self, bounds: Bounds, pending_click: Arc>>, cx: &mut ViewContext, ) { - 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| { @@ -292,25 +292,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); }) @@ -351,7 +351,7 @@ where + Sync + 'static, ) -> Self { - self.listeners.key_down.push(Arc::new(listener)); + self.listeners.key_down.push(Box::new(listener)); self } } @@ -377,8 +377,8 @@ where ) -> Self::ElementState { cx.with_focus( self.focusability.focus_handle().cloned(), - self.listeners.key_down.clone(), - self.listeners.key_up.clone(), + mem::take(&mut self.listeners.key_down), + mem::take(&mut self.listeners.key_up), |cx| { for child in &mut self.children { child.initialize(view_state, cx); diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index ee5a1f4fdd5d8763ab878cba9be05f88e36f87ce..624b32bc2035d62dcf9b54b780d990f353fcef77 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -1,9 +1,8 @@ -use smallvec::SmallVec; - use crate::{ point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext, }; -use std::{any::Any, ops::Deref, sync::Arc}; +use smallvec::SmallVec; +use std::{any::Any, ops::Deref}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct KeyDownEvent { @@ -209,29 +208,29 @@ pub struct FocusEvent { pub focused: Option, } -pub type MouseDownListener = Arc< +pub type MouseDownListener = Box< dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -pub type MouseUpListener = Arc< +pub type MouseUpListener = Box< dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; pub type MouseClickListener = - Arc) + Send + Sync + 'static>; + Box) + Send + Sync + 'static>; -pub type MouseMoveListener = Arc< +pub type MouseMoveListener = Box< dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -pub type ScrollWheelListener = Arc< +pub type ScrollWheelListener = Box< dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync @@ -239,10 +238,10 @@ pub type ScrollWheelListener = Arc< >; pub type KeyDownListener = - Arc) + Send + Sync + 'static>; + Box) + Send + Sync + 'static>; pub type KeyUpListener = - Arc) + Send + Sync + 'static>; + Box) + Send + Sync + 'static>; pub struct EventListeners { pub mouse_down: SmallVec<[MouseDownListener; 2]>, diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index ecaebe7681774f451ce6d5f4b7c9c44ce0683b5b..f328f22cc0606ccc7708a4ab785e479ff7392785 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -2,7 +2,6 @@ use crate::{ DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext, }; -use std::sync::Arc; pub trait Interactive: Element { fn listeners(&mut self) -> &mut EventListeners; @@ -20,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) @@ -44,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) @@ -68,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) @@ -92,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) @@ -115,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); } @@ -135,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); } @@ -157,7 +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 } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index e420a28813c73430c3109c76d5ba992665b8a588..26bbc7df4347267302c8182f6b68f15e36dcd1b7 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -40,8 +40,13 @@ pub enum DispatchPhase { Capture, } -type MouseEventHandler = - Arc; +type AnyMouseEventListener = + Box; +type AnyFocusChangeListener = Box; +type AnyKeyDownListener = + Box; +type AnyKeyUpListener = + Box; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FocusId(usize); @@ -91,16 +96,13 @@ pub struct Window { element_states: HashMap, z_index_stack: StackingOrder, content_mask_stack: Vec>, - mouse_listeners: HashMap>, + mouse_listeners: HashMap>, focus_stack: Vec, focus_parents_by_child: HashMap, containing_focus: HashSet, - pub(crate) focus_change_listeners: - Vec>, - key_down_listeners: - Vec>, - key_up_listeners: - Vec>, + pub(crate) focus_change_listeners: Vec, + key_down_listeners: Vec, + key_up_listeners: Vec, propagate_event: bool, mouse_position: Point, scale_factor: f32, @@ -207,12 +209,8 @@ impl ContentMask { struct FocusStackFrame { handle: FocusHandle, - key_down_listeners: SmallVec< - [Arc; 2], - >, - key_up_listeners: SmallVec< - [Arc; 2], - >, + key_down_listeners: SmallVec<[AnyKeyDownListener; 2]>, + key_up_listeners: SmallVec<[AnyKeyUpListener; 2]>, } pub struct WindowContext<'a, 'w> { @@ -402,7 +400,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { .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) }), )) @@ -1136,7 +1134,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let handle = handle.clone(); frame .key_down_listeners - .push(Arc::new(move |event, phase, cx| { + .push(Box::new(move |event, phase, cx| { handle .update(cx, |view, cx| listener(view, event, phase, cx)) .log_err(); @@ -1146,7 +1144,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let handle = handle.clone(); frame .key_up_listeners - .push(Arc::new(move |event, phase, cx| { + .push(Box::new(move |event, phase, cx| { handle .update(cx, |view, cx| listener(view, event, phase, cx)) .log_err(); @@ -1156,14 +1154,14 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { window.focus_stack.push(frame); if Some(focus_handle.id) == window.focus { - for frame in &window.focus_stack { + for frame in &mut window.focus_stack { window.containing_focus.insert(frame.handle.id); window .key_down_listeners - .extend_from_slice(&frame.key_down_listeners); + .extend(frame.key_down_listeners.drain(..)); window .key_up_listeners - .extend_from_slice(&frame.key_up_listeners); + .extend(frame.key_up_listeners.drain(..)); } } From 8914b945777e6d1982a22dd5a9681d529c7c0ffa Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 15:46:17 +0200 Subject: [PATCH 07/15] Checkpoint --- crates/gpui3/src/app.rs | 6 +- crates/gpui3/src/elements/div.rs | 1 + crates/gpui3/src/events.rs | 5 ++ crates/gpui3/src/focus.rs | 98 ++++++++++++++++++++++++++++++++ crates/gpui3/src/gpui3.rs | 2 + crates/gpui3/src/window.rs | 41 +++++++++---- 6 files changed, 139 insertions(+), 14 deletions(-) create mode 100644 crates/gpui3/src/focus.rs diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index e871a7fad6fda76e0c5181b7c58c739f0ec0bdd0..59c62f4c17c4b571726d5bb7012d657fd6674265 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -234,7 +234,7 @@ impl AppContext { fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option) { self.update_window(window_id, |cx| { if cx.window.focus == focused { - let mut listeners = mem::take(&mut cx.window.focus_change_listeners); + 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 }; @@ -242,8 +242,8 @@ impl AppContext { listener(&event, cx); } - listeners.extend(cx.window.focus_change_listeners.drain(..)); - cx.window.focus_change_listeners = listeners; + listeners.extend(cx.window.focus_listeners.drain(..)); + cx.window.focus_listeners = listeners; } }) .ok(); diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 8bf748568e3e4aa4b248414dba31b5572bd6a86d..5d874eb79e87f7e1085056f0f8a1850357c095b9 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -379,6 +379,7 @@ where 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); diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 624b32bc2035d62dcf9b54b780d990f353fcef77..7bd99d61697e6ec25391cb4e54a4548f658ca32f 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -243,6 +243,9 @@ pub type KeyDownListener = pub type KeyUpListener = Box) + Send + Sync + 'static>; +pub type FocusListener = + Box) + Send + Sync + 'static>; + pub struct EventListeners { pub mouse_down: SmallVec<[MouseDownListener; 2]>, pub mouse_up: SmallVec<[MouseUpListener; 2]>, @@ -251,6 +254,7 @@ pub struct EventListeners { pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, pub key_down: SmallVec<[KeyDownListener; 2]>, pub key_up: SmallVec<[KeyUpListener; 2]>, + pub focus: SmallVec<[FocusListener; 2]>, } impl Default for EventListeners { @@ -263,6 +267,7 @@ impl Default for EventListeners { scroll_wheel: SmallVec::new(), key_down: SmallVec::new(), key_up: SmallVec::new(), + focus: SmallVec::new(), } } } diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs new file mode 100644 index 0000000000000000000000000000000000000000..bc9ebb984715c48551d6bf0bceb4ef6455fb6fc1 --- /dev/null +++ b/crates/gpui3/src/focus.rs @@ -0,0 +1,98 @@ +use crate::{Element, EventListeners, FocusEvent, FocusHandle, ViewContext}; + +pub trait Focus: Element { + fn handle(&self) -> &FocusHandle; + fn listeners(&mut self) -> &mut EventListeners; + + fn on_focus( + mut self, + listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext) + + 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) + + 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) + + 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) + + 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 + } +} diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index 8e841e6c2e499f2be38a2d5ddd9466c4cb64f0c7..caf265a60c8df828308c22524dab7d0eaa612e55 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/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::*; diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 26bbc7df4347267302c8182f6b68f15e36dcd1b7..3e1ab39fa56c66374185a008c074d8b144eb22ba 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,9 +1,9 @@ use crate::{ px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId, - EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, - InputEvent, IsZero, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener, LayoutId, - MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, + 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, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, @@ -42,7 +42,7 @@ pub enum DispatchPhase { type AnyMouseEventListener = Box; -type AnyFocusChangeListener = Box; +type AnyFocusListener = Box; type AnyKeyDownListener = Box; type AnyKeyUpListener = @@ -51,7 +51,7 @@ type AnyKeyUpListener = #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FocusId(usize); -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct FocusHandle { pub(crate) id: FocusId, } @@ -70,9 +70,14 @@ impl FocusHandle { } pub fn within_focused(&self, cx: &WindowContext) -> bool { - let mut ancestor = Some(self.id); + 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 cx.window.focus == Some(ancestor_id) { + if self.id == ancestor_id { return true; } else { ancestor = cx.window.focus_parents_by_child.get(&ancestor_id).copied(); @@ -100,7 +105,7 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, containing_focus: HashSet, - pub(crate) focus_change_listeners: Vec, + pub(crate) focus_listeners: Vec, key_down_listeners: Vec, key_up_listeners: Vec, propagate_event: bool, @@ -173,7 +178,7 @@ impl Window { focus_parents_by_child: HashMap::default(), containing_focus: HashSet::default(), mouse_listeners: HashMap::default(), - focus_change_listeners: Vec::new(), + focus_listeners: Vec::new(), key_down_listeners: Vec::new(), key_up_listeners: Vec::new(), propagate_event: true, @@ -235,6 +240,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { FocusHandle { id } } + pub fn focused(&self) -> Option { + 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); @@ -751,7 +760,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { // Clear focus state, because we determine what is focused when the new elements // in the upcoming frame are initialized. - window.focus_change_listeners.clear(); + window.focus_listeners.clear(); window.key_down_listeners.clear(); window.key_up_listeners.clear(); window.containing_focus.clear(); @@ -1110,6 +1119,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { focus_handle: Option, key_down: impl IntoIterator>, key_up: impl IntoIterator>, + focus: impl IntoIterator>, f: impl FnOnce(&mut Self) -> R, ) -> R { let Some(focus_handle) = focus_handle else { @@ -1118,6 +1128,16 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { 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(); + })); + } + if let Some(parent_frame) = window.focus_stack.last() { window .focus_parents_by_child @@ -1150,7 +1170,6 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { .log_err(); })); } - window.focus_stack.push(frame); if Some(focus_handle.id) == window.focus { From 1270bcc6edc05ce357688cc31e0e6413dae4ebb6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:10:58 +0200 Subject: [PATCH 08/15] Checkpoint --- crates/gpui3/src/events.rs | 13 +++ crates/gpui3/src/focus.rs | 37 ++++++++- crates/gpui3/src/platform.rs | 2 +- crates/gpui3/src/platform/mac/window.rs | 2 +- crates/gpui3/src/window.rs | 102 ++++++++++++++++++------ 5 files changed, 130 insertions(+), 26 deletions(-) diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 7bd99d61697e6ec25391cb4e54a4548f658ca32f..8adfba37ada3cd9038378a8f421e352433b001ce 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -201,6 +201,19 @@ impl InputEvent { 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 { diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs index bc9ebb984715c48551d6bf0bceb4ef6455fb6fc1..50338d36a903ca122ff3da9cf1a5ff5536c28588 100644 --- a/crates/gpui3/src/focus.rs +++ b/crates/gpui3/src/focus.rs @@ -1,4 +1,7 @@ -use crate::{Element, EventListeners, FocusEvent, FocusHandle, ViewContext}; +use crate::{ + DispatchPhase, Element, EventListeners, FocusEvent, FocusHandle, KeyDownEvent, KeyUpEvent, + ViewContext, +}; pub trait Focus: Element { fn handle(&self) -> &FocusHandle; @@ -95,4 +98,36 @@ pub trait Focus: Element { })); self } + + fn on_key_down( + mut self, + listener: impl Fn( + &mut Self::ViewState, + &KeyDownEvent, + DispatchPhase, + &mut ViewContext, + ) + 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) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + self.listeners().key_up.push(Box::new(listener)); + self + } } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 1706ec14fe9bff02b0def88a71561742c3399503..5bb59acae03831bb37da3d72ed704f9c74c8d900 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -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 bool>); + fn on_input(&self, callback: Box bool>); fn on_active_status_change(&self, callback: Box); fn on_resize(&self, callback: Box, f32)>); fn on_fullscreen(&self, callback: Box); diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index c73d59edde992b9031847721338a7e8867136007..519eab5b093a1a0e9fbaf6b9446522df689b1f12 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -854,7 +854,7 @@ impl PlatformWindow for MacWindow { .detach(); } - fn on_event(&self, callback: Box bool>) { + fn on_input(&self, callback: Box bool>) { self.0.as_ref().lock().event_callback = Some(callback); } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 3e1ab39fa56c66374185a008c074d8b144eb22ba..a9c4dcadea09317643efbb33b8fbea742b804928 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -10,7 +10,7 @@ use crate::{ WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; -use collections::{HashMap, HashSet}; +use collections::HashMap; use derive_more::{Deref, DerefMut}; use smallvec::SmallVec; use std::{ @@ -42,6 +42,8 @@ pub enum DispatchPhase { type AnyMouseEventListener = Box; +type AnyKeyboardEventListener = + Box; type AnyFocusListener = Box; type AnyKeyDownListener = Box; @@ -66,7 +68,8 @@ impl FocusHandle { } pub fn contains_focused(&self, cx: &WindowContext) -> bool { - cx.window.containing_focus.contains(&self.id) + cx.focused() + .map_or(false, |focused| self.contains(&focused, cx)) } pub fn within_focused(&self, cx: &WindowContext) -> bool { @@ -102,12 +105,10 @@ pub struct Window { z_index_stack: StackingOrder, content_mask_stack: Vec>, mouse_listeners: HashMap>, + keyboard_listeners: HashMap>, focus_stack: Vec, focus_parents_by_child: HashMap, - containing_focus: HashSet, pub(crate) focus_listeners: Vec, - key_down_listeners: Vec, - key_up_listeners: Vec, propagate_event: bool, mouse_position: Point, scale_factor: f32, @@ -149,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)) @@ -174,13 +175,11 @@ impl Window { element_states: HashMap::default(), z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), + mouse_listeners: HashMap::default(), + keyboard_listeners: HashMap::default(), focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), - containing_focus: HashSet::default(), - mouse_listeners: HashMap::default(), focus_listeners: Vec::new(), - key_down_listeners: Vec::new(), - key_up_listeners: Vec::new(), propagate_event: true, mouse_position, scale_factor, @@ -415,6 +414,19 @@ impl<'a, 'w> WindowContext<'a, 'w> { )) } + pub fn on_keyboard_event( + &mut self, + handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static, + ) { + self.window + .keyboard_listeners + .entry(TypeId::of::()) + .or_default() + .push(Box::new(move |event: &dyn Any, phase, cx| { + handler(event.downcast_ref().unwrap(), phase, cx) + })) + } + pub fn mouse_position(&self) -> Point { self.window.mouse_position } @@ -761,9 +773,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { // Clear focus state, because we determine what is focused when the new elements // in the upcoming frame are initialized. window.focus_listeners.clear(); - window.key_down_listeners.clear(); - window.key_up_listeners.clear(); - window.containing_focus.clear(); + window.keyboard_listeners.values_mut().for_each(Vec::clear); window.focus_parents_by_child.clear(); } @@ -819,6 +829,39 @@ impl<'a, 'w> WindowContext<'a, 'w> { .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 @@ -1138,7 +1181,8 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { })); } - if let Some(parent_frame) = window.focus_stack.last() { + 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); @@ -1170,20 +1214,20 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { .log_err(); })); } - window.focus_stack.push(frame); + focus_stack.push(frame); if Some(focus_handle.id) == window.focus { - for frame in &mut window.focus_stack { - window.containing_focus.insert(frame.handle.id); - window - .key_down_listeners - .extend(frame.key_down_listeners.drain(..)); - window - .key_up_listeners - .extend(frame.key_up_listeners.drain(..)); + 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 @@ -1232,6 +1276,18 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { }) }); } + + pub fn on_keyboard_event( + &mut self, + handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext) + 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> { From d98c347902c9c08d69c77ed2f38fef05b79dd963 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:13:36 +0200 Subject: [PATCH 09/15] Checkpoint --- crates/gpui3/src/element.rs | 6 ++++++ crates/gpui3/src/elements/div.rs | 21 +++++++-------------- crates/gpui3/src/focus.rs | 6 ++---- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 2d81e406913351bbfd968ed9a1516fa11885cec6..9f5d0078f87b5771ab1a08d08fab96818d2ad4c7 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -61,6 +61,12 @@ pub trait ElementFocusability: 'static + Send + Sync { pub struct Focusable(FocusHandle); +impl AsRef for Focusable { + fn as_ref(&self) -> &FocusHandle { + &self.0 + } +} + impl ElementFocusability for Focusable { fn focus_handle(&self) -> Option<&FocusHandle> { Some(&self.0) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 5d874eb79e87f7e1085056f0f8a1850357c095b9..2f6fc0059500a1426fec9b038b5afb1f3a81573b 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,9 +1,9 @@ use crate::{ Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element, - ElementFocusability, ElementId, ElementIdentity, EventListeners, FocusHandle, Focusable, Hover, - Identified, Interactive, IntoAnyElement, KeyDownEvent, LayoutId, MouseClickEvent, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, - Point, ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, ViewContext, + 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; @@ -339,20 +339,13 @@ where } } -impl Div +impl Focus for Div where I: ElementIdentity, V: 'static + Send + Sync, { - pub fn on_key_down( - mut self, - listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, - ) -> Self { - self.listeners.key_down.push(Box::new(listener)); - self + fn handle(&self) -> &FocusHandle { + self.focusability.as_ref() } } diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs index 50338d36a903ca122ff3da9cf1a5ff5536c28588..c7f405a358b241a4c5ad5cb1ede3ab756830e6ed 100644 --- a/crates/gpui3/src/focus.rs +++ b/crates/gpui3/src/focus.rs @@ -1,11 +1,9 @@ use crate::{ - DispatchPhase, Element, EventListeners, FocusEvent, FocusHandle, KeyDownEvent, KeyUpEvent, - ViewContext, + DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, ViewContext, }; -pub trait Focus: Element { +pub trait Focus: Interactive { fn handle(&self) -> &FocusHandle; - fn listeners(&mut self) -> &mut EventListeners; fn on_focus( mut self, From 036e266bae6f8189d960d43d37af6ee21e7d35f6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:21:52 +0200 Subject: [PATCH 10/15] Checkpoint --- crates/gpui3/src/elements/div.rs | 38 ++++++++++++++++++++++++++++++++ crates/gpui3/src/focus.rs | 30 ++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 2f6fc0059500a1426fec9b038b5afb1f3a81573b..1cada008f22eb01d340df488cdecad68b825f22d 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -74,6 +74,9 @@ where group_hover: None, active_style: StyleRefinement::default(), group_active: None, + focus_style: StyleRefinement::default(), + focus_in_style: StyleRefinement::default(), + in_focus_style: StyleRefinement::default(), listeners: EventListeners::default(), } } @@ -88,6 +91,9 @@ pub struct Div, active_style: StyleRefinement, group_active: Option, + focus_style: StyleRefinement, + focus_in_style: StyleRefinement, + in_focus_style: StyleRefinement, listeners: EventListeners, } @@ -112,6 +118,9 @@ 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, } } @@ -192,6 +201,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() { @@ -334,6 +357,9 @@ 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, } } @@ -347,6 +373,18 @@ where 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 Element for Div diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs index c7f405a358b241a4c5ad5cb1ede3ab756830e6ed..d6e9adc6fd5c156d412f8cf5978733beb980a5c0 100644 --- a/crates/gpui3/src/focus.rs +++ b/crates/gpui3/src/focus.rs @@ -1,10 +1,38 @@ use crate::{ - DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, ViewContext, + 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) From f4d50c4dca30f6f26e29a7fab24b523999362152 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:27:58 +0200 Subject: [PATCH 11/15] Checkpoint --- crates/gpui3/src/elements/div.rs | 32 ++++++------ crates/gpui3/src/elements/img.rs | 2 +- crates/gpui3/src/elements/svg.rs | 83 +++++++++++++++++++++++--------- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 1cada008f22eb01d340df488cdecad68b825f22d..6919247c53085c01dcd0cf50358266126341cdd3 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -60,7 +60,7 @@ impl ScrollState { } } -pub fn div() -> Div +pub fn div() -> Div where V: 'static + Send + Sync, { @@ -81,7 +81,7 @@ where } } -pub struct Div { +pub struct Div { identity: I, focusability: F, children: SmallVec<[AnyElement; 2]>, @@ -102,12 +102,12 @@ struct GroupStyle { style: StyleRefinement, } -impl Div +impl Div where F: ElementFocusability, V: 'static + Send + Sync, { - pub fn id(self, id: impl Into) -> Div { + pub fn id(self, id: impl Into) -> Div { Div { identity: Identified(id.into()), focusability: self.focusability, @@ -126,7 +126,7 @@ where } } -impl Div +impl Div where I: ElementIdentity, F: ElementFocusability, @@ -341,12 +341,12 @@ where } } -impl Div +impl Div where I: ElementIdentity, V: 'static + Send + Sync, { - pub fn focusable(self, handle: &FocusHandle) -> Div { + pub fn focusable(self, handle: &FocusHandle) -> Div { Div { identity: self.identity, focusability: handle.clone().into(), @@ -365,7 +365,7 @@ where } } -impl Focus for Div +impl Focus for Div where I: ElementIdentity, V: 'static + Send + Sync, @@ -387,7 +387,7 @@ where } } -impl Element for Div +impl Element for Div where I: ElementIdentity, F: ElementFocusability, @@ -502,7 +502,7 @@ where } } -impl IntoAnyElement for Div +impl IntoAnyElement for Div where I: ElementIdentity, F: ElementFocusability, @@ -513,7 +513,7 @@ where } } -impl ParentElement for Div +impl ParentElement for Div where I: ElementIdentity, F: ElementFocusability, @@ -524,7 +524,7 @@ where } } -impl Styled for Div +impl Styled for Div where I: ElementIdentity, F: ElementFocusability, @@ -535,7 +535,7 @@ where } } -impl Interactive for Div +impl Interactive for Div where I: ElementIdentity, F: ElementFocusability, @@ -546,7 +546,7 @@ where } } -impl Hover for Div +impl Hover for Div where I: ElementIdentity, F: ElementFocusability, @@ -561,14 +561,14 @@ where } } -impl Click for Div +impl Click for Div where F: ElementFocusability, V: 'static + Send + Sync, { } -impl Active for Div +impl Active for Div where F: ElementFocusability, V: 'static + Send + Sync, diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index 261062a5b942035c0ae3b0694791c00f2de6ab71..6d9ba59251e5cc98ed460fdc87bf874dd90b86d6 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -7,7 +7,7 @@ use futures::FutureExt; use util::ResultExt; pub struct Img { - base: Div, + base: Div, uri: Option, grayscale: bool, } diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index f0d6956503aee723f4c6c9c51c675c61d41f313e..c3f4972cb66dfcefae599252a4cefd2100874147 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -1,16 +1,17 @@ use crate::{ - div, Active, Anonymous, AnyElement, Bounds, Click, Div, DivState, Element, ElementId, - ElementIdentity, EventListeners, Hover, Identified, Interactive, IntoAnyElement, LayoutId, - NonFocusable, Pixels, SharedString, StyleRefinement, Styled, ViewContext, + 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 { - base: Div, +pub struct Svg { + base: Div, path: Option, } -pub fn svg() -> Svg +pub fn svg() -> Svg where V: 'static + Send + Sync, { @@ -20,10 +21,11 @@ where } } -impl Svg +impl Svg where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { pub fn path(mut self, path: impl Into) -> Self { self.path = Some(path.into()); @@ -31,8 +33,12 @@ where } } -impl Svg { - pub fn id(self, id: impl Into) -> Svg { +impl Svg +where + V: 'static + Send + Sync, + F: ElementFocusability, +{ + pub fn id(self, id: impl Into) -> Svg { Svg { base: self.base.id(id), path: self.path, @@ -40,20 +46,22 @@ impl Svg { } } -impl IntoAnyElement for Svg +impl IntoAnyElement for Svg where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl Element for Svg +impl Element for Svg where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { type ViewState = V; type ElementState = DivState; @@ -101,43 +109,74 @@ where } } -impl Styled for Svg +impl Styled for Svg where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn style(&mut self) -> &mut StyleRefinement { self.base.style() } } -impl Interactive for Svg +impl Interactive for Svg where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn listeners(&mut self) -> &mut EventListeners { self.base.listeners() } } -impl Hover for Svg +impl Hover for Svg where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn set_hover_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_hover_style(group, style); } } -impl Click for Svg where V: 'static + Send + Sync {} +impl Click for Svg +where + V: 'static + Send + Sync, + F: ElementFocusability, +{ +} -impl Active for Svg +impl Active for Svg where V: 'static + Send + Sync, + F: ElementFocusability, { fn set_active_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_active_style(group, style) } } + +impl Focus for Svg +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() + } +} From f58a9bad42f6d3436bb2a9c271b3c2d53cb9f71f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:30:03 +0200 Subject: [PATCH 12/15] Checkpoint --- crates/gpui3/src/elements/img.rs | 81 +++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index 6d9ba59251e5cc98ed460fdc87bf874dd90b86d6..a4c8f16b4b10bed0f711ce947bdbde3e9f39a1ca 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -1,18 +1,19 @@ use crate::{ div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element, - ElementId, ElementIdentity, EventListeners, Hover, Identified, Interactive, IntoAnyElement, - LayoutId, NonFocusable, Pixels, SharedString, StyleRefinement, Styled, ViewContext, + 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 { - base: Div, +pub struct Img { + base: Div, uri: Option, grayscale: bool, } -pub fn img() -> Img +pub fn img() -> Img where V: 'static + Send + Sync, { @@ -23,10 +24,11 @@ where } } -impl Img +impl Img where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { pub fn uri(mut self, uri: impl Into) -> Self { self.uri = Some(uri.into()); @@ -39,8 +41,12 @@ where } } -impl Img { - pub fn id(self, id: impl Into) -> Img { +impl Img +where + V: 'static + Send + Sync, + F: ElementFocusability, +{ + pub fn id(self, id: impl Into) -> Img { Img { base: self.base.id(id), uri: self.uri, @@ -49,20 +55,22 @@ impl Img { } } -impl IntoAnyElement for Img +impl IntoAnyElement for Img where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl Element for Img +impl Element for Img where V: Send + Sync + 'static, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { type ViewState = V; type ElementState = DivState; @@ -127,43 +135,74 @@ where } } -impl Styled for Img +impl Styled for Img where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn style(&mut self) -> &mut StyleRefinement { self.base.style() } } -impl Interactive for Img +impl Interactive for Img where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn listeners(&mut self) -> &mut EventListeners { self.base.listeners() } } -impl Hover for Img +impl Hover for Img where V: 'static + Send + Sync, - K: ElementIdentity, + I: ElementIdentity, + F: ElementFocusability, { fn set_hover_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_hover_style(group, style); } } -impl Click for Img where V: 'static + Send + Sync {} +impl Click for Img +where + V: 'static + Send + Sync, + F: ElementFocusability, +{ +} -impl Active for Img +impl Active for Img where V: 'static + Send + Sync, + F: ElementFocusability, { fn set_active_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_active_style(group, style) } } + +impl Focus for Img +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() + } +} From fecb27232e584ee65306617fa7e652d9dd72c35d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:30:48 +0200 Subject: [PATCH 13/15] Checkpoint --- crates/gpui3/src/elements/div.rs | 6 +++++- crates/gpui3/src/elements/img.rs | 6 +++++- crates/gpui3/src/elements/svg.rs | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 6919247c53085c01dcd0cf50358266126341cdd3..c95ca6e853604b823d784d5b7b8bfc97b8178dfe 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -81,7 +81,11 @@ where } } -pub struct Div { +pub struct Div< + V: 'static + Send + Sync, + I: ElementIdentity = Anonymous, + F: ElementFocusability = NonFocusable, +> { identity: I, focusability: F, children: SmallVec<[AnyElement; 2]>, diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index a4c8f16b4b10bed0f711ce947bdbde3e9f39a1ca..2cd411f0856af226a88b8580ab0bcbd17834fdf3 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -7,7 +7,11 @@ use crate::{ use futures::FutureExt; use util::ResultExt; -pub struct Img { +pub struct Img< + V: 'static + Send + Sync, + I: ElementIdentity = Anonymous, + F: ElementFocusability = NonFocusable, +> { base: Div, uri: Option, grayscale: bool, diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index c3f4972cb66dfcefae599252a4cefd2100874147..17be310457db6a929e1882d52ab0ddf4dea8a292 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -6,7 +6,11 @@ use crate::{ }; use util::ResultExt; -pub struct Svg { +pub struct Svg< + V: 'static + Send + Sync, + I: ElementIdentity = Anonymous, + F: ElementFocusability = NonFocusable, +> { base: Div, path: Option, } From 0e4bd485e012ebd8ebbc6977776f813d0890f6c9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:36:48 +0200 Subject: [PATCH 14/15] Checkpoint --- crates/gpui3_macros/src/derive_element.rs | 25 +++++++++++++++-------- crates/ui2/src/theme.rs | 23 +++++++++++++++------ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/crates/gpui3_macros/src/derive_element.rs b/crates/gpui3_macros/src/derive_element.rs index 6dcc84751da6f41a578fab3c01fb12d85ac26c41..bfe7a301c2092e23da24862caead19da4902f3b0 100644 --- a/crates/gpui3_macros/src/derive_element.rs +++ b/crates/gpui3_macros/src/derive_element.rs @@ -57,27 +57,34 @@ pub fn derive_element(input: TokenStream) -> TokenStream { None } - fn layout( + fn initialize( &mut self, view_state: &mut Self::ViewState, - element_state: Option, - cx: &mut gpui3::ViewContext, - ) -> (gpui3::LayoutId, Self::ElementState) { + _: Option, + cx: &mut gpui3::ViewContext + ) -> 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) + self.render(view_state, cx).into_any() + } + + fn layout( + &mut self, + view_state: &mut Self::ViewState, + rendered_element: &mut Self::ElementState, + cx: &mut gpui3::ViewContext, + ) -> gpui3::LayoutId { + rendered_element.layout(view_state, cx) } fn paint( &mut self, bounds: gpui3::Bounds, view_state: &mut Self::ViewState, - element_state: &mut Self::ElementState, + rendered_element: &mut Self::ElementState, cx: &mut gpui3::ViewContext, ) { - element_state.paint(view_state, None, cx) + rendered_element.paint(view_state, None, cx) } } }; diff --git a/crates/ui2/src/theme.rs b/crates/ui2/src/theme.rs index 479b81d9468663968928f8af0dbdc1e6723ee4d5..aa84e7c11f1954646879010e264e12481a0376aa 100644 --- a/crates/ui2/src/theme.rs +++ b/crates/ui2/src/theme.rs @@ -164,31 +164,42 @@ impl Element for Themed { None } - fn layout( + fn initialize( &mut self, - state: &mut E::ViewState, + view_state: &mut Self::ViewState, element_state: Option, + cx: &mut ViewContext, + ) -> 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, - ) -> (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, - state: &mut Self::ViewState, + view_state: &mut Self::ViewState, frame_state: &mut Self::ElementState, cx: &mut ViewContext, ) 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); }); } } From 7149f99f02bcb3e538d921c489c41ef7bb5dfc8d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 16:43:20 +0200 Subject: [PATCH 15/15] Checkpoint --- crates/gpui3/src/view.rs | 13 ++++++++++--- crates/gpui3_macros/src/derive_element.rs | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/gpui3/src/view.rs b/crates/gpui3/src/view.rs index 8d4b72aa438c6270dc6223e1b2476c1efb37bd49..cc9d2d3d27824c18ac1d2011e821a4b85724802b 100644 --- a/crates/gpui3/src/view.rs +++ b/crates/gpui3/src/view.rs @@ -67,7 +67,11 @@ impl Element for View { _: Option, cx: &mut ViewContext<()>, ) -> Self::ElementState { - self.state.update(cx, |state, cx| (self.render)(state, cx)) + self.state.update(cx, |state, cx| { + let mut any_element = (self.render)(state, cx); + any_element.initialize(state, cx); + any_element + }) } fn layout( @@ -161,8 +165,11 @@ impl ViewObject for View { fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox { cx.with_element_id(self.entity_id(), |cx| { - self.state - .update(cx, |state, cx| Box::new((self.render)(state, cx)) as AnyBox) + self.state.update(cx, |state, cx| { + let mut any_element = Box::new((self.render)(state, cx)); + any_element.initialize(state, cx); + any_element as AnyBox + }) }) } diff --git a/crates/gpui3_macros/src/derive_element.rs b/crates/gpui3_macros/src/derive_element.rs index bfe7a301c2092e23da24862caead19da4902f3b0..d7510faf99cf2f21d8d6968e79b15129dfc65e6d 100644 --- a/crates/gpui3_macros/src/derive_element.rs +++ b/crates/gpui3_macros/src/derive_element.rs @@ -65,7 +65,9 @@ pub fn derive_element(input: TokenStream) -> TokenStream { ) -> Self::ElementState { use gpui3::IntoAnyElement; - self.render(view_state, cx).into_any() + let mut element = self.render(view_state, cx).into_any(); + element.initialize(view_state, cx); + element } fn layout(