From ffa3362e166fed44d49cc3418c997ccc7fd0994b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 19 Oct 2023 15:36:31 +0200 Subject: [PATCH 1/6] Checkpoint --- crates/gpui3/src/elements/div.rs | 13 ++-- crates/gpui3/src/events.rs | 31 ++++---- crates/gpui3/src/focus.rs | 26 +++++-- crates/gpui3/src/interactive.rs | 16 ++-- crates/gpui3/src/window.rs | 126 ++++++++++++------------------- 5 files changed, 102 insertions(+), 110 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index ae81ec51ef94826a8fad9b5ee3eeb5c8aa217f63..938325ac89406786594638750c06564d8dcec4f6 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -423,18 +423,21 @@ where element_state: Option, cx: &mut ViewContext, ) -> Self::ElementState { + let key_listeners = mem::take(&mut self.listeners.key); + let focus_listeners = mem::take(&mut self.listeners.focus); 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), + &key_listeners, + &focus_listeners, |cx| { for child in &mut self.children { child.initialize(view_state, cx); } - element_state.unwrap_or_default() }, - ) + ); + self.listeners.key = key_listeners; + self.listeners.focus = focus_listeners; + element_state.unwrap_or_default() } fn layout( diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 8adfba37ada3cd9038378a8f421e352433b001ce..58228b4175139d2da20b032e8acb8e24ec1a32d4 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -2,7 +2,11 @@ use crate::{ point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext, }; use smallvec::SmallVec; -use std::{any::Any, ops::Deref}; +use std::{ + any::{Any, TypeId}, + ops::Deref, + sync::Arc, +}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct KeyDownEvent { @@ -221,43 +225,40 @@ pub struct FocusEvent { pub focused: Option, } -pub type MouseDownListener = Box< +pub type MouseDownListener = Arc< dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -pub type MouseUpListener = Box< +pub type MouseUpListener = Arc< dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; pub type MouseClickListener = - Box) + Send + Sync + 'static>; + Arc) + Send + Sync + 'static>; -pub type MouseMoveListener = Box< +pub type MouseMoveListener = Arc< dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -pub type ScrollWheelListener = Box< +pub type ScrollWheelListener = Arc< dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, >; -pub type KeyDownListener = - Box) + Send + Sync + 'static>; - -pub type KeyUpListener = - Box) + Send + Sync + 'static>; +pub type KeyListener = + Arc) + Send + Sync + 'static>; pub type FocusListener = - Box) + Send + Sync + 'static>; + Arc) + Send + Sync + 'static>; pub struct EventListeners { pub mouse_down: SmallVec<[MouseDownListener; 2]>, @@ -265,8 +266,7 @@ pub struct EventListeners { pub mouse_click: SmallVec<[MouseClickListener; 2]>, pub mouse_move: SmallVec<[MouseMoveListener; 2]>, pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, - pub key_down: SmallVec<[KeyDownListener; 2]>, - pub key_up: SmallVec<[KeyUpListener; 2]>, + pub key: SmallVec<[(TypeId, KeyListener); 32]>, pub focus: SmallVec<[FocusListener; 2]>, } @@ -278,8 +278,7 @@ impl Default for EventListeners { mouse_click: SmallVec::new(), mouse_move: SmallVec::new(), scroll_wheel: SmallVec::new(), - key_down: SmallVec::new(), - key_up: SmallVec::new(), + key: SmallVec::new(), focus: SmallVec::new(), } } diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs index 12f0679655f5e7066d6b78ee47065b8edf2ad6e9..ecfda740f2296bc4e2aa24abe54210854793af18 100644 --- a/crates/gpui3/src/focus.rs +++ b/crates/gpui3/src/focus.rs @@ -1,3 +1,5 @@ +use std::{any::TypeId, sync::Arc}; + use crate::{ DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, StyleRefinement, ViewContext, @@ -46,7 +48,7 @@ pub trait Focus: Interactive { let handle = self.handle().clone(); self.listeners() .focus - .push(Box::new(move |view, event, cx| { + .push(Arc::new(move |view, event, cx| { if event.focused.as_ref() == Some(&handle) { listener(view, event, cx) } @@ -67,7 +69,7 @@ pub trait Focus: Interactive { let handle = self.handle().clone(); self.listeners() .focus - .push(Box::new(move |view, event, cx| { + .push(Arc::new(move |view, event, cx| { if event.blurred.as_ref() == Some(&handle) { listener(view, event, cx) } @@ -88,7 +90,7 @@ pub trait Focus: Interactive { let handle = self.handle().clone(); self.listeners() .focus - .push(Box::new(move |view, event, cx| { + .push(Arc::new(move |view, event, cx| { let descendant_blurred = event .blurred .as_ref() @@ -118,7 +120,7 @@ pub trait Focus: Interactive { let handle = self.handle().clone(); self.listeners() .focus - .push(Box::new(move |view, event, cx| { + .push(Arc::new(move |view, event, cx| { let descendant_blurred = event .blurred .as_ref() @@ -148,7 +150,13 @@ pub trait Focus: Interactive { where Self: Sized, { - self.listeners().key_down.push(Box::new(listener)); + self.listeners().key.push(( + TypeId::of::(), + Arc::new(move |view, event, phase, cx| { + let event = event.downcast_ref().unwrap(); + listener(view, event, phase, cx) + }), + )); self } @@ -162,7 +170,13 @@ pub trait Focus: Interactive { where Self: Sized, { - self.listeners().key_up.push(Box::new(listener)); + self.listeners().key.push(( + TypeId::of::(), + Arc::new(move |view, event, phase, cx| { + let event = event.downcast_ref().unwrap(); + listener(view, event, phase, cx) + }), + )); self } } diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index f328f22cc0606ccc7708a4ab785e479ff7392785..ac150652dd1e5d9bbd27c3b6c247c2737954ba8a 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::{ DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext, @@ -19,7 +21,7 @@ pub trait Interactive: Element { { self.listeners() .mouse_down - .push(Box::new(move |view, event, bounds, phase, cx| { + .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && event.button == button && bounds.contains_point(&event.position) @@ -43,7 +45,7 @@ pub trait Interactive: Element { { self.listeners() .mouse_up - .push(Box::new(move |view, event, bounds, phase, cx| { + .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && event.button == button && bounds.contains_point(&event.position) @@ -67,7 +69,7 @@ pub trait Interactive: Element { { self.listeners() .mouse_down - .push(Box::new(move |view, event, bounds, phase, cx| { + .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture && event.button == button && !bounds.contains_point(&event.position) @@ -91,7 +93,7 @@ pub trait Interactive: Element { { self.listeners() .mouse_up - .push(Box::new(move |view, event, bounds, phase, cx| { + .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture && event.button == button && !bounds.contains_point(&event.position) @@ -114,7 +116,7 @@ pub trait Interactive: Element { { self.listeners() .mouse_move - .push(Box::new(move |view, event, bounds, phase, cx| { + .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { handler(view, event, cx); } @@ -134,7 +136,7 @@ pub trait Interactive: Element { { self.listeners() .scroll_wheel - .push(Box::new(move |view, event, bounds, phase, cx| { + .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { handler(view, event, cx); } @@ -156,7 +158,7 @@ pub trait Click: Interactive { { self.listeners() .mouse_click - .push(Box::new(move |view, event, cx| handler(view, event, cx))); + .push(Arc::new(move |view, event, cx| handler(view, event, cx))); self } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index e0edbac4489511bf5911877e6e75c25d5203cb92..ca3acc41ecf5c627f844037c07696be2911d2181 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -2,12 +2,11 @@ use crate::{ px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FocusListener, FontId, GlobalElementId, GlyphId, Handle, Hsla, - ImageData, InputEvent, IsZero, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener, - LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, - PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, - RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, - WindowOptions, SUBPIXEL_VARIANTS, + ImageData, InputEvent, IsZero, KeyListener, 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; @@ -45,15 +44,8 @@ pub enum DispatchPhase { Capture, } -type AnyMouseEventListener = - Box; -type AnyKeyboardEventListener = - Box; -type AnyFocusListener = Box; -type AnyKeyDownListener = - Box; -type AnyKeyUpListener = - Box; +type AnyListener = Arc; +type AnyFocusListener = Arc; slotmap::new_key_type! { pub struct FocusId; } @@ -153,9 +145,10 @@ pub struct Window { element_states: HashMap, z_index_stack: StackingOrder, content_mask_stack: Vec>, - mouse_listeners: HashMap>, - keyboard_listeners: HashMap>, - focus_stack: Vec, + mouse_listeners: HashMap>, + key_listeners: HashMap>, + push_key_listeners: bool, + focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, pub(crate) focus_handles: Arc>>, @@ -226,7 +219,8 @@ impl Window { z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), mouse_listeners: HashMap::default(), - keyboard_listeners: HashMap::default(), + key_listeners: HashMap::default(), + push_key_listeners: true, focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), focus_listeners: Vec::new(), @@ -262,12 +256,6 @@ impl ContentMask { } } -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>, @@ -468,7 +456,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { .or_default() .push(( order, - Box::new(move |event: &dyn Any, phase, cx| { + Arc::new(move |event: &dyn Any, phase, cx| { handler(event.downcast_ref().unwrap(), phase, cx) }), )) @@ -479,10 +467,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static, ) { self.window - .keyboard_listeners + .key_listeners .entry(TypeId::of::()) .or_default() - .push(Box::new(move |event: &dyn Any, phase, cx| { + .push(Arc::new(move |event: &dyn Any, phase, cx| { handler(event.downcast_ref().unwrap(), phase, cx) })) } @@ -833,8 +821,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.keyboard_listeners.values_mut().for_each(Vec::clear); + window.key_listeners.values_mut().for_each(Vec::clear); window.focus_parents_by_child.clear(); + window.push_key_listeners = true; } fn end_frame(&mut self) { @@ -893,7 +882,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { } else if let Some(any_keyboard_event) = event.keyboard_event() { if let Some(mut handlers) = self .window - .keyboard_listeners + .key_listeners .remove(&any_keyboard_event.type_id()) { for handler in &handlers { @@ -914,13 +903,13 @@ impl<'a, 'w> WindowContext<'a, 'w> { handlers.extend( self.window - .keyboard_listeners + .key_listeners .get_mut(&any_keyboard_event.type_id()) .into_iter() .flat_map(|handlers| handlers.drain(..)), ); self.window - .keyboard_listeners + .key_listeners .insert(any_keyboard_event.type_id(), handlers); } } @@ -1221,9 +1210,8 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { pub fn with_focus( &mut self, focus_handle: Option, - key_down: impl IntoIterator>, - key_up: impl IntoIterator>, - focus: impl IntoIterator>, + key_listeners: &[(TypeId, KeyListener)], + focus_listeners: &[FocusListener], f: impl FnOnce(&mut Self) -> R, ) -> R { let Some(focus_handle) = focus_handle else { @@ -1233,63 +1221,49 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let handle = self.handle(); let window = &mut *self.window; - for listener in focus { + for listener in focus_listeners.iter().cloned() { let handle = handle.clone(); - window.focus_listeners.push(Box::new(move |event, cx| { + window.focus_listeners.push(Arc::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() { + if let Some(parent_focus_id) = window.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(); - })); + .insert(focus_handle.id, *parent_focus_id); } - 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(); - })); + window.focus_stack.push(focus_handle.id); + + if window.push_key_listeners { + for (type_id, listener) in key_listeners { + let handle = handle.clone(); + let listener = listener.clone(); + window + .key_listeners + .entry(*type_id) + .or_default() + .push(Arc::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); - } - } + window.push_key_listeners = false; } - self.window.focus_stack = focus_stack; let result = f(self); + + if self.window.push_key_listeners { + for (type_id, _) in key_listeners { + self.window.key_listeners.get_mut(type_id).unwrap().pop(); + } + } self.window.focus_stack.pop(); result } From 3d8e9a593eb692d798d22de8b83a648c7622cb1f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 19 Oct 2023 15:44:02 +0200 Subject: [PATCH 2/6] Checkpoint --- crates/gpui3/src/elements/div.rs | 8 +++++--- crates/gpui3/src/window.rs | 34 +++++++++++++++++--------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 938325ac89406786594638750c06564d8dcec4f6..13af47d088365f57cda7bdf8d75a561d34835310 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -423,12 +423,14 @@ where element_state: Option, cx: &mut ViewContext, ) -> Self::ElementState { + for listener in self.listeners.focus.iter().cloned() { + cx.on_focus_changed(move |view, event, cx| listener(view, event, cx)); + } + let key_listeners = mem::take(&mut self.listeners.key); - let focus_listeners = mem::take(&mut self.listeners.focus); cx.with_focus( self.focusability.focus_handle().cloned(), &key_listeners, - &focus_listeners, |cx| { for child in &mut self.children { child.initialize(view_state, cx); @@ -436,7 +438,7 @@ where }, ); self.listeners.key = key_listeners; - self.listeners.focus = focus_listeners; + element_state.unwrap_or_default() } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index ca3acc41ecf5c627f844037c07696be2911d2181..cd90c1dab064d5a2f079e00952584d6567b242b8 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,12 +1,12 @@ use crate::{ px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId, - EventEmitter, FocusEvent, FocusListener, FontId, GlobalElementId, GlyphId, Handle, Hsla, - ImageData, InputEvent, IsZero, KeyListener, LayoutId, MainThread, MainThreadOnly, - MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, PlatformAtlas, PlatformWindow, Point, - PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, - ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, - Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, + InputEvent, IsZero, KeyListener, 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; @@ -1207,11 +1207,22 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { }); } + pub fn on_focus_changed( + &mut self, + listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext) + Send + Sync + 'static, + ) { + let handle = self.handle(); + self.window.focus_listeners.push(Arc::new(move |event, cx| { + handle + .update(cx, |view, cx| listener(view, event, cx)) + .log_err(); + })); + } + pub fn with_focus( &mut self, focus_handle: Option, key_listeners: &[(TypeId, KeyListener)], - focus_listeners: &[FocusListener], f: impl FnOnce(&mut Self) -> R, ) -> R { let Some(focus_handle) = focus_handle else { @@ -1221,15 +1232,6 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let handle = self.handle(); let window = &mut *self.window; - for listener in focus_listeners.iter().cloned() { - let handle = handle.clone(); - window.focus_listeners.push(Arc::new(move |event, cx| { - handle - .update(cx, |view, cx| listener(view, event, cx)) - .log_err(); - })); - } - if let Some(parent_focus_id) = window.focus_stack.last() { window .focus_parents_by_child From 98c0e00a2c49c492f40983ef1cc5fa91ede2181d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 19 Oct 2023 15:52:17 +0200 Subject: [PATCH 3/6] Checkpoint --- crates/gpui3/src/elements/div.rs | 16 +++++---- crates/gpui3/src/window.rs | 56 +++++++++++++++++--------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 13af47d088365f57cda7bdf8d75a561d34835310..76561f742b8f3a3ce12318fee4cea366c6d7d5e6 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -428,15 +428,19 @@ where } let key_listeners = mem::take(&mut self.listeners.key); - cx.with_focus( - self.focusability.focus_handle().cloned(), - &key_listeners, - |cx| { + cx.with_key_listeners(&key_listeners, |cx| { + if let Some(focus_handle) = self.focusability.focus_handle().cloned() { + cx.with_focus(focus_handle, |cx| { + for child in &mut self.children { + child.initialize(view_state, cx); + } + }) + } else { for child in &mut self.children { child.initialize(view_state, cx); } - }, - ); + } + }); self.listeners.key = key_listeners; element_state.unwrap_or_default() diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index cd90c1dab064d5a2f079e00952584d6567b242b8..d16539a369ab3ea748c83fa7ea3b1ad8fcd0ab4d 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -147,7 +147,7 @@ pub struct Window { content_mask_stack: Vec>, mouse_listeners: HashMap>, key_listeners: HashMap>, - push_key_listeners: bool, + key_events_enabled: bool, focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, @@ -220,7 +220,7 @@ impl Window { content_mask_stack: Vec::new(), mouse_listeners: HashMap::default(), key_listeners: HashMap::default(), - push_key_listeners: true, + key_events_enabled: true, focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), focus_listeners: Vec::new(), @@ -823,7 +823,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { window.focus_listeners.clear(); window.key_listeners.values_mut().for_each(Vec::clear); window.focus_parents_by_child.clear(); - window.push_key_listeners = true; + window.key_events_enabled = true; } fn end_frame(&mut self) { @@ -1219,31 +1219,17 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { })); } - pub fn with_focus( + pub fn with_key_listeners( &mut self, - focus_handle: Option, key_listeners: &[(TypeId, KeyListener)], 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; - - if let Some(parent_focus_id) = window.focus_stack.last() { - window - .focus_parents_by_child - .insert(focus_handle.id, *parent_focus_id); - } - window.focus_stack.push(focus_handle.id); - - if window.push_key_listeners { + if self.window.key_events_enabled { + let handle = self.handle(); for (type_id, listener) in key_listeners { let handle = handle.clone(); let listener = listener.clone(); - window + self.window .key_listeners .entry(*type_id) .or_default() @@ -1255,17 +1241,35 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { } } - if Some(focus_handle.id) == window.focus { - window.push_key_listeners = false; - } - let result = f(self); - if self.window.push_key_listeners { + if self.window.key_events_enabled { for (type_id, _) in key_listeners { self.window.key_listeners.get_mut(type_id).unwrap().pop(); } } + + result + } + + pub fn with_focus( + &mut self, + focus_handle: FocusHandle, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + if let Some(parent_focus_id) = self.window.focus_stack.last().copied() { + self.window + .focus_parents_by_child + .insert(focus_handle.id, parent_focus_id); + } + self.window.focus_stack.push(focus_handle.id); + + if Some(focus_handle.id) == self.window.focus { + self.window.key_events_enabled = false; + } + + let result = f(self); + self.window.focus_stack.pop(); result } From b16d37953dd2e89853ca2b15a700170fc69cb26b Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 19 Oct 2023 12:06:05 -0400 Subject: [PATCH 4/6] Use `line_height` in `z_index` stories --- crates/storybook2/src/stories/z_index.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/storybook2/src/stories/z_index.rs b/crates/storybook2/src/stories/z_index.rs index deb6560eac5cac209f19567ae1218233882200c5..d729b75c2a54fdb58bb2f22dd8f0444fdd195056 100644 --- a/crates/storybook2/src/stories/z_index.rs +++ b/crates/storybook2/src/stories/z_index.rs @@ -72,8 +72,7 @@ trait Styles: Styled + Sized { self.bg(rgb::(0xe5e8fc)) .border_5() .border_color(rgb::(0x112382)) - // HACK: Simulate `line-height: 55px`. - .pt(px(16.)) + .line_height(px(55.)) // HACK: Simulate `text-align: center`. .pl(px(24.)) } @@ -119,8 +118,7 @@ impl ZIndexExample { .text_color(rgb::(0x000000)) .border_5() .border_color(rgb::(0xe3e0a1)) - // HACK: Simulate `line-height: 215px`. - .pt(px(100.)) + .line_height(px(215.)) // HACK: Simulate `text-align: center`. .pl(px(24.)) .z_index(self.z_index) From 61e09ff532187ef90d30841a057a94104daaddb9 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 19 Oct 2023 12:58:17 -0400 Subject: [PATCH 5/6] Checkpoint: Thread `WindowContext` through to `user_settings` --- crates/storybook2/src/storybook2.rs | 29 +++++++++++++------- crates/ui2/src/components/assistant_panel.rs | 2 +- crates/ui2/src/components/chat_panel.rs | 13 ++++----- crates/ui2/src/components/icon_button.rs | 4 +-- crates/ui2/src/components/list.rs | 4 +-- crates/ui2/src/components/panel.rs | 10 +++---- crates/ui2/src/components/project_panel.rs | 2 +- crates/ui2/src/components/title_bar.rs | 4 +-- crates/ui2/src/components/workspace.rs | 25 +++++++---------- crates/ui2/src/elements/button.rs | 4 +-- crates/ui2/src/elements/icon.rs | 4 +-- crates/ui2/src/elements/label.rs | 2 +- crates/ui2/src/lib.rs | 8 ++++++ crates/ui2/src/prelude.rs | 6 ++-- crates/ui2/src/settings.rs | 18 ++++++------ 15 files changed, 71 insertions(+), 64 deletions(-) diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 490006ecb9ad2182f30f1ee7d734da6fe3ca0de7..32bc45ae5844fa5d259087bafa1f10789ce3cd16 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -17,7 +17,7 @@ use log::LevelFilter; use simplelog::SimpleLogger; use story_selector::ComponentStory; use ui::prelude::*; -use ui::themed; +use ui::{themed, FakeSettings}; use crate::assets::Assets; use crate::story_selector::StorySelector; @@ -68,8 +68,10 @@ fn main() { move |cx| { view( cx.entity(|cx| { - cx.with_global(theme.clone(), |cx| { - StoryWrapper::new(selector.story(cx), theme) + cx.with_global(FakeSettings::default(), |cx| { + cx.with_global(theme.clone(), |cx| { + StoryWrapper::new(selector.story(cx), theme) + }) }) }), StoryWrapper::render, @@ -85,20 +87,27 @@ fn main() { pub struct StoryWrapper { story: AnyView, theme: Theme, + settings: FakeSettings, } impl StoryWrapper { pub(crate) fn new(story: AnyView, theme: Theme) -> Self { - Self { story, theme } + Self { + story, + theme, + settings: FakeSettings::default(), + } } fn render(&mut self, cx: &mut ViewContext) -> impl Element { - themed(self.theme.clone(), cx, |cx| { - div() - .flex() - .flex_col() - .size_full() - .child(self.story.clone()) + cx.with_global(self.settings.clone(), |cx| { + themed(self.theme.clone(), cx, |cx| { + div() + .flex() + .flex_col() + .size_full() + .child(self.story.clone()) + }) }) } } diff --git a/crates/ui2/src/components/assistant_panel.rs b/crates/ui2/src/components/assistant_panel.rs index b64f7befd30ff7f4ee45442234f3c6dc744a9433..e6b099f338753e870c8b25133e259dfaadaaf7ea 100644 --- a/crates/ui2/src/components/assistant_panel.rs +++ b/crates/ui2/src/components/assistant_panel.rs @@ -29,7 +29,7 @@ impl AssistantPanel { fn render(&mut self, view: &mut S, cx: &mut ViewContext) -> impl Element { let theme = theme(cx); - Panel::new(self.scroll_state.clone()) + Panel::new(cx) .children(vec![div() .flex() .flex_col() diff --git a/crates/ui2/src/components/chat_panel.rs b/crates/ui2/src/components/chat_panel.rs index 45a62e207a264ce1eff21ae59bb1f311135c96cd..81a039a6ad7afc1fca37baffe9d3b999593f66ba 100644 --- a/crates/ui2/src/components/chat_panel.rs +++ b/crates/ui2/src/components/chat_panel.rs @@ -138,13 +138,10 @@ mod stories { Story::container(cx) .child(Story::title_for::<_, ChatPanel>(cx)) .child(Story::label(cx, "Default")) - .child( - Panel::new(ScrollState::default()) - .child(ChatPanel::new(ScrollState::default())), - ) + .child(Panel::new(cx).child(ChatPanel::new(ScrollState::default()))) .child(Story::label(cx, "With Mesages")) - .child(Panel::new(ScrollState::default()).child( - ChatPanel::new(ScrollState::default()).messages(vec![ + .child( + Panel::new(cx).child(ChatPanel::new(ScrollState::default()).messages(vec![ ChatMessage::new( "osiewicz".to_string(), "is this thing on?".to_string(), @@ -159,8 +156,8 @@ mod stories { .unwrap() .naive_local(), ), - ]), - )) + ])), + ) } } } diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index 3f41b4f5b953e89f54db58fa97aefcfcfbc3c4f1..ebe7d29e58a0559c8ea1f24c4ad2bdb720758f5d 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -78,8 +78,8 @@ impl IconButton { let mut button = h_stack() .justify_center() .rounded_md() - .py(ui_size(0.25)) - .px(ui_size(6. / 14.)) + .py(ui_size(cx, 0.25)) + .px(ui_size(cx, 6. / 14.)) .when(self.variant == ButtonVariant::Filled, |this| { this.bg(color.filled_element) }) diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index 1a60581855aae9f5052c703d3b1d94c82d1bddb1..ce7977e7ea29ce6a2054605e2efd284258a37091 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -363,7 +363,7 @@ impl ListEntry { let theme = theme(cx); let system_color = SystemColor::new(); let color = ThemeColor::new(cx); - let setting = user_settings(); + let settings = user_settings(cx); let left_content = match self.left_content.clone() { Some(LeftContent::Icon(i)) => Some( @@ -395,7 +395,7 @@ impl ListEntry { // .ml(rems(0.75 * self.indent_level as f32)) .children((0..self.indent_level).map(|_| { div() - .w(*setting.list_indent_depth) + .w(*settings.list_indent_depth) .h_full() .flex() .justify_center() diff --git a/crates/ui2/src/components/panel.rs b/crates/ui2/src/components/panel.rs index 52e36b2f14e21c4c2f61683d8d8b0ce10efeadc3..53851e1433102de8166058bc01f7793dfbf7c5cf 100644 --- a/crates/ui2/src/components/panel.rs +++ b/crates/ui2/src/components/panel.rs @@ -53,15 +53,15 @@ pub struct Panel { } impl Panel { - pub fn new(scroll_state: ScrollState) -> Self { - let setting = user_settings(); + pub fn new(cx: &mut WindowContext) -> Self { + let settings = user_settings(cx); Self { state_type: PhantomData, - scroll_state, + scroll_state: ScrollState::default(), current_side: PanelSide::default(), allowed_sides: PanelAllowedSides::default(), - initial_width: *setting.default_panel_size, + initial_width: *settings.default_panel_size, width: None, children: SmallVec::new(), } @@ -175,7 +175,7 @@ mod stories { .child(Story::title_for::<_, Panel>(cx)) .child(Story::label(cx, "Default")) .child( - Panel::new(ScrollState::default()).child( + Panel::new(cx).child( div() .overflow_y_scroll(ScrollState::default()) .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))), diff --git a/crates/ui2/src/components/project_panel.rs b/crates/ui2/src/components/project_panel.rs index 4c4fbe9f293602bd4dfe8435837203ccee50df22..480ae7f2c0de9cef18711ba774cde791b6911bec 100644 --- a/crates/ui2/src/components/project_panel.rs +++ b/crates/ui2/src/components/project_panel.rs @@ -87,7 +87,7 @@ mod stories { .child(Story::title_for::<_, ProjectPanel>(cx)) .child(Story::label(cx, "Default")) .child( - Panel::new(ScrollState::default()) + Panel::new(cx) .child(ProjectPanel::new(ScrollState::default())), ) } diff --git a/crates/ui2/src/components/title_bar.rs b/crates/ui2/src/components/title_bar.rs index 68033629ba0f9dbe69a4aeda0c9e42cddbde2330..4472c8cc6e7e0eec34b94316e42bb23d4859f82c 100644 --- a/crates/ui2/src/components/title_bar.rs +++ b/crates/ui2/src/components/title_bar.rs @@ -95,7 +95,7 @@ impl TitleBar { fn render(&mut self, cx: &mut ViewContext) -> impl Element { let theme = theme(cx); let color = ThemeColor::new(cx); - let setting = user_settings(); + let settings = user_settings(cx); // let has_focus = cx.window_is_active(); let has_focus = true; @@ -127,7 +127,7 @@ impl TitleBar { .flex() .items_center() .gap_1() - .when(*setting.titlebar.show_project_owner, |this| { + .when(*settings.titlebar.show_project_owner, |this| { this.child(Button::new("iamnbutler")) }) .child(Button::new("zed")) diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index b198861e9538f9cc46f8072df1325779ead434df..96cb4090463aaf5708e7e57f80137c68973eb3c7 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -165,7 +165,7 @@ impl Workspace { .border_color(theme.lowest.base.default.border) .children( Some( - Panel::new(self.left_panel_scroll_state.clone()) + Panel::new(cx) .side(PanelSide::Left) .child(ProjectPanel::new(ScrollState::default())), ) @@ -173,7 +173,7 @@ impl Workspace { ) .children( Some( - Panel::new(self.left_panel_scroll_state.clone()) + Panel::new(cx) .child(CollabPanel::new(ScrollState::default())) .side(PanelSide::Left), ) @@ -196,7 +196,7 @@ impl Workspace { ) .children( Some( - Panel::new(self.bottom_panel_scroll_state.clone()) + Panel::new(cx) .child(Terminal::new()) .allowed_sides(PanelAllowedSides::BottomOnly) .side(PanelSide::Bottom), @@ -205,10 +205,8 @@ impl Workspace { ), ) .children( - Some( - Panel::new(self.right_panel_scroll_state.clone()) - .side(PanelSide::Right) - .child(ChatPanel::new(ScrollState::default()).messages(vec![ + Some(Panel::new(cx).side(PanelSide::Right).child( + ChatPanel::new(ScrollState::default()).messages(vec![ ChatMessage::new( "osiewicz".to_string(), "is this thing on?".to_string(), @@ -223,24 +221,21 @@ impl Workspace { .unwrap() .naive_local(), ), - ])), - ) + ]), + )) .filter(|_| self.is_chat_panel_open()), ) .children( Some( - Panel::new(self.right_panel_scroll_state.clone()) + Panel::new(cx) .side(PanelSide::Right) .child(div().w_96().h_full().child("Notifications")), ) .filter(|_| self.is_notifications_panel_open()), ) .children( - Some( - Panel::new(self.right_panel_scroll_state.clone()) - .child(AssistantPanel::new()), - ) - .filter(|_| self.is_assistant_panel_open()), + Some(Panel::new(cx).child(AssistantPanel::new())) + .filter(|_| self.is_assistant_panel_open()), ), ) .child(StatusBar::new()) diff --git a/crates/ui2/src/elements/button.rs b/crates/ui2/src/elements/button.rs index 3491352116a6aff076f111e67f8391aaaa78e44e..7d7040e18154ad7f185140430bc8ab63dfead727 100644 --- a/crates/ui2/src/elements/button.rs +++ b/crates/ui2/src/elements/button.rs @@ -149,11 +149,11 @@ impl Button { fn render(&mut self, _view: &mut S, cx: &mut ViewContext) -> impl Element { let icon_color = self.icon_color(); let border_color = self.border_color(cx); - let setting = user_settings(); + let settings = user_settings(cx); let mut el = h_stack() .p_1() - .text_size(ui_size(1.)) + .text_size(ui_size(cx, 1.)) .rounded_md() .border() .border_color(border_color) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index 00c22432d47110dce99dac7df785b6638125c6e5..000863a399762f7e19608bc2b26d358382b928a8 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -180,8 +180,8 @@ impl IconElement { let theme = theme(cx); let fill = self.color.color(theme); let svg_size = match self.size { - IconSize::Small => ui_size(12. / 14.), - IconSize::Medium => ui_size(15. / 14.), + IconSize::Small => ui_size(cx, 12. / 14.), + IconSize::Medium => ui_size(cx, 15. / 14.), }; svg() diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index 37cabc553490e3e70def4a014999bed779020e70..95c7e86a102a58d6da1ed07c5315a28199d7bcaf 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -96,7 +96,7 @@ impl Label { .bg(LabelColor::Hidden.hsla(cx)), ) }) - .text_size(ui_size(1.)) + .text_size(ui_size(cx, 1.)) .when(self.line_height_style == LineHeightStyle::UILabel, |this| { this.line_height(relative(1.)) }) diff --git a/crates/ui2/src/lib.rs b/crates/ui2/src/lib.rs index 417a77fabe59b1e89794765b06ce9531c679ad10..10419ed362b7732e568e7f8dbe4cc18d7d36c9e8 100644 --- a/crates/ui2/src/lib.rs +++ b/crates/ui2/src/lib.rs @@ -14,6 +14,14 @@ pub use elements::*; pub use prelude::*; pub use static_data::*; +// This needs to be fully qualified with `crate::` otherwise we get a panic +// at: +// thread '' panicked at crates/gpui3/src/platform/mac/platform.rs:66:81: +// called `Option::unwrap()` on a `None` value +// +// AFAICT this is something to do with conflicting names between crates and modules that +// interfaces with declaring the `ClassDecl`. +pub use crate::settings::*; pub use crate::theme::*; #[cfg(feature = "stories")] diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index a132542e299b4e088449e215f262df0e42f93ac9..ef7e0fc5ab60e8b33859b52d468f5388ce4fd506 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -142,12 +142,12 @@ impl HighlightColor { } } -pub fn ui_size(size: f32) -> Rems { +pub fn ui_size(cx: &mut WindowContext, size: f32) -> Rems { const UI_SCALE_RATIO: f32 = 0.875; - let setting = user_settings(); + let settings = user_settings(cx); - rems(*setting.ui_scale * UI_SCALE_RATIO * size) + rems(*settings.ui_scale * UI_SCALE_RATIO * size) } #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] diff --git a/crates/ui2/src/settings.rs b/crates/ui2/src/settings.rs index e91ff4a961cfdf1e5e04cfb01ffcd3e057849540..fd7693fd08209ab92199959c99422aea47122c92 100644 --- a/crates/ui2/src/settings.rs +++ b/crates/ui2/src/settings.rs @@ -1,15 +1,13 @@ use std::ops::Deref; -use gpui3::{rems, AbsoluteLength}; +use gpui3::{rems, AbsoluteLength, WindowContext}; use crate::DisclosureControlStyle; -// This is a fake static example of user settings overriding the default settings -pub fn user_settings() -> Settings { - let mut settings = Settings::default(); - settings.list_indent_depth = SettingValue::UserDefined(rems(0.5).into()); - // settings.ui_scale = SettingValue::UserDefined(2.); - settings +/// Returns the user settings. +pub fn user_settings(cx: &WindowContext) -> FakeSettings { + // cx.global::().clone() + FakeSettings::default() } #[derive(Clone)] @@ -48,7 +46,7 @@ impl Default for TitlebarSettings { // These should be merged into settings #[derive(Clone)] -pub struct Settings { +pub struct FakeSettings { pub default_panel_size: SettingValue, pub list_disclosure_style: SettingValue, pub list_indent_depth: SettingValue, @@ -56,7 +54,7 @@ pub struct Settings { pub ui_scale: SettingValue, } -impl Default for Settings { +impl Default for FakeSettings { fn default() -> Self { Self { titlebar: TitlebarSettings::default(), @@ -68,4 +66,4 @@ impl Default for Settings { } } -impl Settings {} +impl FakeSettings {} From a1f7a97ff5c3c53f8eafb52830bea1c38383523e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 19 Oct 2023 13:02:32 -0400 Subject: [PATCH 6/6] Pull the settings from the global state --- crates/storybook2/src/storybook2.rs | 4 +- crates/ui2/src/settings.rs | 84 +++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 32bc45ae5844fa5d259087bafa1f10789ce3cd16..4beca32ca18377c948d44ac8d84b65801b237a88 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -17,7 +17,7 @@ use log::LevelFilter; use simplelog::SimpleLogger; use story_selector::ComponentStory; use ui::prelude::*; -use ui::{themed, FakeSettings}; +use ui::{themed, with_settings, FakeSettings}; use crate::assets::Assets; use crate::story_selector::StorySelector; @@ -100,7 +100,7 @@ impl StoryWrapper { } fn render(&mut self, cx: &mut ViewContext) -> impl Element { - cx.with_global(self.settings.clone(), |cx| { + with_settings(self.settings.clone(), cx, |cx| { themed(self.theme.clone(), cx, |cx| { div() .flex() diff --git a/crates/ui2/src/settings.rs b/crates/ui2/src/settings.rs index fd7693fd08209ab92199959c99422aea47122c92..05df5b49566c9c4d9f97b6d0bbd70e4c483df904 100644 --- a/crates/ui2/src/settings.rs +++ b/crates/ui2/src/settings.rs @@ -1,13 +1,14 @@ use std::ops::Deref; -use gpui3::{rems, AbsoluteLength, WindowContext}; +use gpui3::{ + rems, AbsoluteLength, AnyElement, BorrowAppContext, Bounds, LayoutId, Pixels, WindowContext, +}; -use crate::DisclosureControlStyle; +use crate::prelude::*; /// Returns the user settings. pub fn user_settings(cx: &WindowContext) -> FakeSettings { - // cx.global::().clone() - FakeSettings::default() + cx.global::().clone() } #[derive(Clone)] @@ -67,3 +68,78 @@ impl Default for FakeSettings { } impl FakeSettings {} + +pub fn with_settings( + settings: FakeSettings, + cx: &mut ViewContext, + build_child: F, +) -> WithSettings +where + E: Element, + F: FnOnce(&mut ViewContext) -> E, +{ + let child = cx.with_global(theme.clone(), |cx| build_child(cx)); + WithSettings { settings, child } +} + +pub struct WithSettings { + pub(crate) settings: FakeSettings, + pub(crate) child: E, +} + +impl IntoAnyElement for WithSettings +where + E: Element, +{ + fn into_any(self) -> AnyElement { + AnyElement::new(self) + } +} + +impl Element for WithSettings { + type ViewState = E::ViewState; + type ElementState = E::ElementState; + + fn id(&self) -> Option { + None + } + + fn initialize( + &mut self, + view_state: &mut Self::ViewState, + element_state: Option, + cx: &mut ViewContext, + ) -> Self::ElementState { + cx.with_global(self.settings.clone(), |cx| { + self.child.initialize(view_state, element_state, cx) + }) + } + + fn layout( + &mut self, + view_state: &mut E::ViewState, + element_state: &mut Self::ElementState, + cx: &mut ViewContext, + ) -> LayoutId + where + Self: Sized, + { + cx.with_global(self.settings.clone(), |cx| { + self.child.layout(view_state, element_state, cx) + }) + } + + fn paint( + &mut self, + bounds: Bounds, + view_state: &mut Self::ViewState, + frame_state: &mut Self::ElementState, + cx: &mut ViewContext, + ) where + Self: Sized, + { + cx.with_global(self.settings.clone(), |cx| { + self.child.paint(bounds, view_state, frame_state, cx); + }); + } +}