Detailed changes
@@ -3,21 +3,22 @@ use crate::{
interactive::{InteractionHandlers, Interactive},
layout_context::LayoutContext,
paint_context::PaintContext,
- style::{Style, StyleHelpers, StyleRefinement, Styleable},
+ style::{Style, StyleHelpers, Styleable},
};
use anyhow::Result;
use gpui::LayoutId;
+use refineable::{Refineable, RefinementCascade};
use smallvec::SmallVec;
pub struct Div<V: 'static> {
- style: StyleRefinement,
+ styles: RefinementCascade<Style>,
handlers: InteractionHandlers<V>,
children: SmallVec<[AnyElement<V>; 2]>,
}
pub fn div<V>() -> Div<V> {
Div {
- style: Default::default(),
+ styles: Default::default(),
handlers: Default::default(),
children: Default::default(),
}
@@ -36,16 +37,16 @@ impl<V: 'static> Element<V> for Div<V> {
.map(|child| child.layout(view, cx))
.collect::<Result<Vec<LayoutId>>>()?;
- cx.add_layout_node(self.style(), (), children)
+ let style = Style::from_refinement(&self.style_cascade().merged());
+ cx.add_layout_node(style.clone(), (), children)
}
fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
where
Self: Sized,
{
- let style = self.style();
-
- style.paint_background::<V, Self>(layout, cx);
+ self.computed_style()
+ .paint_background(layout.bounds(cx), cx);
for child in &mut self.children {
child.paint(view, cx);
}
@@ -55,8 +56,12 @@ impl<V: 'static> Element<V> for Div<V> {
impl<V> Styleable for Div<V> {
type Style = Style;
- fn declared_style(&mut self) -> &mut StyleRefinement {
- &mut self.style
+ fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
+ &mut self.styles
+ }
+
+ fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
+ self.styles.base()
}
}
@@ -1,5 +1,4 @@
-use anyhow::Result;
-use derive_more::{Deref, DerefMut};
+use anyhow::{anyhow, Result};
use gpui::{geometry::rect::RectF, EngineLayout};
use smallvec::SmallVec;
use std::marker::PhantomData;
@@ -83,13 +82,10 @@ impl<V> AnyElement<V> {
}
}
-#[derive(Deref, DerefMut)]
pub struct Layout<V, D> {
id: LayoutId,
engine_layout: Option<EngineLayout>,
- #[deref]
- #[deref_mut]
- element_data: D,
+ element_data: Option<D>,
view_type: PhantomData<V>,
}
@@ -98,7 +94,7 @@ impl<V: 'static, D> Layout<V, D> {
Self {
id,
engine_layout: None,
- element_data: element_data,
+ element_data: Some(element_data),
view_type: PhantomData,
}
}
@@ -111,20 +107,26 @@ impl<V: 'static, D> Layout<V, D> {
self.engine_layout(cx).order
}
+ pub fn update<F, T>(&mut self, update: F) -> Result<T>
+ where
+ F: FnOnce(&mut Self, &mut D) -> T,
+ {
+ self.element_data
+ .take()
+ .map(|mut element_data| {
+ let result = update(self, &mut element_data);
+ self.element_data = Some(element_data);
+ result
+ })
+ .ok_or_else(|| anyhow!("reentrant calls to Layout::update are not allowed"))
+ }
+
fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
self.engine_layout
.get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
}
}
-impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
- pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
- let mut element = self.element_data.take().unwrap();
- element.paint(view, cx);
- self.element_data = Some(element);
- }
-}
-
pub trait ParentElement<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
@@ -2,24 +2,24 @@ use crate::{
element::{Element, Layout},
layout_context::LayoutContext,
paint_context::PaintContext,
- style::{Style, StyleHelpers, StyleRefinement, Styleable},
+ style::{Style, StyleHelpers, Styleable},
};
use anyhow::Result;
use gpui::platform::MouseMovedEvent;
-use refineable::Refineable;
-use std::cell::Cell;
+use refineable::{CascadeSlot, Refineable, RefinementCascade};
+use std::{cell::Cell, rc::Rc};
pub struct Hoverable<E: Styleable> {
- hovered: Cell<bool>,
- child_style: StyleRefinement,
- hovered_style: StyleRefinement,
+ hovered: Rc<Cell<bool>>,
+ cascade_slot: CascadeSlot,
+ hovered_style: <E::Style as Refineable>::Refinement,
child: E,
}
pub fn hoverable<E: Styleable>(mut child: E) -> Hoverable<E> {
Hoverable {
- hovered: Cell::new(false),
- child_style: child.declared_style().clone(),
+ hovered: Rc::new(Cell::new(false)),
+ cascade_slot: child.style_cascade().reserve(),
hovered_style: Default::default(),
child,
}
@@ -28,7 +28,11 @@ pub fn hoverable<E: Styleable>(mut child: E) -> Hoverable<E> {
impl<E: Styleable> Styleable for Hoverable<E> {
type Style = E::Style;
- fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+ fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
+ self.child.style_cascade()
+ }
+
+ fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
&mut self.hovered_style
}
}
@@ -55,13 +59,10 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
let order = layout.order(cx);
self.hovered.set(bounds.contains_point(cx.mouse_position()));
- if self.hovered.get() {
- // If hovered, refine the child's style with this element's style.
- self.child.declared_style().refine(&self.hovered_style);
- } else {
- // Otherwise, set the child's style back to its original style.
- *self.child.declared_style() = self.child_style.clone();
- }
+
+ let slot = self.cascade_slot;
+ let style = self.hovered.get().then_some(self.hovered_style.clone());
+ self.style_cascade().set(slot, style);
let hovered = self.hovered.clone();
cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
@@ -22,6 +22,7 @@ mod hoverable;
mod interactive;
mod layout_context;
mod paint_context;
+mod pressable;
mod style;
mod text;
mod themes;
@@ -54,8 +55,10 @@ fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
.h_full()
.w_1_2()
.fill(theme.success(0.5))
- .hoverable()
+ .hovered()
.fill(theme.error(0.5))
+ .pressed()
+ .fill(theme.warning(0.5))
// .child(button().label("Hello").click(|_, _, _| println!("click!")))
}
@@ -0,0 +1,81 @@
+use crate::{
+ element::{Element, Layout},
+ layout_context::LayoutContext,
+ paint_context::PaintContext,
+ style::{Style, StyleHelpers, Styleable},
+};
+use anyhow::Result;
+use gpui::platform::MouseButtonEvent;
+use refineable::{CascadeSlot, Refineable, RefinementCascade};
+use std::{cell::Cell, rc::Rc};
+
+pub struct Pressable<E: Styleable> {
+ pressed: Rc<Cell<bool>>,
+ pressed_style: <E::Style as Refineable>::Refinement,
+ cascade_slot: CascadeSlot,
+ child: E,
+}
+
+pub fn pressable<E: Styleable>(mut child: E) -> Pressable<E> {
+ Pressable {
+ pressed: Rc::new(Cell::new(false)),
+ pressed_style: Default::default(),
+ cascade_slot: child.style_cascade().reserve(),
+ child,
+ }
+}
+
+impl<E: Styleable> Styleable for Pressable<E> {
+ type Style = E::Style;
+
+ fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
+ &mut self.pressed_style
+ }
+
+ fn style_cascade(&mut self) -> &mut RefinementCascade<E::Style> {
+ self.child.style_cascade()
+ }
+}
+
+impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
+ type Layout = E::Layout;
+
+ fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+ where
+ Self: Sized,
+ {
+ self.child.layout(view, cx)
+ }
+
+ fn paint(
+ &mut self,
+ view: &mut V,
+ layout: &mut Layout<V, Self::Layout>,
+ cx: &mut PaintContext<V>,
+ ) where
+ Self: Sized,
+ {
+ let slot = self.cascade_slot;
+ let style = self.pressed.get().then_some(self.pressed_style.clone());
+ self.style_cascade().set(slot, style);
+
+ let bounds = layout.bounds(cx);
+ let order = layout.order(cx);
+ let pressed = self.pressed.clone();
+ cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+ if event.is_down {
+ if bounds.contains_point(event.position) {
+ pressed.set(true);
+ cx.repaint();
+ }
+ } else if pressed.get() {
+ pressed.set(false);
+ cx.repaint();
+ }
+ });
+
+ self.child.paint(view, layout, cx);
+ }
+}
+
+impl<E: Styleable<Style = Style>> StyleHelpers for Pressable<E> {}
@@ -1,18 +1,18 @@
use crate::{
color::Hsla,
- element::{Element, Layout},
hoverable::{hoverable, Hoverable},
paint_context::PaintContext,
+ pressable::{pressable, Pressable},
};
use gpui::{
fonts::TextStyleRefinement,
geometry::{
- AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point, PointRefinement,
- Size, SizeRefinement,
+ rect::RectF, AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point,
+ PointRefinement, Size, SizeRefinement,
},
};
use playground_macros::styleable_helpers;
-use refineable::Refineable;
+use refineable::{Refineable, RefinementCascade};
pub use taffy::style::{
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
Overflow, Position,
@@ -126,12 +126,7 @@ impl Style {
/// Paints the background of an element styled with this style.
/// Return the bounds in which to paint the content.
- pub fn paint_background<V: 'static, E: Element<V>>(
- &self,
- layout: &mut Layout<V, E::Layout>,
- cx: &mut PaintContext<V>,
- ) {
- let bounds = layout.bounds(cx);
+ pub fn paint_background<V: 'static>(&self, bounds: RectF, cx: &mut PaintContext<V>) {
let rem_size = cx.rem_pixels();
if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
cx.scene.push_quad(gpui::Quad {
@@ -202,7 +197,7 @@ impl OptionalTextStyle {
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub enum Fill {
Color(Hsla),
}
@@ -247,22 +242,28 @@ impl CornerRadii {
}
pub trait Styleable {
- type Style: refineable::Refineable;
+ type Style: Refineable + Default;
- fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
+ fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style>;
+ fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement;
- fn style(&mut self) -> playground::style::Style {
- let mut style = playground::style::Style::default();
- style.refine(self.declared_style());
- style
+ fn computed_style(&mut self) -> Self::Style {
+ Self::Style::from_refinement(&self.style_cascade().merged())
}
- fn hoverable(self) -> Hoverable<Self>
+ fn hovered(self) -> Hoverable<Self>
where
Self: Sized,
{
hoverable(self)
}
+
+ fn pressed(self) -> Pressable<Self>
+ where
+ Self: Sized,
+ {
+ pressable(self)
+ }
}
// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
@@ -270,7 +271,6 @@ pub trait Styleable {
// Example:
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
// fn p_2(mut self) -> Self where Self: Sized;
-use crate as playground; // Macro invocation references this crate as playground.
pub trait StyleHelpers: Styleable<Style = Style> {
styleable_helpers!();
@@ -62,16 +62,16 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
#where_clause
{
- type Layout = Option<playground::element::AnyElement<#view_type_name #lifetimes>>;
+ type Layout = playground::element::AnyElement<#view_type_name #lifetimes>;
fn layout(
&mut self,
view: &mut V,
cx: &mut playground::element::LayoutContext<V>,
) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
- let mut element = self.render(view, cx).into_any();
- let layout_id = element.layout(view, cx)?;
- Ok(playground::element::Layout::new(layout_id, Some(element)))
+ let mut rendered_element = self.render(view, cx).into_any();
+ let layout_id = rendered_element.layout(view, cx)?;
+ Ok(playground::element::Layout::new(layout_id, rendered_element))
}
fn paint(
@@ -80,7 +80,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
layout: &mut playground::element::Layout<V, Self::Layout>,
cx: &mut playground::element::PaintContext<V>,
) {
- layout.paint(view, cx);
+ layout.update(|_, rendered_element| rendered_element.paint(view, cx)).ok();
}
}
@@ -20,6 +20,7 @@ pub fn styleable_helpers(input: TokenStream) -> TokenStream {
let output = quote! {
#(#methods)*
};
+
output.into()
}
@@ -1905,7 +1905,6 @@ impl AppContext {
fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
self.update_window(window, |cx| {
- cx.layout(false).log_err();
if let Some(scene) = cx.paint().log_err() {
cx.window.platform_window.present_scene(scene);
}
@@ -1,7 +1,7 @@
pub use derive_refineable::Refineable;
-pub trait Refineable {
- type Refinement: Default;
+pub trait Refineable: Clone {
+ type Refinement: Refineable<Refinement = Self::Refinement> + Default;
fn refine(&mut self, refinement: &Self::Refinement);
fn refined(mut self, refinement: &Self::Refinement) -> Self
@@ -11,4 +11,46 @@ pub trait Refineable {
self.refine(refinement);
self
}
+ fn from_refinement(refinement: &Self::Refinement) -> Self
+ where
+ Self: Default + Sized,
+ {
+ Self::default().refined(refinement)
+ }
+}
+
+pub struct RefinementCascade<S: Refineable>(Vec<Option<S::Refinement>>);
+
+impl<S: Refineable + Default> Default for RefinementCascade<S> {
+ fn default() -> Self {
+ Self(vec![Some(Default::default())])
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct CascadeSlot(usize);
+
+impl<S: Refineable + Default> RefinementCascade<S> {
+ pub fn reserve(&mut self) -> CascadeSlot {
+ self.0.push(None);
+ return CascadeSlot(self.0.len() - 1);
+ }
+
+ pub fn base(&mut self) -> &mut S::Refinement {
+ self.0[0].as_mut().unwrap()
+ }
+
+ pub fn set(&mut self, slot: CascadeSlot, refinement: Option<S::Refinement>) {
+ self.0[slot.0] = refinement
+ }
+
+ pub fn merged(&self) -> S::Refinement {
+ let mut merged = self.0[0].clone().unwrap();
+ for refinement in self.0.iter().skip(1) {
+ if let Some(refinement) = refinement {
+ merged.refine(refinement);
+ }
+ }
+ merged
+ }
}