diff --git a/crates/gpui3/src/action.rs b/crates/gpui3/src/action.rs index 6a21aee95c34883c150ccbfb42a26ef449f82574..22546a1449e654149eecbe173e0db774a9a4928a 100644 --- a/crates/gpui3/src/action.rs +++ b/crates/gpui3/src/action.rs @@ -10,14 +10,14 @@ pub trait Action: Any + Send + Sync { } #[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct ActionContext { +pub struct DispatchContext { set: HashSet, map: HashMap, } -impl ActionContext { +impl DispatchContext { pub fn new() -> Self { - ActionContext { + DispatchContext { set: HashSet::default(), map: HashMap::default(), } @@ -68,7 +68,7 @@ impl ActionContextPredicate { } } - pub fn eval(&self, contexts: &[ActionContext]) -> bool { + pub fn eval(&self, contexts: &[&DispatchContext]) -> bool { let Some(context) = contexts.first() else { return false; }; diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 20832a1ea38493e0bcef0e1a630869872b11d8b7..961a22a5f95c189ac31ce351af1e56d2f51272e8 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,5 +1,9 @@ -use crate::{BorrowWindow, Bounds, ElementId, FocusHandle, LayoutId, Pixels, Point, ViewContext}; +use crate::{ + BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, LayoutId, + MouseDownEvent, Pixels, Point, Style, StyleRefinement, ViewContext, WindowContext, +}; use derive_more::{Deref, DerefMut}; +use refineable::Refineable; pub(crate) use smallvec::SmallVec; use std::mem; @@ -55,34 +59,94 @@ impl ElementIdentity for Anonymous { } } -pub trait ElementFocusability: 'static + Send + Sync { - fn focus_handle(&self) -> Option<&FocusHandle>; -} +pub trait ElementFocusability: 'static + Send + Sync { + fn as_focusable(&self) -> Option<&Focusable>; -pub struct Focusable(FocusHandle); + fn initialize( + &self, + cx: &mut ViewContext, + f: impl FnOnce(&mut ViewContext) -> R, + ) -> R { + if let Some(focusable) = self.as_focusable() { + for listener in focusable.focus_listeners.iter().cloned() { + cx.on_focus_changed(move |view, event, cx| listener(view, event, cx)); + } + cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx)) + } else { + f(cx) + } + } -impl AsRef for Focusable { - fn as_ref(&self) -> &FocusHandle { - &self.0 + fn refine_style(&self, style: &mut Style, cx: &WindowContext) { + if let Some(focusable) = self.as_focusable() { + if focusable.focus_handle.contains_focused(cx) { + style.refine(&focusable.focus_in_style); + } + + if focusable.focus_handle.within_focused(cx) { + style.refine(&focusable.in_focus_style); + } + + if focusable.focus_handle.is_focused(cx) { + style.refine(&focusable.focus_style); + } + } + } + + fn paint(&self, bounds: Bounds, cx: &mut WindowContext) { + if let Some(focusable) = self.as_focusable() { + let focus_handle = focusable.focus_handle.clone(); + cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + if !cx.default_prevented() { + cx.focus(&focus_handle); + cx.prevent_default(); + } + } + }) + } } } -impl ElementFocusability for Focusable { - fn focus_handle(&self) -> Option<&FocusHandle> { - Some(&self.0) +pub struct Focusable { + pub focus_handle: FocusHandle, + pub focus_listeners: FocusListeners, + pub focus_style: StyleRefinement, + pub focus_in_style: StyleRefinement, + pub in_focus_style: StyleRefinement, +} + +impl ElementFocusability for Focusable +where + V: 'static + Send + Sync, +{ + fn as_focusable(&self) -> Option<&Focusable> { + Some(self) } } -impl From for Focusable { +impl From for Focusable +where + V: 'static + Send + Sync, +{ fn from(value: FocusHandle) -> Self { - Self(value) + Self { + focus_handle: value, + focus_listeners: FocusListeners::default(), + focus_style: StyleRefinement::default(), + focus_in_style: StyleRefinement::default(), + in_focus_style: StyleRefinement::default(), + } } } pub struct NonFocusable; -impl ElementFocusability for NonFocusable { - fn focus_handle(&self) -> Option<&FocusHandle> { +impl ElementFocusability for NonFocusable +where + V: 'static + Send + Sync, +{ + fn as_focusable(&self) -> Option<&Focusable> { None } } diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 356e783971dc26929cc60b897df0f409efcb7fa4..27e245bafba1e32518ea3bd90cfe004089fa836b 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,10 +1,10 @@ use crate::{ Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element, - ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusHandle, Focusable, - GlobalElementId, Hover, Identified, Interactive, IntoAnyElement, KeyDownEvent, KeyMatch, - LayoutId, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, - Overflow, ParentElement, Pixels, Point, ScrollWheelEvent, SharedString, Style, StyleRefinement, - Styled, ViewContext, + ElementFocusability, ElementId, ElementIdentity, Focus, FocusHandle, FocusListeners, Focusable, + GlobalElementId, Hover, Identified, Interactive, InteractiveState, IntoAnyElement, + KeyDownEvent, KeyMatch, LayoutId, MouseClickEvent, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point, ScrollWheelEvent, + SharedString, Style, StyleRefinement, Styled, ViewContext, }; use collections::HashMap; use parking_lot::Mutex; @@ -61,6 +61,23 @@ impl ScrollState { } } +pub struct Div< + V: 'static + Send + Sync, + I: ElementIdentity = Anonymous, + F: ElementFocusability = NonFocusable, +> { + identity: I, + focusability: F, + children: SmallVec<[AnyElement; 2]>, + group: Option, + base_style: StyleRefinement, + hover_style: StyleRefinement, + group_hover: Option, + active_style: StyleRefinement, + group_active: Option, + interactive_state: InteractiveState, +} + pub fn div() -> Div where V: 'static + Send + Sync, @@ -75,33 +92,10 @@ 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(), + interactive_state: InteractiveState::default(), } } -pub struct Div< - V: 'static + Send + Sync, - I: ElementIdentity = Anonymous, - F: ElementFocusability = NonFocusable, -> { - identity: I, - focusability: F, - children: SmallVec<[AnyElement; 2]>, - group: Option, - base_style: StyleRefinement, - hover_style: StyleRefinement, - group_hover: Option, - active_style: StyleRefinement, - group_active: Option, - focus_style: StyleRefinement, - focus_in_style: StyleRefinement, - in_focus_style: StyleRefinement, - listeners: EventListeners, -} - struct GroupStyle { group: SharedString, style: StyleRefinement, @@ -109,7 +103,7 @@ struct GroupStyle { impl Div where - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { pub fn id(self, id: impl Into) -> Div { @@ -123,10 +117,7 @@ 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, + interactive_state: self.interactive_state, } } } @@ -134,7 +125,7 @@ where impl Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { pub fn group(mut self, group: impl Into) -> Self { @@ -206,19 +197,7 @@ 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); - } - } + self.focusability.refine_style(&mut computed_style, cx); let mouse_position = cx.mouse_position(); @@ -296,7 +275,7 @@ where pending_click: Arc>>, cx: &mut ViewContext, ) { - let click_listeners = mem::take(&mut self.listeners.mouse_click); + let click_listeners = mem::take(&mut self.interactive_state.mouse_click); let mouse_down = pending_click.lock().clone(); if let Some(mouse_down) = mouse_down { @@ -321,37 +300,25 @@ where }); } - if let Some(focus_handle) = self.focusability.focus_handle() { - let focus_handle = focus_handle.clone(); - cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { - if !cx.default_prevented() { - cx.focus(&focus_handle); - cx.prevent_default(); - } - } - }) - } - - for listener in mem::take(&mut self.listeners.mouse_down) { + for listener in mem::take(&mut self.interactive_state.mouse_down) { cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| { listener(state, event, &bounds, phase, cx); }) } - for listener in mem::take(&mut self.listeners.mouse_up) { + for listener in mem::take(&mut self.interactive_state.mouse_up) { cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { listener(state, event, &bounds, phase, cx); }) } - for listener in mem::take(&mut self.listeners.mouse_move) { + for listener in mem::take(&mut self.interactive_state.mouse_move) { cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| { listener(state, event, &bounds, phase, cx); }) } - for listener in mem::take(&mut self.listeners.scroll_wheel) { + for listener in mem::take(&mut self.interactive_state.scroll_wheel) { cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| { listener(state, event, &bounds, phase, cx); }) @@ -364,7 +331,7 @@ 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(), @@ -375,40 +342,41 @@ 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, + interactive_state: self.interactive_state, } } } -impl Focus for Div +impl Focus for Div> where I: ElementIdentity, V: 'static + Send + Sync, { + fn focus_listeners(&mut self) -> &mut FocusListeners { + &mut self.focusability.focus_listeners + } + fn handle(&self) -> &FocusHandle { - self.focusability.as_ref() + &self.focusability.focus_handle } fn set_focus_style(&mut self, style: StyleRefinement) { - self.focus_style = style; + self.focusability.focus_style = style; } fn set_focus_in_style(&mut self, style: StyleRefinement) { - self.focus_in_style = style; + self.focusability.focus_in_style = style; } fn set_in_focus_style(&mut self, style: StyleRefinement) { - self.in_focus_style = style; + self.focusability.in_focus_style = style; } } impl Element for Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { type ViewState = V; @@ -426,20 +394,16 @@ where ) -> Self::ElementState { self.with_element_id(cx, |this, global_id, cx| { let element_state = element_state.unwrap_or_default(); - for listener in this.listeners.focus.iter().cloned() { - cx.on_focus_changed(move |view, event, cx| listener(view, event, cx)); - } - - let mut key_listeners = mem::take(&mut this.listeners.key); + let mut key_listeners = mem::take(&mut this.interactive_state.key); if let Some(global_id) = global_id { key_listeners.push(( TypeId::of::(), - Arc::new(move |_, key_down, phase, cx| { + Arc::new(move |_, key_down, context, phase, cx| { if phase == DispatchPhase::Bubble { let key_down = key_down.downcast_ref::().unwrap(); if let KeyMatch::Some(action) = - cx.match_keystroke(&global_id, &key_down.keystroke) + cx.match_keystroke(&global_id, &key_down.keystroke, context) { return Some(action); } @@ -451,19 +415,13 @@ where } cx.with_key_listeners(&key_listeners, |cx| { - if let Some(focus_handle) = this.focusability.focus_handle().cloned() { - cx.with_focus(focus_handle, |cx| { - for child in &mut this.children { - child.initialize(view_state, cx); - } - }) - } else { + this.focusability.initialize(cx, |cx| { for child in &mut this.children { child.initialize(view_state, cx); } - } + }); }); - this.listeners.key = key_listeners; + this.interactive_state.key = key_listeners; element_state }) @@ -526,6 +484,7 @@ where element_state.active_state.clone(), cx, ); + this.focusability.paint(bounds, cx); this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx); }); @@ -554,7 +513,7 @@ where impl IntoAnyElement for Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { fn into_any(self) -> AnyElement { @@ -565,7 +524,7 @@ where impl ParentElement for Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { @@ -576,7 +535,7 @@ where impl Styled for Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { fn style(&mut self) -> &mut StyleRefinement { @@ -587,18 +546,18 @@ where impl Interactive for Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { - fn listeners(&mut self) -> &mut EventListeners { - &mut self.listeners + fn interactive_state(&mut self) -> &mut InteractiveState { + &mut self.interactive_state } } impl Hover for Div where I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { fn set_hover_style(&mut self, group: Option, style: StyleRefinement) { @@ -612,14 +571,14 @@ where impl Click for Div where - F: ElementFocusability, + F: ElementFocusability, V: 'static + Send + Sync, { } impl Active for Div where - F: ElementFocusability, + 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 2cd411f0856af226a88b8580ab0bcbd17834fdf3..d5db8032d725b2d9a52f25ac767fdf225649ca71 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -1,8 +1,8 @@ use crate::{ div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element, - ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover, - Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, - StyleRefinement, Styled, ViewContext, + ElementFocusability, ElementId, ElementIdentity, Focus, FocusListeners, Focusable, Hover, + Identified, Interactive, InteractiveState, IntoAnyElement, LayoutId, NonFocusable, Pixels, + SharedString, StyleRefinement, Styled, ViewContext, }; use futures::FutureExt; use util::ResultExt; @@ -10,7 +10,7 @@ use util::ResultExt; pub struct Img< V: 'static + Send + Sync, I: ElementIdentity = Anonymous, - F: ElementFocusability = NonFocusable, + F: ElementFocusability = NonFocusable, > { base: Div, uri: Option, @@ -32,7 +32,7 @@ impl Img where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { pub fn uri(mut self, uri: impl Into) -> Self { self.uri = Some(uri.into()); @@ -48,7 +48,7 @@ where impl Img where V: 'static + Send + Sync, - F: ElementFocusability, + F: ElementFocusability, { pub fn id(self, id: impl Into) -> Img { Img { @@ -63,7 +63,7 @@ impl IntoAnyElement for Img where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { fn into_any(self) -> AnyElement { AnyElement::new(self) @@ -74,7 +74,7 @@ impl Element for Img where V: Send + Sync + 'static, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { type ViewState = V; type ElementState = DivState; @@ -143,7 +143,7 @@ impl Styled for Img where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { fn style(&mut self) -> &mut StyleRefinement { self.base.style() @@ -154,10 +154,10 @@ impl Interactive for Img where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { - fn listeners(&mut self) -> &mut EventListeners { - self.base.listeners() + fn interactive_state(&mut self) -> &mut InteractiveState { + self.base.interactive_state() } } @@ -165,7 +165,7 @@ impl Hover for Img where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { fn set_hover_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_hover_style(group, style); @@ -175,25 +175,29 @@ where impl Click for Img where V: 'static + Send + Sync, - F: ElementFocusability, + F: ElementFocusability, { } impl Active for Img where V: 'static + Send + Sync, - F: ElementFocusability, + F: ElementFocusability, { fn set_active_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_active_style(group, style) } } -impl Focus for Img +impl Focus for Img> where V: 'static + Send + Sync, I: ElementIdentity, { + fn focus_listeners(&mut self) -> &mut FocusListeners { + self.base.focus_listeners() + } + fn set_focus_style(&mut self, style: StyleRefinement) { self.base.set_focus_style(style) } diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index 17be310457db6a929e1882d52ab0ddf4dea8a292..b3d8c66b19023dd593c4fdedd5c1f117dfe414d9 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -1,15 +1,15 @@ use crate::{ 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, + ElementId, ElementIdentity, Focus, FocusListeners, Focusable, Hover, Identified, Interactive, + InteractiveState, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, + StyleRefinement, Styled, ViewContext, }; use util::ResultExt; pub struct Svg< V: 'static + Send + Sync, I: ElementIdentity = Anonymous, - F: ElementFocusability = NonFocusable, + F: ElementFocusability = NonFocusable, > { base: Div, path: Option, @@ -29,7 +29,7 @@ impl Svg where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { pub fn path(mut self, path: impl Into) -> Self { self.path = Some(path.into()); @@ -40,7 +40,7 @@ where impl Svg where V: 'static + Send + Sync, - F: ElementFocusability, + F: ElementFocusability, { pub fn id(self, id: impl Into) -> Svg { Svg { @@ -54,7 +54,7 @@ impl IntoAnyElement for Svg where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { fn into_any(self) -> AnyElement { AnyElement::new(self) @@ -65,7 +65,7 @@ impl Element for Svg where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { type ViewState = V; type ElementState = DivState; @@ -117,7 +117,7 @@ impl Styled for Svg where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { fn style(&mut self) -> &mut StyleRefinement { self.base.style() @@ -128,10 +128,10 @@ impl Interactive for Svg where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { - fn listeners(&mut self) -> &mut EventListeners { - self.base.listeners() + fn interactive_state(&mut self) -> &mut InteractiveState { + self.base.interactive_state() } } @@ -139,7 +139,7 @@ impl Hover for Svg where V: 'static + Send + Sync, I: ElementIdentity, - F: ElementFocusability, + F: ElementFocusability, { fn set_hover_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_hover_style(group, style); @@ -149,25 +149,29 @@ where impl Click for Svg where V: 'static + Send + Sync, - F: ElementFocusability, + F: ElementFocusability, { } impl Active for Svg where V: 'static + Send + Sync, - F: ElementFocusability, + F: ElementFocusability, { fn set_active_style(&mut self, group: Option, style: StyleRefinement) { self.base.set_active_style(group, style) } } -impl Focus for Svg +impl Focus for Svg> where V: 'static + Send + Sync, I: ElementIdentity, { + fn focus_listeners(&mut self) -> &mut FocusListeners { + self.base.focus_listeners() + } + fn set_focus_style(&mut self, style: StyleRefinement) { self.base.set_focus_style(style) } diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs deleted file mode 100644 index 10eeb688cc52bd5695ee436683df2e293e87ed57..0000000000000000000000000000000000000000 --- a/crates/gpui3/src/events.rs +++ /dev/null @@ -1,290 +0,0 @@ -use crate::{ - point, Action, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, - ViewContext, -}; -use smallvec::SmallVec; -use std::{ - any::{Any, TypeId}, - ops::Deref, - sync::Arc, -}; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct KeyDownEvent { - pub keystroke: Keystroke, - pub is_held: bool, -} - -#[derive(Clone, Debug)] -pub struct KeyUpEvent { - pub keystroke: Keystroke, -} - -#[derive(Clone, Debug, Default)] -pub struct ModifiersChangedEvent { - pub modifiers: Modifiers, -} - -impl Deref for ModifiersChangedEvent { - type Target = Modifiers; - - fn deref(&self) -> &Self::Target { - &self.modifiers - } -} - -/// The phase of a touch motion event. -/// Based on the winit enum of the same name. -#[derive(Clone, Copy, Debug)] -pub enum TouchPhase { - Started, - Moved, - Ended, -} - -#[derive(Clone, Debug, Default)] -pub struct MouseDownEvent { - pub button: MouseButton, - pub position: Point, - pub modifiers: Modifiers, - pub click_count: usize, -} - -#[derive(Clone, Debug, Default)] -pub struct MouseUpEvent { - pub button: MouseButton, - pub position: Point, - pub modifiers: Modifiers, - pub click_count: usize, -} - -#[derive(Clone, Debug, Default)] -pub struct MouseClickEvent { - pub down: MouseDownEvent, - pub up: MouseUpEvent, -} - -#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] -pub enum MouseButton { - Left, - Right, - Middle, - Navigate(NavigationDirection), -} - -impl MouseButton { - pub fn all() -> Vec { - vec![ - MouseButton::Left, - MouseButton::Right, - MouseButton::Middle, - MouseButton::Navigate(NavigationDirection::Back), - MouseButton::Navigate(NavigationDirection::Forward), - ] - } -} - -impl Default for MouseButton { - fn default() -> Self { - Self::Left - } -} - -#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] -pub enum NavigationDirection { - Back, - Forward, -} - -impl Default for NavigationDirection { - fn default() -> Self { - Self::Back - } -} - -#[derive(Clone, Debug, Default)] -pub struct MouseMoveEvent { - pub position: Point, - pub pressed_button: Option, - pub modifiers: Modifiers, -} - -#[derive(Clone, Debug)] -pub struct ScrollWheelEvent { - pub position: Point, - pub delta: ScrollDelta, - pub modifiers: Modifiers, - pub touch_phase: TouchPhase, -} - -impl Deref for ScrollWheelEvent { - type Target = Modifiers; - - fn deref(&self) -> &Self::Target { - &self.modifiers - } -} - -#[derive(Clone, Copy, Debug)] -pub enum ScrollDelta { - Pixels(Point), - Lines(Point), -} - -impl Default for ScrollDelta { - fn default() -> Self { - Self::Lines(Default::default()) - } -} - -impl ScrollDelta { - pub fn precise(&self) -> bool { - match self { - ScrollDelta::Pixels(_) => true, - ScrollDelta::Lines(_) => false, - } - } - - pub fn pixel_delta(&self, line_height: Pixels) -> Point { - match self { - ScrollDelta::Pixels(delta) => *delta, - ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y), - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct MouseExitEvent { - pub position: Point, - pub pressed_button: Option, - pub modifiers: Modifiers, -} - -impl Deref for MouseExitEvent { - type Target = Modifiers; - - fn deref(&self) -> &Self::Target { - &self.modifiers - } -} - -#[derive(Clone, Debug)] -pub enum InputEvent { - KeyDown(KeyDownEvent), - KeyUp(KeyUpEvent), - ModifiersChanged(ModifiersChangedEvent), - MouseDown(MouseDownEvent), - MouseUp(MouseUpEvent), - MouseMoved(MouseMoveEvent), - MouseExited(MouseExitEvent), - ScrollWheel(ScrollWheelEvent), -} - -impl InputEvent { - pub fn position(&self) -> Option> { - match self { - InputEvent::KeyDown { .. } => None, - InputEvent::KeyUp { .. } => None, - InputEvent::ModifiersChanged { .. } => None, - InputEvent::MouseDown(event) => Some(event.position), - InputEvent::MouseUp(event) => Some(event.position), - InputEvent::MouseMoved(event) => Some(event.position), - InputEvent::MouseExited(event) => Some(event.position), - InputEvent::ScrollWheel(event) => Some(event.position), - } - } - - pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> { - match self { - InputEvent::KeyDown { .. } => None, - InputEvent::KeyUp { .. } => None, - InputEvent::ModifiersChanged { .. } => None, - InputEvent::MouseDown(event) => Some(event), - InputEvent::MouseUp(event) => Some(event), - InputEvent::MouseMoved(event) => Some(event), - InputEvent::MouseExited(event) => Some(event), - InputEvent::ScrollWheel(event) => Some(event), - } - } - - pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> { - match self { - InputEvent::KeyDown(event) => Some(event), - InputEvent::KeyUp(event) => Some(event), - InputEvent::ModifiersChanged(event) => Some(event), - InputEvent::MouseDown(_) => None, - InputEvent::MouseUp(_) => None, - InputEvent::MouseMoved(_) => None, - InputEvent::MouseExited(_) => None, - InputEvent::ScrollWheel(_) => None, - } - } -} - -pub struct FocusEvent { - pub blurred: Option, - 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 KeyListener = Arc< - dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext) -> Option> - + Send - + Sync - + 'static, ->; - -pub type FocusListener = - 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: SmallVec<[(TypeId, KeyListener); 32]>, - pub focus: SmallVec<[FocusListener; 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: SmallVec::new(), - focus: SmallVec::new(), - } - } -} diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs index c7cefbe27885f83c91955c06fb1c9fb53dd794cc..16595f2e808e28fde14d373084cccab4c70d439f 100644 --- a/crates/gpui3/src/focus.rs +++ b/crates/gpui3/src/focus.rs @@ -1,7 +1,14 @@ -use crate::{FocusEvent, FocusHandle, Interactive, StyleRefinement, ViewContext}; +use crate::{Element, FocusEvent, FocusHandle, StyleRefinement, ViewContext}; +use smallvec::SmallVec; use std::sync::Arc; -pub trait Focus: Interactive { +pub type FocusListeners = SmallVec<[FocusListener; 2]>; + +pub type FocusListener = + Arc) + Send + Sync + 'static>; + +pub trait Focus: Element { + fn focus_listeners(&mut self) -> &mut FocusListeners; 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); @@ -42,8 +49,7 @@ pub trait Focus: Interactive { Self: Sized, { let handle = self.handle().clone(); - self.listeners() - .focus + self.focus_listeners() .push(Arc::new(move |view, event, cx| { if event.focused.as_ref() == Some(&handle) { listener(view, event, cx) @@ -63,8 +69,7 @@ pub trait Focus: Interactive { Self: Sized, { let handle = self.handle().clone(); - self.listeners() - .focus + self.focus_listeners() .push(Arc::new(move |view, event, cx| { if event.blurred.as_ref() == Some(&handle) { listener(view, event, cx) @@ -84,8 +89,7 @@ pub trait Focus: Interactive { Self: Sized, { let handle = self.handle().clone(); - self.listeners() - .focus + self.focus_listeners() .push(Arc::new(move |view, event, cx| { let descendant_blurred = event .blurred @@ -114,8 +118,7 @@ pub trait Focus: Interactive { Self: Sized, { let handle = self.handle().clone(); - self.listeners() - .focus + self.focus_listeners() .push(Arc::new(move |view, event, cx| { let descendant_blurred = event .blurred diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index fbc9d8d0f2af3141d3b181828d2ed0b9ea5f8d4d..800593e4c189641f9a6bce65ceb982aa598ad08a 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -5,7 +5,6 @@ mod assets; mod color; mod element; mod elements; -mod events; mod executor; mod focus; mod geometry; @@ -33,7 +32,6 @@ pub use assets::*; pub use color::*; pub use element::*; pub use elements::*; -pub use events::*; pub use executor::*; pub use focus::*; pub use geometry::*; diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index 9d1197419f114ac2ce1b807bb8cbb1d3ce9f14be..d67a5d75787348cf6de01a8e6e0c105558affe53 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,12 +1,17 @@ -use std::{any::TypeId, sync::Arc}; +use smallvec::SmallVec; use crate::{ - DispatchPhase, Element, EventListeners, KeyDownEvent, KeyUpEvent, MouseButton, MouseClickEvent, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext, + point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke, + Modifiers, Pixels, Point, ViewContext, +}; +use std::{ + any::{Any, TypeId}, + ops::Deref, + sync::Arc, }; pub trait Interactive: Element { - fn listeners(&mut self) -> &mut EventListeners; + fn interactive_state(&mut self) -> &mut InteractiveState; fn on_mouse_down( mut self, @@ -19,16 +24,16 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners() - .mouse_down - .push(Arc::new(move |view, event, bounds, phase, cx| { + self.interactive_state().mouse_down.push(Arc::new( + move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && event.button == button && bounds.contains_point(&event.position) { handler(view, event, cx) } - })); + }, + )); self } @@ -43,7 +48,7 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners() + self.interactive_state() .mouse_up .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble @@ -67,16 +72,16 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners() - .mouse_down - .push(Arc::new(move |view, event, bounds, phase, cx| { + self.interactive_state().mouse_down.push(Arc::new( + move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture && event.button == button && !bounds.contains_point(&event.position) { handler(view, event, cx) } - })); + }, + )); self } @@ -91,7 +96,7 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners() + self.interactive_state() .mouse_up .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture @@ -114,13 +119,13 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners() - .mouse_move - .push(Arc::new(move |view, event, bounds, phase, cx| { + self.interactive_state().mouse_move.push(Arc::new( + move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { handler(view, event, cx); } - })); + }, + )); self } @@ -134,13 +139,13 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners() - .scroll_wheel - .push(Arc::new(move |view, event, bounds, phase, cx| { + self.interactive_state().scroll_wheel.push(Arc::new( + move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { handler(view, event, cx); } - })); + }, + )); self } @@ -158,9 +163,9 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners().key.push(( + self.interactive_state().key.push(( TypeId::of::(), - Arc::new(move |view, event, phase, cx| { + Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); None @@ -179,9 +184,9 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners().key.push(( + self.interactive_state().key.push(( TypeId::of::(), - Arc::new(move |view, event, phase, cx| { + Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); None @@ -200,9 +205,9 @@ pub trait Interactive: Element { where Self: Sized, { - self.listeners().key.push(( + self.interactive_state().key.push(( TypeId::of::(), - Arc::new(move |view, event, phase, cx| { + Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); None @@ -223,9 +228,290 @@ pub trait Click: Interactive { where Self: Sized, { - self.listeners() + self.interactive_state() .mouse_click .push(Arc::new(move |view, event, cx| handler(view, event, cx))); self } } + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct KeyDownEvent { + pub keystroke: Keystroke, + pub is_held: bool, +} + +#[derive(Clone, Debug)] +pub struct KeyUpEvent { + pub keystroke: Keystroke, +} + +#[derive(Clone, Debug, Default)] +pub struct ModifiersChangedEvent { + pub modifiers: Modifiers, +} + +impl Deref for ModifiersChangedEvent { + type Target = Modifiers; + + fn deref(&self) -> &Self::Target { + &self.modifiers + } +} + +/// The phase of a touch motion event. +/// Based on the winit enum of the same name. +#[derive(Clone, Copy, Debug)] +pub enum TouchPhase { + Started, + Moved, + Ended, +} + +#[derive(Clone, Debug, Default)] +pub struct MouseDownEvent { + pub button: MouseButton, + pub position: Point, + pub modifiers: Modifiers, + pub click_count: usize, +} + +#[derive(Clone, Debug, Default)] +pub struct MouseUpEvent { + pub button: MouseButton, + pub position: Point, + pub modifiers: Modifiers, + pub click_count: usize, +} + +#[derive(Clone, Debug, Default)] +pub struct MouseClickEvent { + pub down: MouseDownEvent, + pub up: MouseUpEvent, +} + +#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] +pub enum MouseButton { + Left, + Right, + Middle, + Navigate(NavigationDirection), +} + +impl MouseButton { + pub fn all() -> Vec { + vec![ + MouseButton::Left, + MouseButton::Right, + MouseButton::Middle, + MouseButton::Navigate(NavigationDirection::Back), + MouseButton::Navigate(NavigationDirection::Forward), + ] + } +} + +impl Default for MouseButton { + fn default() -> Self { + Self::Left + } +} + +#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] +pub enum NavigationDirection { + Back, + Forward, +} + +impl Default for NavigationDirection { + fn default() -> Self { + Self::Back + } +} + +#[derive(Clone, Debug, Default)] +pub struct MouseMoveEvent { + pub position: Point, + pub pressed_button: Option, + pub modifiers: Modifiers, +} + +#[derive(Clone, Debug)] +pub struct ScrollWheelEvent { + pub position: Point, + pub delta: ScrollDelta, + pub modifiers: Modifiers, + pub touch_phase: TouchPhase, +} + +impl Deref for ScrollWheelEvent { + type Target = Modifiers; + + fn deref(&self) -> &Self::Target { + &self.modifiers + } +} + +#[derive(Clone, Copy, Debug)] +pub enum ScrollDelta { + Pixels(Point), + Lines(Point), +} + +impl Default for ScrollDelta { + fn default() -> Self { + Self::Lines(Default::default()) + } +} + +impl ScrollDelta { + pub fn precise(&self) -> bool { + match self { + ScrollDelta::Pixels(_) => true, + ScrollDelta::Lines(_) => false, + } + } + + pub fn pixel_delta(&self, line_height: Pixels) -> Point { + match self { + ScrollDelta::Pixels(delta) => *delta, + ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct MouseExitEvent { + pub position: Point, + pub pressed_button: Option, + pub modifiers: Modifiers, +} + +impl Deref for MouseExitEvent { + type Target = Modifiers; + + fn deref(&self) -> &Self::Target { + &self.modifiers + } +} + +#[derive(Clone, Debug)] +pub enum InputEvent { + KeyDown(KeyDownEvent), + KeyUp(KeyUpEvent), + ModifiersChanged(ModifiersChangedEvent), + MouseDown(MouseDownEvent), + MouseUp(MouseUpEvent), + MouseMoved(MouseMoveEvent), + MouseExited(MouseExitEvent), + ScrollWheel(ScrollWheelEvent), +} + +impl InputEvent { + pub fn position(&self) -> Option> { + match self { + InputEvent::KeyDown { .. } => None, + InputEvent::KeyUp { .. } => None, + InputEvent::ModifiersChanged { .. } => None, + InputEvent::MouseDown(event) => Some(event.position), + InputEvent::MouseUp(event) => Some(event.position), + InputEvent::MouseMoved(event) => Some(event.position), + InputEvent::MouseExited(event) => Some(event.position), + InputEvent::ScrollWheel(event) => Some(event.position), + } + } + + pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> { + match self { + InputEvent::KeyDown { .. } => None, + InputEvent::KeyUp { .. } => None, + InputEvent::ModifiersChanged { .. } => None, + InputEvent::MouseDown(event) => Some(event), + InputEvent::MouseUp(event) => Some(event), + InputEvent::MouseMoved(event) => Some(event), + InputEvent::MouseExited(event) => Some(event), + InputEvent::ScrollWheel(event) => Some(event), + } + } + + pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> { + match self { + InputEvent::KeyDown(event) => Some(event), + InputEvent::KeyUp(event) => Some(event), + InputEvent::ModifiersChanged(event) => Some(event), + InputEvent::MouseDown(_) => None, + InputEvent::MouseUp(_) => None, + InputEvent::MouseMoved(_) => None, + InputEvent::MouseExited(_) => None, + InputEvent::ScrollWheel(_) => None, + } + } +} + +pub struct FocusEvent { + pub blurred: Option, + 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 KeyListener = Arc< + dyn Fn( + &mut V, + &dyn Any, + &[&DispatchContext], + DispatchPhase, + &mut ViewContext, + ) -> Option> + + Send + + Sync + + 'static, +>; + +pub struct InteractiveState { + 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: SmallVec<[(TypeId, KeyListener); 32]>, +} + +impl Default for InteractiveState { + fn default() -> Self { + Self { + mouse_down: SmallVec::new(), + mouse_up: SmallVec::new(), + mouse_click: SmallVec::new(), + mouse_move: SmallVec::new(), + scroll_wheel: SmallVec::new(), + key: SmallVec::new(), + } + } +} diff --git a/crates/gpui3/src/keymap/binding.rs b/crates/gpui3/src/keymap/binding.rs index 2860ef52e78f8e040b68301edc228a2158b30c71..27289d0c674df508c49c4f2ab9c80e8027075718 100644 --- a/crates/gpui3/src/keymap/binding.rs +++ b/crates/gpui3/src/keymap/binding.rs @@ -1,4 +1,4 @@ -use crate::{Action, ActionContext, ActionContextPredicate, KeyMatch, Keystroke}; +use crate::{Action, ActionContextPredicate, DispatchContext, KeyMatch, Keystroke}; use anyhow::Result; use smallvec::SmallVec; @@ -32,7 +32,7 @@ impl KeyBinding { }) } - pub fn matches_context(&self, contexts: &[ActionContext]) -> bool { + pub fn matches_context(&self, contexts: &[&DispatchContext]) -> bool { self.context_predicate .as_ref() .map(|predicate| predicate.eval(contexts)) @@ -42,7 +42,7 @@ impl KeyBinding { pub fn match_keystrokes( &self, pending_keystrokes: &[Keystroke], - contexts: &[ActionContext], + contexts: &[&DispatchContext], ) -> KeyMatch { if self.keystrokes.as_ref().starts_with(&pending_keystrokes) && self.matches_context(contexts) @@ -61,7 +61,7 @@ impl KeyBinding { pub fn keystrokes_for_action( &self, action: &dyn Action, - contexts: &[ActionContext], + contexts: &[&DispatchContext], ) -> Option> { if self.action.eq(action) && self.matches_context(contexts) { Some(self.keystrokes.clone()) diff --git a/crates/gpui3/src/keymap/matcher.rs b/crates/gpui3/src/keymap/matcher.rs index 1718a871f99f87553e1ad58c8b49f79b0e4f201f..494285aaea6b7e644d5f0addbdad4e471444a3cc 100644 --- a/crates/gpui3/src/keymap/matcher.rs +++ b/crates/gpui3/src/keymap/matcher.rs @@ -1,4 +1,4 @@ -use crate::{Action, ActionContext, Keymap, KeymapVersion, Keystroke}; +use crate::{Action, DispatchContext, Keymap, KeymapVersion, Keystroke}; use parking_lot::RwLock; use smallvec::SmallVec; use std::sync::Arc; @@ -44,7 +44,7 @@ impl KeyMatcher { pub fn match_keystroke( &mut self, keystroke: &Keystroke, - context_stack: &[ActionContext], + context_stack: &[&DispatchContext], ) -> KeyMatch { let keymap = self.keymap.read(); // Clear pending keystrokes if the keymap has changed since the last matched keystroke. @@ -86,7 +86,7 @@ impl KeyMatcher { pub fn keystrokes_for_action( &self, action: &dyn Action, - contexts: &[ActionContext], + contexts: &[&DispatchContext], ) -> Option> { self.keymap .read() diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 5a68e0dfd2a55fe343a57435084e1bd68fa4767a..d1af98b667c331213bf87dbd4b2d3033640af81b 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,13 +1,13 @@ use crate::{ px, size, Action, 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, KeyListener, KeyMatch, KeyMatcher, Keystroke, 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, + BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, + DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, + GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, + Keystroke, 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; @@ -47,7 +47,12 @@ pub enum DispatchPhase { type AnyListener = Arc; type AnyKeyListener = Arc< - dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) -> Option> + dyn Fn( + &dyn Any, + &[&DispatchContext], + DispatchPhase, + &mut WindowContext, + ) -> Option> + Send + Sync + 'static, @@ -155,8 +160,8 @@ pub struct Window { z_index_stack: StackingOrder, content_mask_stack: Vec>, mouse_listeners: HashMap>, - key_listeners: Vec<(TypeId, AnyKeyListener)>, - key_events_enabled: bool, + key_dispatch_stack: Vec, + freeze_key_dispatch_stack: bool, focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, @@ -230,24 +235,32 @@ impl Window { z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), mouse_listeners: HashMap::default(), - key_listeners: Vec::new(), - key_events_enabled: true, + key_dispatch_stack: Vec::new(), + freeze_key_dispatch_stack: false, focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), focus_listeners: Vec::new(), + focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), propagate: true, default_prevented: true, mouse_position, scale_factor, scene_builder: SceneBuilder::new(), dirty: true, - focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), last_blur: None, focus: None, } } } +enum KeyDispatchStackFrame { + Listener { + event_type: TypeId, + listener: AnyKeyListener, + }, + Context(DispatchContext), +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] #[repr(C)] pub struct ContentMask { @@ -833,9 +846,9 @@ 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_listeners.clear(); + window.key_dispatch_stack.clear(); window.focus_parents_by_child.clear(); - window.key_events_enabled = true; + window.freeze_key_dispatch_stack = false; } fn dispatch_event(&mut self, event: InputEvent) -> bool { @@ -888,36 +901,67 @@ impl<'a, 'w> WindowContext<'a, 'w> { .insert(any_mouse_event.type_id(), handlers); } } else if let Some(any_key_event) = event.keyboard_event() { - let key_listeners = mem::take(&mut self.window.key_listeners); + let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack); let key_event_type = any_key_event.type_id(); - - for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate() { - if key_event_type == *listener_event_type { - if let Some(action) = listener(any_key_event, DispatchPhase::Capture, self) { - self.dispatch_action(action, &key_listeners[..ix]); + let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new(); + + for (ix, frame) in key_dispatch_stack.iter().enumerate() { + match frame { + KeyDispatchStackFrame::Listener { + event_type, + listener, + } => { + if key_event_type == *event_type { + if let Some(action) = listener( + any_key_event, + &context_stack, + DispatchPhase::Capture, + self, + ) { + self.dispatch_action(action, &key_dispatch_stack[..ix]); + } + if !self.window.propagate { + break; + } + } } - if !self.window.propagate { - break; + KeyDispatchStackFrame::Context(context) => { + context_stack.push(&context); } } } if self.window.propagate { - for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate().rev() - { - if key_event_type == *listener_event_type { - if let Some(action) = listener(any_key_event, DispatchPhase::Bubble, self) { - self.dispatch_action(action, &key_listeners[..ix]); - } + for (ix, frame) in key_dispatch_stack.iter().enumerate().rev() { + match frame { + KeyDispatchStackFrame::Listener { + event_type, + listener, + } => { + if key_event_type == *event_type { + if let Some(action) = listener( + any_key_event, + &context_stack, + DispatchPhase::Bubble, + self, + ) { + self.dispatch_action(action, &key_dispatch_stack[..ix]); + } - if !self.window.propagate { - break; + if !self.window.propagate { + break; + } + } + } + KeyDispatchStackFrame::Context(_) => { + context_stack.pop(); } } } } - self.window.key_listeners = key_listeners; + drop(context_stack); + self.window.key_dispatch_stack = key_dispatch_stack; } true @@ -927,13 +971,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { &mut self, element_id: &GlobalElementId, keystroke: &Keystroke, + context_stack: &[&DispatchContext], ) -> KeyMatch { let key_match = self .window .key_matchers .get_mut(element_id) .unwrap() - .match_keystroke(keystroke, &[]); + .match_keystroke(keystroke, context_stack); if key_match.is_some() { for matcher in self.window.key_matchers.values_mut() { @@ -944,23 +989,39 @@ impl<'a, 'w> WindowContext<'a, 'w> { key_match } - fn dispatch_action(&mut self, action: Box, listeners: &[(TypeId, AnyKeyListener)]) { + fn dispatch_action( + &mut self, + action: Box, + dispatch_stack: &[KeyDispatchStackFrame], + ) { let action_type = action.as_any().type_id(); - for (event_type, listener) in listeners { - if action_type == *event_type { - listener(action.as_any(), DispatchPhase::Capture, self); - if !self.window.propagate { - break; + for stack_frame in dispatch_stack { + if let KeyDispatchStackFrame::Listener { + event_type, + listener, + } = stack_frame + { + if action_type == *event_type { + listener(action.as_any(), &[], DispatchPhase::Capture, self); + if !self.window.propagate { + break; + } } } } if self.window.propagate { - for (event_type, listener) in listeners.iter().rev() { - if action_type == *event_type { - listener(action.as_any(), DispatchPhase::Bubble, self); - if !self.window.propagate { - break; + for stack_frame in dispatch_stack.iter().rev() { + if let KeyDispatchStackFrame::Listener { + event_type, + listener, + } = stack_frame + { + if action_type == *event_type { + listener(action.as_any(), &[], DispatchPhase::Bubble, self); + if !self.window.propagate { + break; + } } } } @@ -1287,26 +1348,56 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { key_listeners: &[(TypeId, KeyListener)], f: impl FnOnce(&mut Self) -> R, ) -> R { - if self.window.key_events_enabled { + if !self.window.freeze_key_dispatch_stack { for (event_type, listener) in key_listeners.iter().cloned() { let handle = self.handle(); let listener = Arc::new( - move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_, '_>| { + move |event: &dyn Any, + context_stack: &[&DispatchContext], + phase: DispatchPhase, + cx: &mut WindowContext<'_, '_>| { handle - .update(cx, |view, cx| listener(view, event, phase, cx)) + .update(cx, |view, cx| { + listener(view, event, context_stack, phase, cx) + }) .log_err() .flatten() }, ); - self.window.key_listeners.push((event_type, listener)); + self.window + .key_dispatch_stack + .push(KeyDispatchStackFrame::Listener { + event_type, + listener, + }); } } let result = f(self); - if self.window.key_events_enabled { - let prev_len = self.window.key_listeners.len() - key_listeners.len(); - self.window.key_listeners.truncate(prev_len); + if !self.window.freeze_key_dispatch_stack { + let prev_len = self.window.key_dispatch_stack.len() - key_listeners.len(); + self.window.key_dispatch_stack.truncate(prev_len); + } + + result + } + + pub fn with_key_dispatch_context( + &mut self, + context: DispatchContext, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + if !self.window.freeze_key_dispatch_stack { + self.window + .key_dispatch_stack + .push(KeyDispatchStackFrame::Context(context)); + } + + let result = f(self); + + if !self.window.freeze_key_dispatch_stack { + self.window.key_dispatch_stack.pop(); } result @@ -1325,7 +1416,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { self.window.focus_stack.push(focus_handle.id); if Some(focus_handle.id) == self.window.focus { - self.window.key_events_enabled = false; + self.window.freeze_key_dispatch_stack = true; } let result = f(self);