Detailed changes
@@ -8,9 +8,10 @@ pub use model_context::*;
use refineable::Refineable;
use crate::{
- current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId,
- MainThread, MainThreadOnly, Platform, SubscriberSet, SvgRenderer, Task, TextStyle,
- TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
+ current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor,
+ FocusEvent, FocusHandle, FocusId, LayoutId, MainThread, MainThreadOnly, Platform,
+ SubscriberSet, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
+ WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@@ -54,6 +55,7 @@ impl App {
this: this.clone(),
text_system: Arc::new(TextSystem::new(platform.text_system())),
pending_updates: 0,
+ flushing_effects: false,
next_frame_callbacks: Default::default(),
platform: MainThreadOnly::new(platform, executor.clone()),
executor,
@@ -97,6 +99,7 @@ pub struct AppContext {
this: Weak<Mutex<AppContext>>,
pub(crate) platform: MainThreadOnly<dyn Platform>,
text_system: Arc<TextSystem>,
+ flushing_effects: bool,
pending_updates: usize,
pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
pub(crate) executor: Executor,
@@ -119,8 +122,10 @@ impl AppContext {
pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
self.pending_updates += 1;
let result = update(self);
- if self.pending_updates == 1 {
+ if !self.flushing_effects && self.pending_updates == 1 {
+ self.flushing_effects = true;
self.flush_effects();
+ self.flushing_effects = false;
}
self.pending_updates -= 1;
result
@@ -158,6 +163,7 @@ impl AppContext {
}
}
Effect::Emit { .. } => self.pending_effects.push_back(effect),
+ Effect::FocusChanged { .. } => self.pending_effects.push_back(effect),
}
}
@@ -168,6 +174,9 @@ impl AppContext {
match effect {
Effect::Notify { emitter } => self.apply_notify_effect(emitter),
Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
+ Effect::FocusChanged { window_id, focused } => {
+ self.apply_focus_changed(window_id, focused)
+ }
}
} else {
break;
@@ -222,6 +231,24 @@ impl AppContext {
.retain(&emitter, |handler| handler(&event, self));
}
+ fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option<FocusId>) {
+ self.update_window(window_id, |cx| {
+ if cx.window.focus == focused {
+ let mut listeners = mem::take(&mut cx.window.focus_listeners);
+ let focused = focused.map(FocusHandle::new);
+ let blurred = cx.window.last_blur.unwrap().map(FocusHandle::new);
+ let event = FocusEvent { focused, blurred };
+ for listener in &listeners {
+ listener(&event, cx);
+ }
+
+ listeners.extend(cx.window.focus_listeners.drain(..));
+ cx.window.focus_listeners = listeners;
+ }
+ })
+ .ok();
+ }
+
pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
}
@@ -426,6 +453,10 @@ pub(crate) enum Effect {
emitter: EntityId,
event: Box<dyn Any + Send + Sync + 'static>,
},
+ FocusChanged {
+ window_id: WindowId,
+ focused: Option<FocusId>,
+ },
}
#[cfg(test)]
@@ -199,6 +199,16 @@ pub struct WeakHandle<T> {
entity_map: Weak<RwLock<EntityMapState>>,
}
+impl<T: 'static + Send + Sync> Clone for WeakHandle<T> {
+ fn clone(&self) -> Self {
+ Self {
+ id: self.id,
+ entity_type: self.entity_type,
+ entity_map: self.entity_map.clone(),
+ }
+ }
+}
+
impl<T: Send + Sync + 'static> WeakHandle<T> {
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
let entity_map = &self.entity_map.upgrade()?;
@@ -1,6 +1,7 @@
-use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext};
+use crate::{BorrowWindow, Bounds, ElementId, FocusHandle, LayoutId, Pixels, Point, ViewContext};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
+use std::mem;
pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
type ViewState: 'static + Send + Sync;
@@ -8,17 +9,24 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
fn id(&self) -> Option<ElementId>;
- fn layout(
+ fn initialize(
&mut self,
- state: &mut Self::ViewState,
+ view_state: &mut Self::ViewState,
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState);
+ ) -> Self::ElementState;
+
+ fn layout(
+ &mut self,
+ view_state: &mut Self::ViewState,
+ element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> LayoutId;
fn paint(
&mut self,
bounds: Bounds<Pixels>,
- state: &mut Self::ViewState,
+ view_state: &mut Self::ViewState,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<Self::ViewState>,
);
@@ -31,21 +39,54 @@ pub trait ElementIdentity: 'static + Send + Sync {
fn id(&self) -> Option<ElementId>;
}
-pub struct IdentifiedElement(pub(crate) ElementId);
-pub struct AnonymousElement;
+pub struct Identified(pub(crate) ElementId);
-impl ElementIdentity for IdentifiedElement {
+impl ElementIdentity for Identified {
fn id(&self) -> Option<ElementId> {
Some(self.0.clone())
}
}
-impl ElementIdentity for AnonymousElement {
+pub struct Anonymous;
+
+impl ElementIdentity for Anonymous {
fn id(&self) -> Option<ElementId> {
None
}
}
+pub trait ElementFocusability: 'static + Send + Sync {
+ fn focus_handle(&self) -> Option<&FocusHandle>;
+}
+
+pub struct Focusable(FocusHandle);
+
+impl AsRef<FocusHandle> for Focusable {
+ fn as_ref(&self) -> &FocusHandle {
+ &self.0
+ }
+}
+
+impl ElementFocusability for Focusable {
+ fn focus_handle(&self) -> Option<&FocusHandle> {
+ Some(&self.0)
+ }
+}
+
+impl From<FocusHandle> for Focusable {
+ fn from(value: FocusHandle) -> Self {
+ Self(value)
+ }
+}
+
+pub struct NonFocusable;
+
+impl ElementFocusability for NonFocusable {
+ fn focus_handle(&self) -> Option<&FocusHandle> {
+ None
+ }
+}
+
pub trait ParentElement: Element {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]>;
@@ -70,9 +111,10 @@ pub trait ParentElement: Element {
}
}
-trait ElementObject<S>: 'static + Send + Sync {
- fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> LayoutId;
- fn paint(&mut self, state: &mut S, offset: Option<Point<Pixels>>, cx: &mut ViewContext<S>);
+trait ElementObject<V>: 'static + Send + Sync {
+ fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
+ fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
+ fn paint(&mut self, view_state: &mut V, offset: Option<Point<Pixels>>, cx: &mut ViewContext<V>);
}
struct RenderedElement<E: Element> {
@@ -81,17 +123,17 @@ struct RenderedElement<E: Element> {
}
#[derive(Default)]
-enum ElementRenderPhase<S> {
+enum ElementRenderPhase<V> {
#[default]
- Rendered,
+ Start,
+ Initialized {
+ frame_state: Option<V>,
+ },
LayoutRequested {
layout_id: LayoutId,
- frame_state: Option<S>,
- },
- Painted {
- bounds: Bounds<Pixels>,
- frame_state: Option<S>,
+ frame_state: Option<V>,
},
+ Painted,
}
/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
@@ -101,52 +143,57 @@ impl<E: Element> RenderedElement<E> {
fn new(element: E) -> Self {
RenderedElement {
element,
- phase: ElementRenderPhase::Rendered,
+ phase: ElementRenderPhase::Start,
}
}
+}
- fn paint_with_element_state(
- &mut self,
- bounds: Bounds<Pixels>,
- view_state: &mut E::ViewState,
- frame_state: &mut Option<E::ElementState>,
- cx: &mut ViewContext<E::ViewState>,
- ) {
- if let Some(id) = self.element.id() {
+impl<E> ElementObject<E::ViewState> for RenderedElement<E>
+where
+ E: Element,
+{
+ fn initialize(&mut self, view_state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) {
+ let frame_state = if let Some(id) = self.element.id() {
cx.with_element_state(id, |element_state, cx| {
- let mut element_state = element_state.unwrap();
- self.element
- .paint(bounds, view_state, &mut element_state, cx);
+ let element_state = self.element.initialize(view_state, element_state, cx);
((), element_state)
});
+ None
} else {
- self.element
- .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
- }
+ let frame_state = self.element.initialize(view_state, None, cx);
+ Some(frame_state)
+ };
+
+ self.phase = ElementRenderPhase::Initialized { frame_state };
}
-}
-impl<E, S> ElementObject<E::ViewState> for RenderedElement<E>
-where
- E: Element<ElementState = S>,
- S: 'static + Send + Sync,
-{
fn layout(&mut self, state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) -> LayoutId {
- let (layout_id, frame_state) = if let Some(id) = self.element.id() {
- let layout_id = cx.with_element_state(id, |element_state, cx| {
- self.element.layout(state, element_state, cx)
- });
- (layout_id, None)
- } else {
- let (layout_id, frame_state) = self.element.layout(state, None, cx);
- (layout_id, Some(frame_state))
+ let layout_id;
+ let mut frame_state;
+ match mem::take(&mut self.phase) {
+ ElementRenderPhase::Initialized {
+ frame_state: initial_frame_state,
+ } => {
+ frame_state = initial_frame_state;
+ if let Some(id) = self.element.id() {
+ layout_id = cx.with_element_state(id, |element_state, cx| {
+ let mut element_state = element_state.unwrap();
+ let layout_id = self.element.layout(state, &mut element_state, cx);
+ (layout_id, element_state)
+ });
+ } else {
+ layout_id = self
+ .element
+ .layout(state, frame_state.as_mut().unwrap(), cx);
+ }
+ }
+ _ => panic!("must call initialize before layout"),
};
self.phase = ElementRenderPhase::LayoutRequested {
layout_id,
frame_state,
};
-
layout_id
}
@@ -156,60 +203,63 @@ where
offset: Option<Point<Pixels>>,
cx: &mut ViewContext<E::ViewState>,
) {
- self.phase = match std::mem::take(&mut self.phase) {
- ElementRenderPhase::Rendered => panic!("must call layout before paint"),
-
+ self.phase = match mem::take(&mut self.phase) {
ElementRenderPhase::LayoutRequested {
layout_id,
mut frame_state,
} => {
let mut bounds = cx.layout_bounds(layout_id);
offset.map(|offset| bounds.origin += offset);
- self.paint_with_element_state(bounds, view_state, &mut frame_state, cx);
- ElementRenderPhase::Painted {
- bounds,
- frame_state,
+ if let Some(id) = self.element.id() {
+ cx.with_element_state(id, |element_state, cx| {
+ let mut element_state = element_state.unwrap();
+ self.element
+ .paint(bounds, view_state, &mut element_state, cx);
+ ((), element_state)
+ });
+ } else {
+ self.element
+ .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
}
+ ElementRenderPhase::Painted
}
- ElementRenderPhase::Painted {
- bounds,
- mut frame_state,
- } => {
- self.paint_with_element_state(bounds, view_state, &mut frame_state, cx);
- ElementRenderPhase::Painted {
- bounds,
- frame_state,
- }
- }
+ _ => panic!("must call layout before paint"),
};
}
}
-pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
+pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
-impl<S: 'static + Send + Sync> AnyElement<S> {
- pub fn new<E: Element<ViewState = S>>(element: E) -> Self {
+impl<V: 'static + Send + Sync> AnyElement<V> {
+ pub fn new<E: Element<ViewState = V>>(element: E) -> Self {
AnyElement(Box::new(RenderedElement::new(element)))
}
-}
-impl<S: 'static + Send + Sync> AnyElement<S> {
- pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> LayoutId {
- self.0.layout(state, cx)
+ pub fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
+ self.0.initialize(view_state, cx);
+ }
+
+ pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
+ self.0.layout(view_state, cx)
}
- pub fn paint(&mut self, state: &mut S, offset: Option<Point<Pixels>>, cx: &mut ViewContext<S>) {
- self.0.paint(state, offset, cx)
+ pub fn paint(
+ &mut self,
+ view_state: &mut V,
+ offset: Option<Point<Pixels>>,
+ cx: &mut ViewContext<V>,
+ ) {
+ self.0.paint(view_state, offset, cx)
}
}
-pub trait IntoAnyElement<S> {
- fn into_any(self) -> AnyElement<S>;
+pub trait IntoAnyElement<V> {
+ fn into_any(self) -> AnyElement<V>;
}
-impl<S> IntoAnyElement<S> for AnyElement<S> {
- fn into_any(self) -> AnyElement<S> {
+impl<V> IntoAnyElement<V> for AnyElement<V> {
+ fn into_any(self) -> AnyElement<V> {
self
}
}
@@ -1,15 +1,15 @@
use crate::{
- Active, AnonymousElement, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase,
- Element, ElementId, ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement,
- LayoutId, MouseClickEvent, MouseDownEvent, MouseEventListeners, MouseMoveEvent, MouseUpEvent,
- Overflow, ParentElement, Pixels, Point, ScrollWheelEvent, SharedString, Style, StyleRefinement,
- Styled, ViewContext,
+ Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
+ ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusHandle, Focusable,
+ Hover, Identified, Interactive, IntoAnyElement, LayoutId, MouseClickEvent, MouseDownEvent,
+ MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point,
+ ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, ViewContext,
};
use collections::HashMap;
use parking_lot::Mutex;
use refineable::Refineable;
use smallvec::SmallVec;
-use std::sync::Arc;
+use std::{mem, sync::Arc};
#[derive(Default)]
pub struct DivState {
@@ -60,12 +60,13 @@ impl ScrollState {
}
}
-pub fn div<S>() -> Div<S, AnonymousElement>
+pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
where
- S: 'static + Send + Sync,
+ V: 'static + Send + Sync,
{
Div {
- kind: AnonymousElement,
+ identity: Anonymous,
+ focusability: NonFocusable,
children: SmallVec::new(),
group: None,
base_style: StyleRefinement::default(),
@@ -73,12 +74,20 @@ where
group_hover: None,
active_style: StyleRefinement::default(),
group_active: None,
- listeners: MouseEventListeners::default(),
+ focus_style: StyleRefinement::default(),
+ focus_in_style: StyleRefinement::default(),
+ in_focus_style: StyleRefinement::default(),
+ listeners: EventListeners::default(),
}
}
-pub struct Div<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
- kind: K,
+pub struct Div<
+ V: 'static + Send + Sync,
+ I: ElementIdentity = Anonymous,
+ F: ElementFocusability = NonFocusable,
+> {
+ identity: I,
+ focusability: F,
children: SmallVec<[AnyElement<V>; 2]>,
group: Option<SharedString>,
base_style: StyleRefinement,
@@ -86,7 +95,10 @@ pub struct Div<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement>
group_hover: Option<GroupStyle>,
active_style: StyleRefinement,
group_active: Option<GroupStyle>,
- listeners: MouseEventListeners<V>,
+ focus_style: StyleRefinement,
+ focus_in_style: StyleRefinement,
+ in_focus_style: StyleRefinement,
+ listeners: EventListeners<V>,
}
struct GroupStyle {
@@ -94,13 +106,15 @@ struct GroupStyle {
style: StyleRefinement,
}
-impl<V> Div<V, AnonymousElement>
+impl<V, F> Div<V, Anonymous, F>
where
+ F: ElementFocusability,
V: 'static + Send + Sync,
{
- pub fn id(self, id: impl Into<ElementId>) -> Div<V, IdentifiedElement> {
+ pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified, F> {
Div {
- kind: IdentifiedElement(id.into()),
+ identity: Identified(id.into()),
+ focusability: self.focusability,
children: self.children,
group: self.group,
base_style: self.base_style,
@@ -108,15 +122,19 @@ where
group_hover: self.group_hover,
active_style: self.active_style,
group_active: self.group_active,
+ focus_style: self.focus_style,
+ focus_in_style: self.focus_in_style,
+ in_focus_style: self.in_focus_style,
listeners: self.listeners,
}
}
}
-impl<V, K> Div<V, K>
+impl<V, I, F> Div<V, I, F>
where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
pub fn group(mut self, group: impl Into<SharedString>) -> Self {
self.group = Some(group.into());
@@ -187,6 +205,20 @@ where
let mut computed_style = Style::default();
computed_style.refine(&self.base_style);
+ if let Some(handle) = self.focusability.focus_handle() {
+ if handle.contains_focused(cx) {
+ computed_style.refine(&self.focus_in_style);
+ }
+
+ if handle.within_focused(cx) {
+ computed_style.refine(&self.in_focus_style);
+ }
+
+ if handle.is_focused(cx) {
+ computed_style.refine(&self.focus_style);
+ }
+ }
+
let mouse_position = cx.mouse_position();
if let Some(group_hover) = self.group_hover.as_ref() {
@@ -258,12 +290,12 @@ where
}
fn paint_event_listeners(
- &self,
+ &mut self,
bounds: Bounds<Pixels>,
pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
cx: &mut ViewContext<V>,
) {
- let click_listeners = self.listeners.mouse_click.clone();
+ let click_listeners = mem::take(&mut self.listeners.mouse_click);
let mouse_down = pending_click.lock().clone();
if let Some(mouse_down) = mouse_down {
cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
@@ -287,25 +319,25 @@ where
});
}
- for listener in self.listeners.mouse_down.iter().cloned() {
+ for listener in mem::take(&mut self.listeners.mouse_down) {
cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
- for listener in self.listeners.mouse_up.iter().cloned() {
+ for listener in mem::take(&mut self.listeners.mouse_up) {
cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
- for listener in self.listeners.mouse_move.iter().cloned() {
+ for listener in mem::take(&mut self.listeners.mouse_move) {
cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
- for listener in self.listeners.scroll_wheel.iter().cloned() {
+ for listener in mem::take(&mut self.listeners.scroll_wheel) {
cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
@@ -313,26 +345,92 @@ where
}
}
-impl<V, K> Element for Div<V, K>
+impl<V, I> Div<V, I, NonFocusable>
+where
+ I: ElementIdentity,
+ V: 'static + Send + Sync,
+{
+ pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable> {
+ Div {
+ identity: self.identity,
+ focusability: handle.clone().into(),
+ children: self.children,
+ group: self.group,
+ base_style: self.base_style,
+ hover_style: self.hover_style,
+ group_hover: self.group_hover,
+ active_style: self.active_style,
+ group_active: self.group_active,
+ focus_style: self.focus_style,
+ focus_in_style: self.focus_in_style,
+ in_focus_style: self.in_focus_style,
+ listeners: self.listeners,
+ }
+ }
+}
+
+impl<V, I> Focus for Div<V, I, Focusable>
where
+ I: ElementIdentity,
+ V: 'static + Send + Sync,
+{
+ fn handle(&self) -> &FocusHandle {
+ self.focusability.as_ref()
+ }
+
+ fn set_focus_style(&mut self, style: StyleRefinement) {
+ self.focus_style = style;
+ }
+
+ fn set_focus_in_style(&mut self, style: StyleRefinement) {
+ self.focus_in_style = style;
+ }
+
+ fn set_in_focus_style(&mut self, style: StyleRefinement) {
+ self.in_focus_style = style;
+ }
+}
+
+impl<V, I, F> Element for Div<V, I, F>
+where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
type ViewState = V;
type ElementState = DivState;
fn id(&self) -> Option<ElementId> {
- self.kind.id()
+ self.identity.id()
}
- fn layout(
+ fn initialize(
&mut self,
view_state: &mut Self::ViewState,
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState) {
- let element_state = element_state.unwrap_or_default();
- let style = self.compute_style(Bounds::default(), &element_state, cx);
+ ) -> Self::ElementState {
+ cx.with_focus(
+ self.focusability.focus_handle().cloned(),
+ mem::take(&mut self.listeners.key_down),
+ mem::take(&mut self.listeners.key_up),
+ mem::take(&mut self.listeners.focus),
+ |cx| {
+ for child in &mut self.children {
+ child.initialize(view_state, cx);
+ }
+ element_state.unwrap_or_default()
+ },
+ )
+ }
+
+ fn layout(
+ &mut self,
+ view_state: &mut Self::ViewState,
+ element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> LayoutId {
+ let style = self.compute_style(Bounds::default(), element_state, cx);
style.apply_text_style(cx, |cx| {
self.with_element_id(cx, |this, cx| {
let layout_ids = this
@@ -340,9 +438,7 @@ where
.iter_mut()
.map(|child| child.layout(view_state, cx))
.collect::<Vec<_>>();
-
- let layout_id = cx.request_layout(&style, layout_ids);
- (layout_id, element_state)
+ cx.request_layout(&style, layout_ids)
})
})
}
@@ -410,50 +506,55 @@ where
}
}
-impl<V, K> IntoAnyElement<V> for Div<V, K>
+impl<V, I, F> IntoAnyElement<V> for Div<V, I, F>
where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
fn into_any(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
-impl<V, K> ParentElement for Div<V, K>
+impl<V, I, F> ParentElement for Div<V, I, F>
where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
&mut self.children
}
}
-impl<V, K> Styled for Div<V, K>
+impl<V, I, F> Styled for Div<V, I, F>
where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
fn style(&mut self) -> &mut StyleRefinement {
&mut self.base_style
}
}
-impl<V, K> Interactive for Div<V, K>
+impl<V, I, F> Interactive for Div<V, I, F>
where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
- fn listeners(&mut self) -> &mut MouseEventListeners<V> {
+ fn listeners(&mut self) -> &mut EventListeners<V> {
&mut self.listeners
}
}
-impl<V, K> Hover for Div<V, K>
+impl<V, I, F> Hover for Div<V, I, F>
where
+ I: ElementIdentity,
+ F: ElementFocusability,
V: 'static + Send + Sync,
- K: ElementIdentity,
{
fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
if let Some(group) = group {
@@ -464,10 +565,16 @@ where
}
}
-impl<V> Click for Div<V, IdentifiedElement> where V: 'static + Send + Sync {}
+impl<V, F> Click for Div<V, Identified, F>
+where
+ F: ElementFocusability,
+ V: 'static + Send + Sync,
+{
+}
-impl<V> Active for Div<V, IdentifiedElement>
+impl<V, F> Active for Div<V, Identified, F>
where
+ F: ElementFocusability,
V: 'static + Send + Sync,
{
fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
@@ -1,18 +1,23 @@
use crate::{
- div, Active, AnonymousElement, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
- ElementId, ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement, LayoutId,
- MouseEventListeners, Pixels, SharedString, StyleRefinement, Styled, ViewContext,
+ div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
+ ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover,
+ Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
+ StyleRefinement, Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
-pub struct Img<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
- base: Div<V, K>,
+pub struct Img<
+ V: 'static + Send + Sync,
+ I: ElementIdentity = Anonymous,
+ F: ElementFocusability = NonFocusable,
+> {
+ base: Div<V, I, F>,
uri: Option<SharedString>,
grayscale: bool,
}
-pub fn img<V>() -> Img<V, AnonymousElement>
+pub fn img<V>() -> Img<V, Anonymous, NonFocusable>
where
V: 'static + Send + Sync,
{
@@ -23,10 +28,11 @@ where
}
}
-impl<V, K> Img<V, K>
+impl<V, I, F> Img<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
self.uri = Some(uri.into());
@@ -39,8 +45,12 @@ where
}
}
-impl<V: 'static + Send + Sync> Img<V, AnonymousElement> {
- pub fn id(self, id: impl Into<ElementId>) -> Img<V, IdentifiedElement> {
+impl<V, F> Img<V, Anonymous, F>
+where
+ V: 'static + Send + Sync,
+ F: ElementFocusability,
+{
+ pub fn id(self, id: impl Into<ElementId>) -> Img<V, Identified, F> {
Img {
base: self.base.id(id),
uri: self.uri,
@@ -49,20 +59,22 @@ impl<V: 'static + Send + Sync> Img<V, AnonymousElement> {
}
}
-impl<V, K> IntoAnyElement<V> for Img<V, K>
+impl<V, I, F> IntoAnyElement<V> for Img<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
fn into_any(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
-impl<V, K> Element for Img<V, K>
+impl<V, I, F> Element for Img<V, I, F>
where
V: Send + Sync + 'static,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
type ViewState = V;
type ElementState = DivState;
@@ -71,24 +83,30 @@ where
self.base.id()
}
- fn layout(
+ fn initialize(
&mut self,
- view_state: &mut Self::ViewState,
+ view_state: &mut V,
element_state: Option<Self::ElementState>,
+ cx: &mut ViewContext<V>,
+ ) -> Self::ElementState {
+ self.base.initialize(view_state, element_state, cx)
+ }
+
+ fn layout(
+ &mut self,
+ view_state: &mut V,
+ element_state: &mut Self::ElementState,
cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState)
- where
- Self: Sized,
- {
+ ) -> LayoutId {
self.base.layout(view_state, element_state, cx)
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
- view: &mut Self::ViewState,
+ view: &mut V,
element_state: &mut Self::ElementState,
- cx: &mut ViewContext<Self::ViewState>,
+ cx: &mut ViewContext<V>,
) {
cx.stack(0, |cx| {
self.base.paint(bounds, view, element_state, cx);
@@ -121,43 +139,74 @@ where
}
}
-impl<V, K> Styled for Img<V, K>
+impl<V, I, F> Styled for Img<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
}
}
-impl<V, K> Interactive for Img<V, K>
+impl<V, I, F> Interactive for Img<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
- fn listeners(&mut self) -> &mut MouseEventListeners<V> {
+ fn listeners(&mut self) -> &mut EventListeners<V> {
self.base.listeners()
}
}
-impl<V, K> Hover for Img<V, K>
+impl<V, I, F> Hover for Img<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
self.base.set_hover_style(group, style);
}
}
-impl<V> Click for Img<V, IdentifiedElement> where V: 'static + Send + Sync {}
+impl<V, F> Click for Img<V, Identified, F>
+where
+ V: 'static + Send + Sync,
+ F: ElementFocusability,
+{
+}
-impl<V> Active for Img<V, IdentifiedElement>
+impl<V, F> Active for Img<V, Identified, F>
where
V: 'static + Send + Sync,
+ F: ElementFocusability,
{
fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
self.base.set_active_style(group, style)
}
}
+
+impl<V, I> Focus for Img<V, I, Focusable>
+where
+ V: 'static + Send + Sync,
+ I: ElementIdentity,
+{
+ fn set_focus_style(&mut self, style: StyleRefinement) {
+ self.base.set_focus_style(style)
+ }
+
+ fn set_focus_in_style(&mut self, style: StyleRefinement) {
+ self.base.set_focus_in_style(style)
+ }
+
+ fn set_in_focus_style(&mut self, style: StyleRefinement) {
+ self.base.set_in_focus_style(style)
+ }
+
+ fn handle(&self) -> &crate::FocusHandle {
+ self.base.handle()
+ }
+}
@@ -1,16 +1,21 @@
use crate::{
- div, Active, AnonymousElement, AnyElement, Bounds, Click, Div, DivState, Element, ElementId,
- ElementIdentity, Hover, IdentifiedElement, Interactive, IntoAnyElement, LayoutId,
- MouseEventListeners, Pixels, SharedString, StyleRefinement, Styled,
+ div, Active, Anonymous, AnyElement, Bounds, Click, Div, DivState, Element, ElementFocusability,
+ ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover, Identified, Interactive,
+ IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, StyleRefinement, Styled,
+ ViewContext,
};
use util::ResultExt;
-pub struct Svg<V: 'static + Send + Sync, K: ElementIdentity = AnonymousElement> {
- base: Div<V, K>,
+pub struct Svg<
+ V: 'static + Send + Sync,
+ I: ElementIdentity = Anonymous,
+ F: ElementFocusability = NonFocusable,
+> {
+ base: Div<V, I, F>,
path: Option<SharedString>,
}
-pub fn svg<V>() -> Svg<V, AnonymousElement>
+pub fn svg<V>() -> Svg<V, Anonymous, NonFocusable>
where
V: 'static + Send + Sync,
{
@@ -20,10 +25,11 @@ where
}
}
-impl<V, K> Svg<V, K>
+impl<V, I, F> Svg<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
self.path = Some(path.into());
@@ -31,8 +37,12 @@ where
}
}
-impl<V: 'static + Send + Sync> Svg<V, AnonymousElement> {
- pub fn id(self, id: impl Into<ElementId>) -> Svg<V, IdentifiedElement> {
+impl<V, F> Svg<V, Anonymous, F>
+where
+ V: 'static + Send + Sync,
+ F: ElementFocusability,
+{
+ pub fn id(self, id: impl Into<ElementId>) -> Svg<V, Identified, F> {
Svg {
base: self.base.id(id),
path: self.path,
@@ -40,20 +50,22 @@ impl<V: 'static + Send + Sync> Svg<V, AnonymousElement> {
}
}
-impl<V, K> IntoAnyElement<V> for Svg<V, K>
+impl<V, I, F> IntoAnyElement<V> for Svg<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
fn into_any(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
-impl<V, K> Element for Svg<V, K>
+impl<V, I, F> Element for Svg<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
type ViewState = V;
type ElementState = DivState;
@@ -62,16 +74,22 @@ where
self.base.id()
}
- fn layout(
+ fn initialize(
&mut self,
- view: &mut V,
+ view_state: &mut V,
element_state: Option<Self::ElementState>,
- cx: &mut crate::ViewContext<V>,
- ) -> (LayoutId, Self::ElementState)
- where
- Self: Sized,
- {
- self.base.layout(view, element_state, cx)
+ cx: &mut ViewContext<V>,
+ ) -> Self::ElementState {
+ self.base.initialize(view_state, element_state, cx)
+ }
+
+ fn layout(
+ &mut self,
+ view_state: &mut V,
+ element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> LayoutId {
+ self.base.layout(view_state, element_state, cx)
}
fn paint(
@@ -79,7 +97,7 @@ where
bounds: Bounds<Pixels>,
view: &mut Self::ViewState,
element_state: &mut Self::ElementState,
- cx: &mut crate::ViewContext<V>,
+ cx: &mut ViewContext<V>,
) where
Self: Sized,
{
@@ -95,43 +113,74 @@ where
}
}
-impl<V, K> Styled for Svg<V, K>
+impl<V, I, F> Styled for Svg<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
}
}
-impl<V, K> Interactive for Svg<V, K>
+impl<V, I, F> Interactive for Svg<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
- fn listeners(&mut self) -> &mut MouseEventListeners<V> {
+ fn listeners(&mut self) -> &mut EventListeners<V> {
self.base.listeners()
}
}
-impl<V, K> Hover for Svg<V, K>
+impl<V, I, F> Hover for Svg<V, I, F>
where
V: 'static + Send + Sync,
- K: ElementIdentity,
+ I: ElementIdentity,
+ F: ElementFocusability,
{
fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
self.base.set_hover_style(group, style);
}
}
-impl<V> Click for Svg<V, IdentifiedElement> where V: 'static + Send + Sync {}
+impl<V, F> Click for Svg<V, Identified, F>
+where
+ V: 'static + Send + Sync,
+ F: ElementFocusability,
+{
+}
-impl<V> Active for Svg<V, IdentifiedElement>
+impl<V, F> Active for Svg<V, Identified, F>
where
V: 'static + Send + Sync,
+ F: ElementFocusability,
{
fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
self.base.set_active_style(group, style)
}
}
+
+impl<V, I> Focus for Svg<V, I, Focusable>
+where
+ V: 'static + Send + Sync,
+ I: ElementIdentity,
+{
+ fn set_focus_style(&mut self, style: StyleRefinement) {
+ self.base.set_focus_style(style)
+ }
+
+ fn set_focus_in_style(&mut self, style: StyleRefinement) {
+ self.base.set_focus_in_style(style)
+ }
+
+ fn set_in_focus_style(&mut self, style: StyleRefinement) {
+ self.base.set_in_focus_style(style)
+ }
+
+ fn handle(&self) -> &crate::FocusHandle {
+ self.base.handle()
+ }
+}
@@ -39,31 +39,40 @@ impl<S: 'static + Send + Sync> IntoAnyElement<S> for String {
}
}
-pub struct Text<S> {
+pub struct Text<V> {
text: SharedString,
- state_type: PhantomData<S>,
+ state_type: PhantomData<V>,
}
-impl<S: 'static + Send + Sync> IntoAnyElement<S> for Text<S> {
- fn into_any(self) -> AnyElement<S> {
+impl<V: 'static + Send + Sync> IntoAnyElement<V> for Text<V> {
+ fn into_any(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
-impl<S: 'static + Send + Sync> Element for Text<S> {
- type ViewState = S;
+impl<V: 'static + Send + Sync> Element for Text<V> {
+ type ViewState = V;
type ElementState = Arc<Mutex<Option<TextElementState>>>;
fn id(&self) -> Option<crate::ElementId> {
None
}
+ fn initialize(
+ &mut self,
+ _view_state: &mut V,
+ element_state: Option<Self::ElementState>,
+ _cx: &mut ViewContext<V>,
+ ) -> Self::ElementState {
+ element_state.unwrap_or_default()
+ }
+
fn layout(
&mut self,
- _view: &mut S,
- _element_state: Option<Self::ElementState>,
- cx: &mut ViewContext<S>,
- ) -> (LayoutId, Self::ElementState) {
+ _view: &mut V,
+ element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<V>,
+ ) -> LayoutId {
let text_system = cx.text_system().clone();
let text_style = cx.text_style();
let font_size = text_style.font_size * cx.rem_size();
@@ -71,7 +80,6 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
.line_height
.to_pixels(font_size.into(), cx.rem_size());
let text = self.text.clone();
- let element_state = Arc::new(Mutex::new(None));
let rem_size = cx.rem_size();
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
@@ -102,15 +110,15 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
}
});
- (layout_id, element_state)
+ layout_id
}
- fn paint<'a>(
+ fn paint(
&mut self,
bounds: Bounds<Pixels>,
- _: &mut Self::ViewState,
+ _: &mut V,
element_state: &mut Self::ElementState,
- cx: &mut ViewContext<S>,
+ cx: &mut ViewContext<V>,
) {
let element_state = element_state.lock();
let element_state = element_state
@@ -1,4 +1,7 @@
-use crate::{point, Keystroke, Modifiers, Pixels, Point};
+use crate::{
+ point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext,
+};
+use smallvec::SmallVec;
use std::{any::Any, ops::Deref};
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -26,7 +29,7 @@ impl Deref for ModifiersChangedEvent {
}
/// The phase of a touch motion event.
-/// Based on the winit enum of the same name,
+/// Based on the winit enum of the same name.
#[derive(Clone, Copy, Debug)]
pub enum TouchPhase {
Started,
@@ -50,6 +53,12 @@ pub struct MouseUpEvent {
pub click_count: usize,
}
+#[derive(Clone, Debug, Default)]
+pub struct MouseClickEvent {
+ pub down: MouseDownEvent,
+ pub up: MouseUpEvent,
+}
+
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
pub enum MouseButton {
Left,
@@ -155,7 +164,7 @@ impl Deref for MouseExitEvent {
}
#[derive(Clone, Debug)]
-pub enum Event {
+pub enum InputEvent {
KeyDown(KeyDownEvent),
KeyUp(KeyUpEvent),
ModifiersChanged(ModifiersChangedEvent),
@@ -166,30 +175,112 @@ pub enum Event {
ScrollWheel(ScrollWheelEvent),
}
-impl Event {
+impl InputEvent {
pub fn position(&self) -> Option<Point<Pixels>> {
match self {
- Event::KeyDown { .. } => None,
- Event::KeyUp { .. } => None,
- Event::ModifiersChanged { .. } => None,
- Event::MouseDown(event) => Some(event.position),
- Event::MouseUp(event) => Some(event.position),
- Event::MouseMoved(event) => Some(event.position),
- Event::MouseExited(event) => Some(event.position),
- Event::ScrollWheel(event) => Some(event.position),
+ InputEvent::KeyDown { .. } => None,
+ InputEvent::KeyUp { .. } => None,
+ InputEvent::ModifiersChanged { .. } => None,
+ InputEvent::MouseDown(event) => Some(event.position),
+ InputEvent::MouseUp(event) => Some(event.position),
+ InputEvent::MouseMoved(event) => Some(event.position),
+ InputEvent::MouseExited(event) => Some(event.position),
+ InputEvent::ScrollWheel(event) => Some(event.position),
}
}
pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
match self {
- Event::KeyDown { .. } => None,
- Event::KeyUp { .. } => None,
- Event::ModifiersChanged { .. } => None,
- Event::MouseDown(event) => Some(event),
- Event::MouseUp(event) => Some(event),
- Event::MouseMoved(event) => Some(event),
- Event::MouseExited(event) => Some(event),
- Event::ScrollWheel(event) => Some(event),
+ InputEvent::KeyDown { .. } => None,
+ InputEvent::KeyUp { .. } => None,
+ InputEvent::ModifiersChanged { .. } => None,
+ InputEvent::MouseDown(event) => Some(event),
+ InputEvent::MouseUp(event) => Some(event),
+ InputEvent::MouseMoved(event) => Some(event),
+ InputEvent::MouseExited(event) => Some(event),
+ InputEvent::ScrollWheel(event) => Some(event),
+ }
+ }
+
+ pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
+ match self {
+ InputEvent::KeyDown(event) => Some(event),
+ InputEvent::KeyUp(event) => Some(event),
+ InputEvent::ModifiersChanged(event) => Some(event),
+ InputEvent::MouseDown(_) => None,
+ InputEvent::MouseUp(_) => None,
+ InputEvent::MouseMoved(_) => None,
+ InputEvent::MouseExited(_) => None,
+ InputEvent::ScrollWheel(_) => None,
+ }
+ }
+}
+
+pub struct FocusEvent {
+ pub blurred: Option<FocusHandle>,
+ pub focused: Option<FocusHandle>,
+}
+
+pub type MouseDownListener<V> = Box<
+ dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ + Send
+ + Sync
+ + 'static,
+>;
+pub type MouseUpListener<V> = Box<
+ dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ + Send
+ + Sync
+ + 'static,
+>;
+pub type MouseClickListener<V> =
+ Box<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type MouseMoveListener<V> = Box<
+ dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ + Send
+ + Sync
+ + 'static,
+>;
+
+pub type ScrollWheelListener<V> = Box<
+ dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ + Send
+ + Sync
+ + 'static,
+>;
+
+pub type KeyDownListener<V> =
+ Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type KeyUpListener<V> =
+ Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type FocusListener<V> =
+ Box<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub struct EventListeners<V: 'static> {
+ pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
+ pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
+ pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
+ pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
+ pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
+ pub key_down: SmallVec<[KeyDownListener<V>; 2]>,
+ pub key_up: SmallVec<[KeyUpListener<V>; 2]>,
+ pub focus: SmallVec<[FocusListener<V>; 2]>,
+}
+
+impl<V> Default for EventListeners<V> {
+ fn default() -> Self {
+ Self {
+ mouse_down: SmallVec::new(),
+ mouse_up: SmallVec::new(),
+ mouse_click: SmallVec::new(),
+ mouse_move: SmallVec::new(),
+ scroll_wheel: SmallVec::new(),
+ key_down: SmallVec::new(),
+ key_up: SmallVec::new(),
+ focus: SmallVec::new(),
}
}
}
@@ -0,0 +1,159 @@
+use crate::{
+ DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, StyleRefinement,
+ ViewContext,
+};
+
+pub trait Focus: Interactive {
+ fn set_focus_style(&mut self, style: StyleRefinement);
+ fn set_focus_in_style(&mut self, style: StyleRefinement);
+ fn set_in_focus_style(&mut self, style: StyleRefinement);
+ fn handle(&self) -> &FocusHandle;
+
+ fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
+ where
+ Self: Sized,
+ {
+ self.set_focus_style(f(StyleRefinement::default()));
+ self
+ }
+
+ fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
+ where
+ Self: Sized,
+ {
+ self.set_focus_in_style(f(StyleRefinement::default()));
+ self
+ }
+
+ fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
+ where
+ Self: Sized,
+ {
+ self.set_in_focus_style(f(StyleRefinement::default()));
+ self
+ }
+
+ fn on_focus(
+ mut self,
+ listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+ + Send
+ + Sync
+ + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ let handle = self.handle().clone();
+ self.listeners()
+ .focus
+ .push(Box::new(move |view, event, cx| {
+ if event.focused.as_ref() == Some(&handle) {
+ listener(view, event, cx)
+ }
+ }));
+ self
+ }
+
+ fn on_blur(
+ mut self,
+ listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+ + Send
+ + Sync
+ + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ let handle = self.handle().clone();
+ self.listeners()
+ .focus
+ .push(Box::new(move |view, event, cx| {
+ if event.blurred.as_ref() == Some(&handle) {
+ listener(view, event, cx)
+ }
+ }));
+ self
+ }
+
+ fn on_focus_in(
+ mut self,
+ listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+ + Send
+ + Sync
+ + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ let handle = self.handle().clone();
+ self.listeners()
+ .focus
+ .push(Box::new(move |view, event, cx| {
+ if event
+ .focused
+ .as_ref()
+ .map_or(false, |focused| focused.contains(&handle, cx))
+ {
+ listener(view, event, cx)
+ }
+ }));
+ self
+ }
+
+ fn on_focus_out(
+ mut self,
+ listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
+ + Send
+ + Sync
+ + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ let handle = self.handle().clone();
+ self.listeners()
+ .focus
+ .push(Box::new(move |view, event, cx| {
+ if event
+ .blurred
+ .as_ref()
+ .map_or(false, |blurred| handle.contains(&blurred, cx))
+ {
+ listener(view, event, cx)
+ }
+ }));
+ self
+ }
+
+ fn on_key_down(
+ mut self,
+ listener: impl Fn(
+ &mut Self::ViewState,
+ &KeyDownEvent,
+ DispatchPhase,
+ &mut ViewContext<Self::ViewState>,
+ ) + Send
+ + Sync
+ + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.listeners().key_down.push(Box::new(listener));
+ self
+ }
+
+ fn on_key_up(
+ mut self,
+ listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
+ + Send
+ + Sync
+ + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.listeners().key_up.push(Box::new(listener));
+ self
+ }
+}
@@ -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::*;
@@ -1,13 +1,10 @@
-use smallvec::SmallVec;
-
use crate::{
- Bounds, DispatchPhase, Element, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
- Pixels, ScrollWheelEvent, ViewContext,
+ DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent,
+ MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext,
};
-use std::sync::Arc;
pub trait Interactive: Element {
- fn listeners(&mut self) -> &mut MouseEventListeners<Self::ViewState>;
+ fn listeners(&mut self) -> &mut EventListeners<Self::ViewState>;
fn on_mouse_down(
mut self,
@@ -22,7 +19,7 @@ pub trait Interactive: Element {
{
self.listeners()
.mouse_down
- .push(Arc::new(move |view, event, bounds, phase, cx| {
+ .push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == button
&& bounds.contains_point(&event.position)
@@ -46,7 +43,7 @@ pub trait Interactive: Element {
{
self.listeners()
.mouse_up
- .push(Arc::new(move |view, event, bounds, phase, cx| {
+ .push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == button
&& bounds.contains_point(&event.position)
@@ -70,7 +67,7 @@ pub trait Interactive: Element {
{
self.listeners()
.mouse_down
- .push(Arc::new(move |view, event, bounds, phase, cx| {
+ .push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
&& event.button == button
&& !bounds.contains_point(&event.position)
@@ -94,7 +91,7 @@ pub trait Interactive: Element {
{
self.listeners()
.mouse_up
- .push(Arc::new(move |view, event, bounds, phase, cx| {
+ .push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
&& event.button == button
&& !bounds.contains_point(&event.position)
@@ -117,7 +114,7 @@ pub trait Interactive: Element {
{
self.listeners()
.mouse_move
- .push(Arc::new(move |view, event, bounds, phase, cx| {
+ .push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
handler(view, event, cx);
}
@@ -137,7 +134,7 @@ pub trait Interactive: Element {
{
self.listeners()
.scroll_wheel
- .push(Arc::new(move |view, event, bounds, phase, cx| {
+ .push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
handler(view, event, cx);
}
@@ -159,60 +156,7 @@ pub trait Click: Interactive {
{
self.listeners()
.mouse_click
- .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
+ .push(Box::new(move |view, event, cx| handler(view, event, cx)));
self
}
}
-
-type MouseDownHandler<V> = Arc<
- dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
- + Send
- + Sync
- + 'static,
->;
-type MouseUpHandler<V> = Arc<
- dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
- + Send
- + Sync
- + 'static,
->;
-type MouseClickHandler<V> =
- Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
-
-type MouseMoveHandler<V> = Arc<
- dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
- + Send
- + Sync
- + 'static,
->;
-type ScrollWheelHandler<V> = Arc<
- dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
- + Send
- + Sync
- + 'static,
->;
-
-pub struct MouseEventListeners<V: 'static> {
- pub mouse_down: SmallVec<[MouseDownHandler<V>; 2]>,
- pub mouse_up: SmallVec<[MouseUpHandler<V>; 2]>,
- pub mouse_click: SmallVec<[MouseClickHandler<V>; 2]>,
- pub mouse_move: SmallVec<[MouseMoveHandler<V>; 2]>,
- pub scroll_wheel: SmallVec<[ScrollWheelHandler<V>; 2]>,
-}
-
-impl<V> Default for MouseEventListeners<V> {
- fn default() -> Self {
- Self {
- mouse_down: SmallVec::new(),
- mouse_up: SmallVec::new(),
- mouse_click: SmallVec::new(),
- mouse_move: SmallVec::new(),
- scroll_wheel: SmallVec::new(),
- }
- }
-}
-
-pub struct MouseClickEvent {
- pub down: MouseDownEvent,
- pub up: MouseUpEvent,
-}
@@ -5,9 +5,9 @@ mod mac;
mod test;
use crate::{
- AnyWindowHandle, Bounds, DevicePixels, Event, Executor, Font, FontId, FontMetrics, FontRun,
- GlobalPixels, GlyphId, LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams,
- RenderSvgParams, Result, Scene, SharedString, Size,
+ AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, FontRun,
+ GlobalPixels, GlyphId, InputEvent, LineLayout, Pixels, Point, RenderGlyphParams,
+ RenderImageParams, RenderSvgParams, Result, Scene, SharedString, Size,
};
use anyhow::anyhow;
use async_task::Runnable;
@@ -81,7 +81,7 @@ pub(crate) trait Platform: 'static {
fn on_resign_active(&self, callback: Box<dyn FnMut()>);
fn on_quit(&self, callback: Box<dyn FnMut()>);
fn on_reopen(&self, callback: Box<dyn FnMut()>);
- fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
+ fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
fn os_name(&self) -> &'static str;
fn os_version(&self) -> Result<SemanticVersion>;
@@ -141,7 +141,7 @@ pub(crate) trait PlatformWindow {
fn minimize(&self);
fn zoom(&self);
fn toggle_full_screen(&self);
- fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
+ fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>);
@@ -1,5 +1,5 @@
use crate::{
- point, px, Event, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
+ point, px, InputEvent, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection,
Pixels, ScrollDelta, ScrollWheelEvent, TouchPhase,
};
@@ -84,7 +84,7 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
}
}
-impl Event {
+impl InputEvent {
pub unsafe fn from_native(native_event: id, window_height: Option<Pixels>) -> Option<Self> {
let event_type = native_event.eventType();
@@ -1,6 +1,6 @@
use super::BoolExt;
use crate::{
- AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Event, Executor, MacDispatcher,
+ AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Executor, InputEvent, MacDispatcher,
MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, PathPromptOptions, Platform,
PlatformDisplay, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp,
WindowOptions,
@@ -153,7 +153,7 @@ pub struct MacPlatformState {
resign_active: Option<Box<dyn FnMut()>>,
reopen: Option<Box<dyn FnMut()>>,
quit: Option<Box<dyn FnMut()>>,
- event: Option<Box<dyn FnMut(Event) -> bool>>,
+ event: Option<Box<dyn FnMut(InputEvent) -> bool>>,
// menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
// validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
will_open_menu: Option<Box<dyn FnMut()>>,
@@ -621,7 +621,7 @@ impl Platform for MacPlatform {
self.0.lock().reopen = Some(callback);
}
- fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
+ fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
self.0.lock().event = Some(callback);
}
@@ -937,7 +937,7 @@ unsafe fn get_foreground_platform(object: &mut Object) -> &MacPlatform {
extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
unsafe {
- if let Some(event) = Event::from_native(native_event, None) {
+ if let Some(event) = InputEvent::from_native(native_event, None) {
let platform = get_foreground_platform(this);
if let Some(callback) = platform.0.lock().event.as_mut() {
if !callback(event) {
@@ -1,7 +1,7 @@
use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
use crate::{
- display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Event, Executor,
- GlobalPixels, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
+ display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, GlobalPixels,
+ InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance,
WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
@@ -286,7 +286,7 @@ struct MacWindowState {
renderer: MetalRenderer,
scene_to_render: Option<Scene>,
kind: WindowKind,
- event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
+ event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
activate_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
@@ -300,7 +300,7 @@ struct MacWindowState {
synthetic_drag_counter: usize,
last_fresh_keydown: Option<Keystroke>,
traffic_light_position: Option<Point<Pixels>>,
- previous_modifiers_changed_event: Option<Event>,
+ previous_modifiers_changed_event: Option<InputEvent>,
// State tracking what the IME did after the last request
ime_state: ImeState,
// Retains the last IME Text
@@ -854,7 +854,7 @@ impl PlatformWindow for MacWindow {
.detach();
}
- fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
+ fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
self.0.as_ref().lock().event_callback = Some(callback);
}
@@ -975,9 +975,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
let mut lock = window_state.as_ref().lock();
let window_height = lock.content_size().height;
- let event = unsafe { Event::from_native(native_event, Some(window_height)) };
+ let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
- if let Some(Event::KeyDown(event)) = event {
+ if let Some(InputEvent::KeyDown(event)) = event {
// For certain keystrokes, macOS will first dispatch a "key equivalent" event.
// If that event isn't handled, it will then dispatch a "key down" event. GPUI
// makes no distinction between these two types of events, so we need to ignore
@@ -1045,13 +1045,13 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
key: ime_text.clone().unwrap(),
},
};
- handled = callback(Event::KeyDown(event_with_ime_text));
+ handled = callback(InputEvent::KeyDown(event_with_ime_text));
}
if !handled {
// empty key happens when you type a deadkey in input composition.
// (e.g. on a brazillian keyboard typing quote is a deadkey)
if !event.keystroke.key.is_empty() {
- handled = callback(Event::KeyDown(event));
+ handled = callback(InputEvent::KeyDown(event));
}
}
}
@@ -1097,11 +1097,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
let window_height = lock.content_size().height;
- let event = unsafe { Event::from_native(native_event, Some(window_height)) };
+ let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
if let Some(mut event) = event {
let synthesized_second_event = match &mut event {
- Event::MouseDown(
+ InputEvent::MouseDown(
event @ MouseDownEvent {
button: MouseButton::Left,
modifiers: Modifiers { control: true, .. },
@@ -1118,7 +1118,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
..*event
};
- Some(Event::MouseDown(MouseDownEvent {
+ Some(InputEvent::MouseDown(MouseDownEvent {
button: MouseButton::Right,
..*event
}))
@@ -1127,7 +1127,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
// Because we map a ctrl-left_down to a right_down -> right_up let's ignore
// the ctrl-left_up to avoid having a mismatch in button down/up events if the
// user is still holding ctrl when releasing the left mouse button
- Event::MouseUp(MouseUpEvent {
+ InputEvent::MouseUp(MouseUpEvent {
button: MouseButton::Left,
modifiers: Modifiers { control: true, .. },
..
@@ -1140,7 +1140,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
};
match &event {
- Event::MouseMoved(
+ InputEvent::MouseMoved(
event @ MouseMoveEvent {
pressed_button: Some(_),
..
@@ -1157,18 +1157,18 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
.detach();
}
- Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
+ InputEvent::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
- Event::MouseUp(MouseUpEvent {
+ InputEvent::MouseUp(MouseUpEvent {
button: MouseButton::Left,
..
}) => {
lock.synthetic_drag_counter += 1;
}
- Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
+ InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
// Only raise modifiers changed event when they have actually changed
- if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
+ if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent {
modifiers: prev_modifiers,
})) = &lock.previous_modifiers_changed_event
{
@@ -1204,7 +1204,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
modifiers: Default::default(),
key: ".".into(),
};
- let event = Event::KeyDown(KeyDownEvent {
+ let event = InputEvent::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
is_held: false,
});
@@ -1605,7 +1605,7 @@ async fn synthetic_drag(
if lock.synthetic_drag_counter == drag_id {
if let Some(mut callback) = lock.event_callback.take() {
drop(lock);
- callback(Event::MouseMoved(event.clone()));
+ callback(InputEvent::MouseMoved(event.clone()));
window_state.lock().event_callback = Some(callback);
}
} else {
@@ -125,7 +125,7 @@ impl Platform for TestPlatform {
unimplemented!()
}
- fn on_event(&self, _callback: Box<dyn FnMut(crate::Event) -> bool>) {
+ fn on_event(&self, _callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
unimplemented!()
}
@@ -6,12 +6,12 @@ use crate::{
};
use std::{marker::PhantomData, sync::Arc};
-pub struct View<S: Send + Sync> {
- state: Handle<S>,
- render: Arc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S> + Send + Sync + 'static>,
+pub struct View<V: Send + Sync> {
+ state: Handle<V>,
+ render: Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + Sync + 'static>,
}
-impl<S: 'static + Send + Sync> View<S> {
+impl<V: 'static + Send + Sync> View<V> {
pub fn into_any(self) -> AnyView {
AnyView {
view: Arc::new(Mutex::new(self)),
@@ -19,7 +19,7 @@ impl<S: 'static + Send + Sync> View<S> {
}
}
-impl<S: Send + Sync> Clone for View<S> {
+impl<V: Send + Sync> Clone for View<V> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
@@ -28,13 +28,13 @@ impl<S: Send + Sync> Clone for View<S> {
}
}
-pub fn view<S, E>(
- state: Handle<S>,
- render: impl Fn(&mut S, &mut ViewContext<S>) -> E + Send + Sync + 'static,
-) -> View<S>
+pub fn view<V, E>(
+ state: Handle<V>,
+ render: impl Fn(&mut V, &mut ViewContext<V>) -> E + Send + Sync + 'static,
+) -> View<V>
where
- E: IntoAnyElement<S>,
- S: 'static + Send + Sync,
+ E: IntoAnyElement<V>,
+ V: 'static + Send + Sync,
{
View {
state,
@@ -42,8 +42,8 @@ where
}
}
-impl<S: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
- IntoAnyElement<ParentViewState> for View<S>
+impl<V: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
+ IntoAnyElement<ParentViewState> for View<V>
{
fn into_any(self) -> AnyElement<ParentViewState> {
AnyElement::new(EraseViewState {
@@ -53,74 +53,91 @@ impl<S: 'static + Send + Sync, ParentViewState: 'static + Send + Sync>
}
}
-impl<S: 'static + Send + Sync> Element for View<S> {
+impl<V: 'static + Send + Sync> Element for View<V> {
type ViewState = ();
- type ElementState = AnyElement<S>;
+ type ElementState = AnyElement<V>;
fn id(&self) -> Option<crate::ElementId> {
Some(ElementId::View(self.state.id))
}
- fn layout(
+ fn initialize(
&mut self,
- _: &mut Self::ViewState,
+ _: &mut (),
_: Option<Self::ElementState>,
- cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState) {
+ cx: &mut ViewContext<()>,
+ ) -> Self::ElementState {
self.state.update(cx, |state, cx| {
- let mut element = (self.render)(state, cx);
- let layout_id = element.layout(state, cx);
- (layout_id, element)
+ let mut any_element = (self.render)(state, cx);
+ any_element.initialize(state, cx);
+ any_element
})
}
+ fn layout(
+ &mut self,
+ _: &mut (),
+ element: &mut Self::ElementState,
+ cx: &mut ViewContext<()>,
+ ) -> LayoutId {
+ self.state.update(cx, |state, cx| element.layout(state, cx))
+ }
+
fn paint(
&mut self,
_: Bounds<Pixels>,
- _: &mut Self::ViewState,
+ _: &mut (),
element: &mut Self::ElementState,
- cx: &mut ViewContext<Self::ViewState>,
+ cx: &mut ViewContext<()>,
) {
self.state
.update(cx, |state, cx| element.paint(state, None, cx))
}
}
-struct EraseViewState<ViewState: 'static + Send + Sync, ParentViewState> {
- view: View<ViewState>,
- parent_view_state_type: PhantomData<ParentViewState>,
+struct EraseViewState<V: 'static + Send + Sync, ParentV> {
+ view: View<V>,
+ parent_view_state_type: PhantomData<ParentV>,
}
-impl<ViewState, ParentViewState> IntoAnyElement<ParentViewState>
- for EraseViewState<ViewState, ParentViewState>
+impl<V, ParentV> IntoAnyElement<ParentV> for EraseViewState<V, ParentV>
where
- ViewState: 'static + Send + Sync,
- ParentViewState: 'static + Send + Sync,
+ V: 'static + Send + Sync,
+ ParentV: 'static + Send + Sync,
{
- fn into_any(self) -> AnyElement<ParentViewState> {
+ fn into_any(self) -> AnyElement<ParentV> {
AnyElement::new(self)
}
}
-impl<ViewState, ParentViewState> Element for EraseViewState<ViewState, ParentViewState>
+impl<V, ParentV> Element for EraseViewState<V, ParentV>
where
- ViewState: 'static + Send + Sync,
- ParentViewState: 'static + Send + Sync,
+ V: 'static + Send + Sync,
+ ParentV: 'static + Send + Sync,
{
- type ViewState = ParentViewState;
+ type ViewState = ParentV;
type ElementState = AnyBox;
fn id(&self) -> Option<crate::ElementId> {
Element::id(&self.view)
}
- fn layout(
+ fn initialize(
&mut self,
_: &mut Self::ViewState,
_: Option<Self::ElementState>,
cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState) {
- ViewObject::layout(&mut self.view, cx)
+ ) -> Self::ElementState {
+ ViewObject::initialize(&mut self.view, cx)
+ }
+
+ fn layout(
+ &mut self,
+ _: &mut Self::ViewState,
+ element: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> LayoutId {
+ ViewObject::layout(&mut self.view, element, cx)
}
fn paint(
@@ -136,22 +153,31 @@ where
trait ViewObject: 'static + Send + Sync {
fn entity_id(&self) -> EntityId;
- fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox);
+ fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox;
+ fn layout(&mut self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId;
fn paint(&mut self, bounds: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext);
}
-impl<S: Send + Sync + 'static> ViewObject for View<S> {
+impl<V: Send + Sync + 'static> ViewObject for View<V> {
fn entity_id(&self) -> EntityId {
self.state.id
}
- fn layout(&mut self, cx: &mut WindowContext) -> (LayoutId, AnyBox) {
+ fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox {
cx.with_element_id(self.entity_id(), |cx| {
self.state.update(cx, |state, cx| {
- let mut element = (self.render)(state, cx);
- let layout_id = element.layout(state, cx);
- let element = Box::new(element) as AnyBox;
- (layout_id, element)
+ let mut any_element = Box::new((self.render)(state, cx));
+ any_element.initialize(state, cx);
+ any_element as AnyBox
+ })
+ })
+ }
+
+ fn layout(&mut self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId {
+ cx.with_element_id(self.entity_id(), |cx| {
+ self.state.update(cx, |state, cx| {
+ let element = element.downcast_mut::<AnyElement<V>>().unwrap();
+ element.layout(state, cx)
})
})
}
@@ -159,7 +185,7 @@ impl<S: Send + Sync + 'static> ViewObject for View<S> {
fn paint(&mut self, _: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext) {
cx.with_element_id(self.entity_id(), |cx| {
self.state.update(cx, |state, cx| {
- let element = element.downcast_mut::<AnyElement<S>>().unwrap();
+ let element = element.downcast_mut::<AnyElement<V>>().unwrap();
element.paint(state, None, cx);
});
});
@@ -170,11 +196,11 @@ pub struct AnyView {
view: Arc<Mutex<dyn ViewObject>>,
}
-impl<ParentViewState> IntoAnyElement<ParentViewState> for AnyView
+impl<ParentV> IntoAnyElement<ParentV> for AnyView
where
- ParentViewState: 'static + Send + Sync,
+ ParentV: 'static + Send + Sync,
{
- fn into_any(self) -> AnyElement<ParentViewState> {
+ fn into_any(self) -> AnyElement<ParentV> {
AnyElement::new(EraseAnyViewState {
view: self,
parent_view_state_type: PhantomData,
@@ -190,13 +216,22 @@ impl Element for AnyView {
Some(ElementId::View(self.view.lock().entity_id()))
}
- fn layout(
+ fn initialize(
&mut self,
_: &mut Self::ViewState,
_: Option<Self::ElementState>,
cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState) {
- self.view.lock().layout(cx)
+ ) -> Self::ElementState {
+ self.view.lock().initialize(cx)
+ }
+
+ fn layout(
+ &mut self,
+ _: &mut Self::ViewState,
+ element: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> LayoutId {
+ self.view.lock().layout(element, cx)
}
fn paint(
@@ -215,33 +250,42 @@ struct EraseAnyViewState<ParentViewState> {
parent_view_state_type: PhantomData<ParentViewState>,
}
-impl<ParentViewState> IntoAnyElement<ParentViewState> for EraseAnyViewState<ParentViewState>
+impl<ParentV> IntoAnyElement<ParentV> for EraseAnyViewState<ParentV>
where
- ParentViewState: 'static + Send + Sync,
+ ParentV: 'static + Send + Sync,
{
- fn into_any(self) -> AnyElement<ParentViewState> {
+ fn into_any(self) -> AnyElement<ParentV> {
AnyElement::new(self)
}
}
-impl<ParentViewState> Element for EraseAnyViewState<ParentViewState>
+impl<ParentV> Element for EraseAnyViewState<ParentV>
where
- ParentViewState: 'static + Send + Sync,
+ ParentV: 'static + Send + Sync,
{
- type ViewState = ParentViewState;
+ type ViewState = ParentV;
type ElementState = AnyBox;
fn id(&self) -> Option<crate::ElementId> {
Element::id(&self.view)
}
- fn layout(
+ fn initialize(
&mut self,
_: &mut Self::ViewState,
_: Option<Self::ElementState>,
cx: &mut ViewContext<Self::ViewState>,
- ) -> (LayoutId, Self::ElementState) {
- self.view.view.lock().layout(cx)
+ ) -> Self::ElementState {
+ self.view.view.lock().initialize(cx)
+ }
+
+ fn layout(
+ &mut self,
+ _: &mut Self::ViewState,
+ element: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> LayoutId {
+ self.view.view.lock().layout(element, cx)
}
fn paint(
@@ -1,7 +1,8 @@
use crate::{
px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext,
Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId,
- Event, EventEmitter, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, IsZero,
+ EventEmitter, FocusEvent, FocusListener, FontId, GlobalElementId, GlyphId, Handle, Hsla,
+ ImageData, InputEvent, IsZero, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener,
LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform,
PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
@@ -21,7 +22,7 @@ use std::{
mem,
sync::Arc,
};
-use util::ResultExt;
+use util::{post_inc, ResultExt};
#[derive(Deref, DerefMut, Ord, PartialOrd, Eq, PartialEq, Clone, Default)]
pub struct StackingOrder(pub(crate) SmallVec<[u32; 16]>);
@@ -39,8 +40,55 @@ pub enum DispatchPhase {
Capture,
}
-type MouseEventHandler =
- Arc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyMouseEventListener =
+ Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyKeyboardEventListener =
+ Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyFocusListener = Box<dyn Fn(&FocusEvent, &mut WindowContext) + Send + Sync + 'static>;
+type AnyKeyDownListener =
+ Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyKeyUpListener =
+ Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct FocusId(usize);
+
+#[derive(Clone, PartialEq, Eq)]
+pub struct FocusHandle {
+ pub(crate) id: FocusId,
+}
+
+impl FocusHandle {
+ pub(crate) fn new(id: FocusId) -> Self {
+ Self { id }
+ }
+
+ pub fn is_focused(&self, cx: &WindowContext) -> bool {
+ cx.window.focus == Some(self.id)
+ }
+
+ pub fn contains_focused(&self, cx: &WindowContext) -> bool {
+ cx.focused()
+ .map_or(false, |focused| self.contains(&focused, cx))
+ }
+
+ pub fn within_focused(&self, cx: &WindowContext) -> bool {
+ let focused = cx.focused();
+ focused.map_or(false, |focused| focused.contains(self, cx))
+ }
+
+ pub(crate) fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
+ let mut ancestor = Some(other.id);
+ while let Some(ancestor_id) = ancestor {
+ if self.id == ancestor_id {
+ return true;
+ } else {
+ ancestor = cx.window.focus_parents_by_child.get(&ancestor_id).copied();
+ }
+ }
+ false
+ }
+}
pub struct Window {
handle: AnyWindowHandle,
@@ -56,12 +104,19 @@ pub struct Window {
element_states: HashMap<GlobalElementId, AnyBox>,
z_index_stack: StackingOrder,
content_mask_stack: Vec<ContentMask<Pixels>>,
- mouse_event_handlers: HashMap<TypeId, Vec<(StackingOrder, MouseEventHandler)>>,
+ mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseEventListener)>>,
+ keyboard_listeners: HashMap<TypeId, Vec<AnyKeyboardEventListener>>,
+ focus_stack: Vec<FocusStackFrame>,
+ focus_parents_by_child: HashMap<FocusId, FocusId>,
+ pub(crate) focus_listeners: Vec<AnyFocusListener>,
propagate_event: bool,
mouse_position: Point<Pixels>,
scale_factor: f32,
pub(crate) scene_builder: SceneBuilder,
pub(crate) dirty: bool,
+ pub(crate) last_blur: Option<Option<FocusId>>,
+ pub(crate) focus: Option<FocusId>,
+ next_focus_id: FocusId,
}
impl Window {
@@ -95,7 +150,7 @@ impl Window {
}
}));
- platform_window.on_event({
+ platform_window.on_input({
let cx = cx.to_async();
Box::new(move |event| {
cx.update_window(handle, |cx| cx.dispatch_event(event))
@@ -120,12 +175,19 @@ impl Window {
element_states: HashMap::default(),
z_index_stack: StackingOrder(SmallVec::new()),
content_mask_stack: Vec::new(),
- mouse_event_handlers: HashMap::default(),
+ mouse_listeners: HashMap::default(),
+ keyboard_listeners: HashMap::default(),
+ focus_stack: Vec::new(),
+ focus_parents_by_child: HashMap::default(),
+ focus_listeners: Vec::new(),
propagate_event: true,
mouse_position,
scale_factor,
scene_builder: SceneBuilder::new(),
dirty: true,
+ last_blur: None,
+ focus: None,
+ next_focus_id: FocusId(0),
}
}
}
@@ -149,6 +211,12 @@ impl ContentMask<Pixels> {
}
}
+struct FocusStackFrame {
+ handle: FocusHandle,
+ key_down_listeners: SmallVec<[AnyKeyDownListener; 2]>,
+ key_up_listeners: SmallVec<[AnyKeyUpListener; 2]>,
+}
+
pub struct WindowContext<'a, 'w> {
app: Reference<'a, AppContext>,
pub(crate) window: Reference<'w, Window>,
@@ -166,6 +234,43 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.window.dirty = true;
}
+ pub fn focus_handle(&mut self) -> FocusHandle {
+ let id = FocusId(post_inc(&mut self.window.next_focus_id.0));
+ FocusHandle { id }
+ }
+
+ pub fn focused(&self) -> Option<FocusHandle> {
+ self.window.focus.map(|id| FocusHandle::new(id))
+ }
+
+ pub fn focus(&mut self, handle: &FocusHandle) {
+ if self.window.last_blur.is_none() {
+ self.window.last_blur = Some(self.window.focus);
+ }
+
+ let window_id = self.window.handle.id;
+ self.window.focus = Some(handle.id);
+ self.push_effect(Effect::FocusChanged {
+ window_id,
+ focused: Some(handle.id),
+ });
+ self.notify();
+ }
+
+ pub fn blur(&mut self) {
+ if self.window.last_blur.is_none() {
+ self.window.last_blur = Some(self.window.focus);
+ }
+
+ let window_id = self.window.handle.id;
+ self.window.focus = None;
+ self.push_effect(Effect::FocusChanged {
+ window_id,
+ focused: None,
+ });
+ self.notify();
+ }
+
pub fn run_on_main<R>(
&mut self,
f: impl FnOnce(&mut MainThread<WindowContext<'_, '_>>) -> R + Send + 'static,
@@ -298,17 +403,30 @@ impl<'a, 'w> WindowContext<'a, 'w> {
) {
let order = self.window.z_index_stack.clone();
self.window
- .mouse_event_handlers
+ .mouse_listeners
.entry(TypeId::of::<Event>())
.or_default()
.push((
order,
- Arc::new(move |event: &dyn Any, phase, cx| {
+ Box::new(move |event: &dyn Any, phase, cx| {
handler(event.downcast_ref().unwrap(), phase, cx)
}),
))
}
+ pub fn on_keyboard_event<Event: 'static>(
+ &mut self,
+ handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static,
+ ) {
+ self.window
+ .keyboard_listeners
+ .entry(TypeId::of::<Event>())
+ .or_default()
+ .push(Box::new(move |event: &dyn Any, phase, cx| {
+ handler(event.downcast_ref().unwrap(), phase, cx)
+ }))
+ }
+
pub fn mouse_position(&self) -> Point<Pixels> {
self.window.mouse_position
}
@@ -628,7 +746,8 @@ impl<'a, 'w> WindowContext<'a, 'w> {
element_state: Option<AnyBox>,
cx: &mut ViewContext<()>,
) -> AnyBox {
- let (layout_id, mut element_state) = root_view.layout(&mut (), element_state, cx);
+ let mut element_state = root_view.initialize(&mut (), element_state, cx);
+ let layout_id = root_view.layout(&mut (), &mut element_state, cx);
let available_space = cx.window.content_size.map(Into::into);
cx.window
.layout_engine
@@ -645,21 +764,24 @@ impl<'a, 'w> WindowContext<'a, 'w> {
// reference during the upcoming frame.
let window = &mut *self.window;
mem::swap(&mut window.element_states, &mut window.prev_element_states);
- self.window.element_states.clear();
+ window.element_states.clear();
// Clear mouse event listeners, because elements add new element listeners
// when the upcoming frame is painted.
- self.window
- .mouse_event_handlers
- .values_mut()
- .for_each(Vec::clear);
+ window.mouse_listeners.values_mut().for_each(Vec::clear);
+
+ // Clear focus state, because we determine what is focused when the new elements
+ // in the upcoming frame are initialized.
+ window.focus_listeners.clear();
+ window.keyboard_listeners.values_mut().for_each(Vec::clear);
+ window.focus_parents_by_child.clear();
}
fn end_frame(&mut self) {
self.text_system().end_frame();
}
- fn dispatch_event(&mut self, event: Event) -> bool {
+ fn dispatch_event(&mut self, event: InputEvent) -> bool {
if let Some(any_mouse_event) = event.mouse_event() {
if let Some(MouseMoveEvent { position, .. }) = any_mouse_event.downcast_ref() {
self.window.mouse_position = *position;
@@ -667,7 +789,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
if let Some(mut handlers) = self
.window
- .mouse_event_handlers
+ .mouse_listeners
.remove(&any_mouse_event.type_id())
{
// Because handlers may add other handlers, we sort every time.
@@ -698,15 +820,48 @@ impl<'a, 'w> WindowContext<'a, 'w> {
// Just in case any handlers added new handlers, which is weird, but possible.
handlers.extend(
self.window
- .mouse_event_handlers
+ .mouse_listeners
.get_mut(&any_mouse_event.type_id())
.into_iter()
.flat_map(|handlers| handlers.drain(..)),
);
self.window
- .mouse_event_handlers
+ .mouse_listeners
.insert(any_mouse_event.type_id(), handlers);
}
+ } else if let Some(any_keyboard_event) = event.keyboard_event() {
+ if let Some(mut handlers) = self
+ .window
+ .keyboard_listeners
+ .remove(&any_keyboard_event.type_id())
+ {
+ for handler in &handlers {
+ handler(any_keyboard_event, DispatchPhase::Capture, self);
+ if !self.window.propagate_event {
+ break;
+ }
+ }
+
+ if self.window.propagate_event {
+ for handler in handlers.iter().rev() {
+ handler(any_keyboard_event, DispatchPhase::Bubble, self);
+ if !self.window.propagate_event {
+ break;
+ }
+ }
+ }
+
+ handlers.extend(
+ self.window
+ .keyboard_listeners
+ .get_mut(&any_keyboard_event.type_id())
+ .into_iter()
+ .flat_map(|handlers| handlers.drain(..)),
+ );
+ self.window
+ .keyboard_listeners
+ .insert(any_keyboard_event.type_id(), handlers);
+ }
}
true
@@ -882,7 +1037,7 @@ impl<S> BorrowWindow for ViewContext<'_, '_, S> {
}
}
-impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
+impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
fn mutable(app: &'a mut AppContext, window: &'w mut Window, entity_id: EntityId) -> Self {
Self {
window_cx: WindowContext::mutable(app, window),
@@ -891,7 +1046,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
}
}
- pub fn handle(&self) -> WeakHandle<S> {
+ pub fn handle(&self) -> WeakHandle<V> {
self.entities.weak_handle(self.entity_id)
}
@@ -902,7 +1057,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
result
}
- pub fn on_next_frame(&mut self, f: impl FnOnce(&mut S, &mut ViewContext<S>) + Send + 'static) {
+ pub fn on_next_frame(&mut self, f: impl FnOnce(&mut V, &mut ViewContext<V>) + Send + 'static) {
let entity = self.handle();
self.window_cx.on_next_frame(move |cx| {
entity.update(cx, f).ok();
@@ -912,7 +1067,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
pub fn observe<E: Send + Sync + 'static>(
&mut self,
handle: &Handle<E>,
- on_notify: impl Fn(&mut S, Handle<E>, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
+ on_notify: impl Fn(&mut V, Handle<E>, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
) -> Subscription {
let this = self.handle();
let handle = handle.downgrade();
@@ -936,7 +1091,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
&mut self,
handle: &Handle<E>,
- on_event: impl Fn(&mut S, Handle<E>, &E::Event, &mut ViewContext<'_, '_, S>)
+ on_event: impl Fn(&mut V, Handle<E>, &E::Event, &mut ViewContext<'_, '_, V>)
+ Send
+ Sync
+ 'static,
@@ -963,7 +1118,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
pub fn on_release(
&mut self,
- on_release: impl Fn(&mut S, &mut WindowContext) + Send + Sync + 'static,
+ on_release: impl Fn(&mut V, &mut WindowContext) + Send + Sync + 'static,
) -> Subscription {
let window_handle = self.window.handle;
self.app.release_handlers.insert(
@@ -979,7 +1134,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
pub fn observe_release<E: Send + Sync + 'static>(
&mut self,
handle: &Handle<E>,
- on_release: impl Fn(&mut S, &mut E, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
+ on_release: impl Fn(&mut V, &mut E, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
) -> Subscription {
let this = self.handle();
let window_handle = self.window.handle;
@@ -1002,10 +1157,86 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
});
}
+ pub fn with_focus<R>(
+ &mut self,
+ focus_handle: Option<FocusHandle>,
+ key_down: impl IntoIterator<Item = KeyDownListener<V>>,
+ key_up: impl IntoIterator<Item = KeyUpListener<V>>,
+ focus: impl IntoIterator<Item = FocusListener<V>>,
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ let Some(focus_handle) = focus_handle else {
+ return f(self);
+ };
+
+ let handle = self.handle();
+ let window = &mut *self.window;
+
+ for listener in focus {
+ let handle = handle.clone();
+ window.focus_listeners.push(Box::new(move |event, cx| {
+ handle
+ .update(cx, |view, cx| listener(view, event, cx))
+ .log_err();
+ }));
+ }
+
+ let mut focus_stack = mem::take(&mut window.focus_stack);
+ if let Some(parent_frame) = focus_stack.last() {
+ window
+ .focus_parents_by_child
+ .insert(focus_handle.id, parent_frame.handle.id);
+ }
+
+ let mut frame = FocusStackFrame {
+ handle: focus_handle.clone(),
+ key_down_listeners: SmallVec::new(),
+ key_up_listeners: SmallVec::new(),
+ };
+
+ for listener in key_down {
+ let handle = handle.clone();
+ frame
+ .key_down_listeners
+ .push(Box::new(move |event, phase, cx| {
+ handle
+ .update(cx, |view, cx| listener(view, event, phase, cx))
+ .log_err();
+ }));
+ }
+ for listener in key_up {
+ let handle = handle.clone();
+ frame
+ .key_up_listeners
+ .push(Box::new(move |event, phase, cx| {
+ handle
+ .update(cx, |view, cx| listener(view, event, phase, cx))
+ .log_err();
+ }));
+ }
+ focus_stack.push(frame);
+
+ if Some(focus_handle.id) == window.focus {
+ for focus_frame in &mut focus_stack {
+ for listener in focus_frame.key_down_listeners.drain(..) {
+ self.window_cx.on_keyboard_event(listener);
+ }
+ for listener in focus_frame.key_up_listeners.drain(..) {
+ self.window_cx.on_keyboard_event(listener);
+ }
+ }
+ }
+
+ self.window.focus_stack = focus_stack;
+ let result = f(self);
+ self.window.focus_stack.pop();
+ result
+ }
+
pub fn run_on_main<R>(
&mut self,
- view: &mut S,
- f: impl FnOnce(&mut S, &mut MainThread<ViewContext<'_, '_, S>>) -> R + Send + 'static,
+ view: &mut V,
+ f: impl FnOnce(&mut V, &mut MainThread<ViewContext<'_, '_, V>>) -> R + Send + 'static,
) -> Task<Result<R>>
where
R: Send + 'static,
@@ -1021,7 +1252,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
pub fn spawn<Fut, R>(
&mut self,
- f: impl FnOnce(WeakHandle<S>, AsyncWindowContext) -> Fut + Send + 'static,
+ f: impl FnOnce(WeakHandle<V>, AsyncWindowContext) -> Fut + Send + 'static,
) -> Task<R>
where
R: Send + 'static,
@@ -1036,7 +1267,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
pub fn on_mouse_event<Event: 'static>(
&mut self,
- handler: impl Fn(&mut S, &Event, DispatchPhase, &mut ViewContext<S>) + Send + Sync + 'static,
+ handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
) {
let handle = self.handle().upgrade(self).unwrap();
self.window_cx.on_mouse_event(move |event, phase, cx| {
@@ -1045,6 +1276,18 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
})
});
}
+
+ pub fn on_keyboard_event<Event: 'static>(
+ &mut self,
+ handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
+ ) {
+ let handle = self.handle().upgrade(self).unwrap();
+ self.window_cx.on_keyboard_event(move |event, phase, cx| {
+ handle.update(cx, |view, cx| {
+ handler(view, event, phase, cx);
+ })
+ });
+ }
}
impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {
@@ -57,27 +57,36 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
None
}
- fn layout(
+ fn initialize(
&mut self,
view_state: &mut Self::ViewState,
- element_state: Option<Self::ElementState>,
- cx: &mut gpui3::ViewContext<Self::ViewState>,
- ) -> (gpui3::LayoutId, Self::ElementState) {
+ _: Option<Self::ElementState>,
+ cx: &mut gpui3::ViewContext<Self::ViewState>
+ ) -> Self::ElementState {
use gpui3::IntoAnyElement;
- let mut rendered_element = self.render(view_state, cx).into_any();
- let layout_id = rendered_element.layout(view_state, cx);
- (layout_id, rendered_element)
+ let mut element = self.render(view_state, cx).into_any();
+ element.initialize(view_state, cx);
+ element
+ }
+
+ fn layout(
+ &mut self,
+ view_state: &mut Self::ViewState,
+ rendered_element: &mut Self::ElementState,
+ cx: &mut gpui3::ViewContext<Self::ViewState>,
+ ) -> gpui3::LayoutId {
+ rendered_element.layout(view_state, cx)
}
fn paint(
&mut self,
bounds: gpui3::Bounds<gpui3::Pixels>,
view_state: &mut Self::ViewState,
- element_state: &mut Self::ElementState,
+ rendered_element: &mut Self::ElementState,
cx: &mut gpui3::ViewContext<Self::ViewState>,
) {
- element_state.paint(view_state, None, cx)
+ rendered_element.paint(view_state, None, cx)
}
}
};
@@ -164,31 +164,42 @@ impl<E: Element> Element for Themed<E> {
None
}
- fn layout(
+ fn initialize(
&mut self,
- state: &mut E::ViewState,
+ view_state: &mut Self::ViewState,
element_state: Option<Self::ElementState>,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> Self::ElementState {
+ cx.with_global(self.theme.clone(), |cx| {
+ self.child.initialize(view_state, element_state, cx)
+ })
+ }
+
+ fn layout(
+ &mut self,
+ view_state: &mut E::ViewState,
+ element_state: &mut Self::ElementState,
cx: &mut ViewContext<E::ViewState>,
- ) -> (LayoutId, Self::ElementState)
+ ) -> LayoutId
where
Self: Sized,
{
cx.with_global(self.theme.clone(), |cx| {
- self.child.layout(state, element_state, cx)
+ self.child.layout(view_state, element_state, cx)
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
- state: &mut Self::ViewState,
+ view_state: &mut Self::ViewState,
frame_state: &mut Self::ElementState,
cx: &mut ViewContext<Self::ViewState>,
) where
Self: Sized,
{
cx.with_global(self.theme.clone(), |cx| {
- self.child.paint(bounds, state, frame_state, cx);
+ self.child.paint(bounds, view_state, frame_state, cx);
});
}
}