Detailed changes
@@ -423,18 +423,27 @@ where
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<Self::ViewState>,
) -> 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 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);
+ 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);
}
- element_state.unwrap_or_default()
- },
- )
+ }
+ });
+ self.listeners.key = key_listeners;
+
+ element_state.unwrap_or_default()
}
fn layout(
@@ -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<FocusHandle>,
}
-pub type MouseDownListener<V> = Box<
+pub type MouseDownListener<V> = Arc<
dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ Send
+ Sync
+ 'static,
>;
-pub type MouseUpListener<V> = Box<
+pub type MouseUpListener<V> = Arc<
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>;
+ Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
-pub type MouseMoveListener<V> = Box<
+pub type MouseMoveListener<V> = Arc<
dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ Send
+ Sync
+ 'static,
>;
-pub type ScrollWheelListener<V> = Box<
+pub type ScrollWheelListener<V> = Arc<
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 KeyListener<V> =
+ Arc<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
pub type FocusListener<V> =
- Box<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+ Arc<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
pub struct EventListeners<V: 'static> {
pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
@@ -265,8 +266,7 @@ pub struct EventListeners<V: 'static> {
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 key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
pub focus: SmallVec<[FocusListener<V>; 2]>,
}
@@ -278,8 +278,7 @@ impl<V> Default for EventListeners<V> {
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(),
}
}
@@ -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::<KeyDownEvent>(),
+ 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::<KeyUpEvent>(),
+ Arc::new(move |view, event, phase, cx| {
+ let event = event.downcast_ref().unwrap();
+ listener(view, event, phase, cx)
+ }),
+ ));
self
}
}
@@ -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
}
}
@@ -1,13 +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, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener,
- LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform,
- PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
- RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
- Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
- WindowOptions, SUBPIXEL_VARIANTS,
+ EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData,
+ InputEvent, IsZero, 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<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>;
+type AnyListener = Arc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
+type AnyFocusListener = Arc<dyn Fn(&FocusEvent, &mut WindowContext) + Send + Sync + 'static>;
slotmap::new_key_type! { pub struct FocusId; }
@@ -153,9 +145,10 @@ pub struct Window {
element_states: HashMap<GlobalElementId, AnyBox>,
z_index_stack: StackingOrder,
content_mask_stack: Vec<ContentMask<Pixels>>,
- mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseEventListener)>>,
- keyboard_listeners: HashMap<TypeId, Vec<AnyKeyboardEventListener>>,
- focus_stack: Vec<FocusStackFrame>,
+ mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyListener)>>,
+ key_listeners: HashMap<TypeId, Vec<AnyListener>>,
+ key_events_enabled: bool,
+ focus_stack: Vec<FocusId>,
focus_parents_by_child: HashMap<FocusId, FocusId>,
pub(crate) focus_listeners: Vec<AnyFocusListener>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
@@ -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(),
+ key_events_enabled: true,
focus_stack: Vec::new(),
focus_parents_by_child: HashMap::default(),
focus_listeners: Vec::new(),
@@ -262,12 +256,6 @@ 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>,
@@ -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::<Event>())
.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.key_events_enabled = 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);
}
}
@@ -1218,78 +1207,69 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
});
}
- pub fn with_focus<R>(
+ pub fn on_focus_changed(
&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);
- };
-
+ listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static,
+ ) {
let handle = self.handle();
- let window = &mut *self.window;
+ self.window.focus_listeners.push(Arc::new(move |event, cx| {
+ handle
+ .update(cx, |view, cx| listener(view, event, cx))
+ .log_err();
+ }));
+ }
- 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();
- }));
+ pub fn with_key_listeners<R>(
+ &mut self,
+ key_listeners: &[(TypeId, KeyListener<V>)],
+ f: impl FnOnce(&mut Self) -> R,
+ ) -> R {
+ 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();
+ self.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();
+ }));
+ }
}
- 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 result = f(self);
+
+ if self.window.key_events_enabled {
+ for (type_id, _) in key_listeners {
+ self.window.key_listeners.get_mut(type_id).unwrap().pop();
+ }
}
- let mut frame = FocusStackFrame {
- handle: focus_handle.clone(),
- key_down_listeners: SmallVec::new(),
- key_up_listeners: SmallVec::new(),
- };
+ result
+ }
- 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();
- }));
+ pub fn with_focus<R>(
+ &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);
}
- focus_stack.push(frame);
+ self.window.focus_stack.push(focus_handle.id);
- 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);
- }
- }
+ if Some(focus_handle.id) == self.window.focus {
+ self.window.key_events_enabled = false;
}
- self.window.focus_stack = focus_stack;
let result = f(self);
+
self.window.focus_stack.pop();
result
}
@@ -72,8 +72,7 @@ trait Styles: Styled + Sized {
self.bg(rgb::<Hsla>(0xe5e8fc))
.border_5()
.border_color(rgb::<Hsla>(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<S: 'static + Send + Sync> ZIndexExample<S> {
.text_color(rgb::<Hsla>(0x000000))
.border_5()
.border_color(rgb::<Hsla>(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)
@@ -17,7 +17,7 @@ use log::LevelFilter;
use simplelog::SimpleLogger;
use story_selector::ComponentStory;
use ui::prelude::*;
-use ui::themed;
+use ui::{themed, with_settings, 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<Self>) -> impl Element<ViewState = Self> {
- themed(self.theme.clone(), cx, |cx| {
- div()
- .flex()
- .flex_col()
- .size_full()
- .child(self.story.clone())
+ with_settings(self.settings.clone(), cx, |cx| {
+ themed(self.theme.clone(), cx, |cx| {
+ div()
+ .flex()
+ .flex_col()
+ .size_full()
+ .child(self.story.clone())
+ })
})
}
}
@@ -29,7 +29,7 @@ impl<S: 'static + Send + Sync + Clone> AssistantPanel<S> {
fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
let theme = theme(cx);
- Panel::new(self.scroll_state.clone())
+ Panel::new(cx)
.children(vec![div()
.flex()
.flex_col()
@@ -138,13 +138,10 @@ mod stories {
Story::container(cx)
.child(Story::title_for::<_, ChatPanel<S>>(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(),
),
- ]),
- ))
+ ])),
+ )
}
}
}
@@ -78,8 +78,8 @@ impl<S: 'static + Send + Sync> IconButton<S> {
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)
})
@@ -363,7 +363,7 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
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<S: 'static + Send + Sync + Clone> ListEntry<S> {
// .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()
@@ -53,15 +53,15 @@ pub struct Panel<S: 'static + Send + Sync> {
}
impl<S: 'static + Send + Sync> Panel<S> {
- 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<S>>(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)))),
@@ -87,7 +87,7 @@ mod stories {
.child(Story::title_for::<_, ProjectPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(
- Panel::new(ScrollState::default())
+ Panel::new(cx)
.child(ProjectPanel::new(ScrollState::default())),
)
}
@@ -95,7 +95,7 @@ impl TitleBar {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
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"))
@@ -224,7 +224,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())),
)
@@ -232,7 +232,7 @@ impl Workspace {
)
.children(
Some(
- Panel::new(self.left_panel_scroll_state.clone())
+ Panel::new(cx)
.child(CollabPanel::new(ScrollState::default()))
.side(PanelSide::Left),
)
@@ -245,7 +245,7 @@ impl Workspace {
.child(div().flex().flex_1().child(root_group))
.children(
Some(
- Panel::new(self.bottom_panel_scroll_state.clone())
+ Panel::new(cx)
.child(Terminal::new())
.allowed_sides(PanelAllowedSides::BottomOnly)
.side(PanelSide::Bottom),
@@ -254,10 +254,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(),
@@ -272,24 +270,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())
@@ -149,11 +149,11 @@ impl<S: 'static + Send + Sync + Clone> Button<S> {
fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
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)
@@ -180,8 +180,8 @@ impl<S: 'static + Send + Sync> IconElement<S> {
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()
@@ -96,7 +96,7 @@ impl<S: 'static + Send + Sync + Clone> Label<S> {
.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.))
})
@@ -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 '<unnamed>' 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")]
@@ -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)]
@@ -1,15 +1,14 @@
use std::ops::Deref;
-use gpui3::{rems, AbsoluteLength};
+use gpui3::{
+ rems, AbsoluteLength, AnyElement, BorrowAppContext, Bounds, LayoutId, Pixels, WindowContext,
+};
-use crate::DisclosureControlStyle;
+use crate::prelude::*;
-// 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::<FakeSettings>().clone()
}
#[derive(Clone)]
@@ -48,7 +47,7 @@ impl Default for TitlebarSettings {
// These should be merged into settings
#[derive(Clone)]
-pub struct Settings {
+pub struct FakeSettings {
pub default_panel_size: SettingValue<AbsoluteLength>,
pub list_disclosure_style: SettingValue<DisclosureControlStyle>,
pub list_indent_depth: SettingValue<AbsoluteLength>,
@@ -56,7 +55,7 @@ pub struct Settings {
pub ui_scale: SettingValue<f32>,
}
-impl Default for Settings {
+impl Default for FakeSettings {
fn default() -> Self {
Self {
titlebar: TitlebarSettings::default(),
@@ -68,4 +67,79 @@ impl Default for Settings {
}
}
-impl Settings {}
+impl FakeSettings {}
+
+pub fn with_settings<E, F>(
+ settings: FakeSettings,
+ cx: &mut ViewContext<E::ViewState>,
+ build_child: F,
+) -> WithSettings<E>
+where
+ E: Element,
+ F: FnOnce(&mut ViewContext<E::ViewState>) -> E,
+{
+ let child = cx.with_global(theme.clone(), |cx| build_child(cx));
+ WithSettings { settings, child }
+}
+
+pub struct WithSettings<E> {
+ pub(crate) settings: FakeSettings,
+ pub(crate) child: E,
+}
+
+impl<E> IntoAnyElement<E::ViewState> for WithSettings<E>
+where
+ E: Element,
+{
+ fn into_any(self) -> AnyElement<E::ViewState> {
+ AnyElement::new(self)
+ }
+}
+
+impl<E: Element> Element for WithSettings<E> {
+ type ViewState = E::ViewState;
+ type ElementState = E::ElementState;
+
+ fn id(&self) -> Option<gpui3::ElementId> {
+ None
+ }
+
+ fn initialize(
+ &mut self,
+ view_state: &mut Self::ViewState,
+ element_state: Option<Self::ElementState>,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> 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<E::ViewState>,
+ ) -> 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<Pixels>,
+ view_state: &mut Self::ViewState,
+ frame_state: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) where
+ Self: Sized,
+ {
+ cx.with_global(self.settings.clone(), |cx| {
+ self.child.paint(bounds, view_state, frame_state, cx);
+ });
+ }
+}