diff --git a/crates/gpui/playground/src/div.rs b/crates/gpui/playground/src/div.rs index 8a6be0e532..449583f29e 100644 --- a/crates/gpui/playground/src/div.rs +++ b/crates/gpui/playground/src/div.rs @@ -1,57 +1,180 @@ -use crate::style::StyleRefinement; +use std::{marker::PhantomData, rc::Rc}; + +use crate::element::{AnyElement, PaintContext}; +use crate::layout_context::LayoutContext; +use crate::style::{Style, StyleRefinement}; +use anyhow::Result; +use derive_more::{Deref, DerefMut}; +use gpui::EngineLayout; +use gpui::{geometry::rect::RectF, platform::MouseMovedEvent, EventContext}; use playground_macros::styleable_trait; use refineable::Refineable; +use smallvec::SmallVec; +use util::ResultExt; -trait Element { - type Style; +type LayoutId = gpui::LayoutId; - fn hover(self) -> Hover - where - Self: Sized, - Self::Style: Refineable, - ::Refinement: Default, - { - Hover { - child: self, - style: <>::Style as Refineable>::Refinement::default(), +#[derive(Deref, DerefMut)] +pub struct Layout> { + id: LayoutId, + engine_layout: Option, + #[deref] + #[deref_mut] + element_data: E::Layout, +} + +impl> Layout { + pub fn new(id: LayoutId, engine_layout: Option, element_data: E::Layout) -> Self { + Self { + id, + engine_layout, + element_data, } } + + pub fn bounds(&mut self, cx: &mut PaintContext) -> RectF { + self.engine_layout(cx).bounds + } + + 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()) + } +} + +pub trait Element { + type Layout; + + fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result> + where + Self: Sized; + + fn paint(&mut self, view: &mut V, cx: &mut PaintContext) + where + Self: Sized; + + /// ## Helpers + + fn hoverable(self) -> Hoverable + where + Self: Styleable + Sized, + { + hoverable(self) + } } use crate as playground; styleable_trait!(); -struct Hover> -where - E::Style: Refineable, -{ - child: E, - style: ::Refinement, -} - -struct Div { +pub struct Div { style: StyleRefinement, + children: SmallVec<[AnyElement; 2]>, } -impl Styleable for Div { +impl Styleable for Div { + type Style = Style; + fn declared_style(&mut self) -> &mut StyleRefinement { &mut self.style } } -fn div() -> Div { +pub fn div() -> Div { Div { style: Default::default(), + children: Default::default(), } } -impl Element for Div { - type Style = StyleRefinement; +impl Element for Div { + type Layout = (); + + fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result> + where + Self: Sized, + { + let children = self + .children + .iter_mut() + .map(|child| child.layout(view, cx)) + .collect::>>()?; + + cx.add_layout_node(self.style(), (), children) + } + + fn paint(&mut self, view: &mut V, cx: &mut PaintContext) + where + Self: Sized, + { + todo!() + } +} + +pub struct Hoverable + Styleable> { + default_style: Style, + hovered_style: StyleRefinement, + child: E, + view_type: PhantomData, +} + +pub trait Interactive { + fn declared_interactions(&mut self) -> &mut Interactions; + + fn on_mouse_move(mut self, handler: H) -> Self + where + H: 'static + Fn(&mut V, &MouseMovedEvent, &mut EventContext), + Self: Sized, + { + self.declared_interactions().mouse_moved = Some(Rc::new(move |view, event, cx| { + handler(view, event, cx); + cx.bubble + })); + self + } +} + +pub struct Interactions { + mouse_moved: Option) -> bool>>, +} + +pub fn hoverable + Styleable>(mut child: E) -> Hoverable { + Hoverable { + default_style: child.style(), + hovered_style: Default::default(), + child, + view_type: PhantomData, + } +} + +impl + Styleable> Styleable for Hoverable { + type Style = E::Style; + + fn declared_style(&mut self) -> &mut playground::style::StyleRefinement { + self.child.declared_style() + } +} + +impl + Styleable> Element for Hoverable { + type Layout = E::Layout; + + fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result> + where + Self: Sized, + { + todo!() + } + + fn paint(&mut self, view: &mut V, cx: &mut PaintContext) + where + Self: Sized, + { + todo!() + } } #[test] fn test() { - let elt = div().w_auto(); + // let elt = div().w_auto(); } // trait Element { diff --git a/crates/gpui/playground/src/frame.rs b/crates/gpui/playground/src/frame.rs index c7ab6acabb..a4eb7cceb7 100644 --- a/crates/gpui/playground/src/frame.rs +++ b/crates/gpui/playground/src/frame.rs @@ -6,7 +6,7 @@ use crate::{ style::{Style, StyleRefinement}, }; use anyhow::{anyhow, Result}; -use gpui::LayoutNodeId; +use gpui::LayoutId; use playground_macros::IntoElement; use refineable::Refineable; @@ -46,7 +46,7 @@ impl Element for Frame { .children .iter_mut() .map(|child| child.layout(view, cx)) - .collect::>>()?; + .collect::>>()?; let rem_size = cx.rem_pixels(); let style = Style::default().refined(&self.style); diff --git a/crates/gpui/playground/src/layout_context.rs b/crates/gpui/playground/src/layout_context.rs new file mode 100644 index 0000000000..a424e561b3 --- /dev/null +++ b/crates/gpui/playground/src/layout_context.rs @@ -0,0 +1,61 @@ +use anyhow::{anyhow, Result}; +use derive_more::{Deref, DerefMut}; +pub use gpui::LayoutContext as LegacyLayoutContext; +use gpui::{RenderContext, ViewContext}; +pub use taffy::tree::NodeId; + +use crate::{ + div::{Element, Layout}, + style::Style, +}; + +#[derive(Deref, DerefMut)] +pub struct LayoutContext<'a, 'b, 'c, 'd, V> { + #[deref] + #[deref_mut] + pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>, + pub(crate) scene: &'d mut gpui::SceneBuilder, +} + +impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> { + fn text_style(&self) -> gpui::fonts::TextStyle { + self.legacy_cx.text_style() + } + + fn push_text_style(&mut self, style: gpui::fonts::TextStyle) { + self.legacy_cx.push_text_style(style) + } + + fn pop_text_style(&mut self) { + self.legacy_cx.pop_text_style() + } + + fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> { + &mut self.view_context + } +} + +impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> { + pub fn new( + legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>, + scene: &'d mut gpui::SceneBuilder, + ) -> Self { + Self { legacy_cx, scene } + } + + pub fn add_layout_node>( + &mut self, + style: Style, + element_data: E::Layout, + children: impl IntoIterator, + ) -> Result> { + let rem_size = self.rem_pixels(); + let id = self + .legacy_cx + .layout_engine() + .ok_or_else(|| anyhow!("no layout engine"))? + .add_node(style.to_taffy(rem_size), children)?; + + Ok(Layout::new(id, None, element_data)) + } +} diff --git a/crates/gpui/playground/src/paint_context.rs b/crates/gpui/playground/src/paint_context.rs index 56cfdaf689..b849c3f131 100644 --- a/crates/gpui/playground/src/paint_context.rs +++ b/crates/gpui/playground/src/paint_context.rs @@ -1,8 +1,10 @@ -use std::{any::TypeId, rc::Rc}; - +use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; -use gpui::{geometry::rect::RectF, EventContext, RenderContext, ViewContext}; +use gpui::{ + geometry::rect::RectF, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext, +}; pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext}; +use std::{any::TypeId, rc::Rc}; pub use taffy::tree::NodeId; #[derive(Deref, DerefMut)] @@ -66,4 +68,10 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> { view_id: self.view_id(), }); } + + pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result { + self.layout_engine() + .ok_or_else(|| anyhow!("no layout engine present"))? + .computed_layout(layout_id) + } } diff --git a/crates/gpui/playground/src/playground.rs b/crates/gpui/playground/src/playground.rs index d4147047bf..4308a13cea 100644 --- a/crates/gpui/playground/src/playground.rs +++ b/crates/gpui/playground/src/playground.rs @@ -20,6 +20,7 @@ mod div; mod element; mod frame; mod hoverable; +mod layout_context; mod paint_context; mod style; mod text; diff --git a/crates/gpui/playground_macros/src/styleable_trait.rs b/crates/gpui/playground_macros/src/styleable_trait.rs index 2a952bc236..e4325f6269 100644 --- a/crates/gpui/playground_macros/src/styleable_trait.rs +++ b/crates/gpui/playground_macros/src/styleable_trait.rs @@ -109,6 +109,8 @@ pub fn styleable_trait(_item: TokenStream) -> TokenStream { let output = quote! { pub trait Styleable { + type Style: refineable::Refineable; + fn declared_style(&mut self) -> &mut playground::style::StyleRefinement; fn style(&mut self) -> playground::style::Style { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 04eb0f0b40..941e09c1d3 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3392,7 +3392,8 @@ pub trait RenderContext<'a, 'b, V> { } pub struct LayoutContext<'a, 'b, 'c, V> { - view_context: &'c mut ViewContext<'a, 'b, V>, + // Nathan: Making this is public while I work on playground. + pub view_context: &'c mut ViewContext<'a, 'b, V>, new_parents: &'c mut HashMap, views_to_notify_if_ancestors_change: &'c mut HashMap>, text_style_stack: Vec, @@ -3643,6 +3644,9 @@ impl BorrowWindowContext for PaintContext<'_, '_, '_, V> { pub struct EventContext<'a, 'b, 'c, V> { view_context: &'c mut ViewContext<'a, 'b, V>, pub(crate) handled: bool, + // I would like to replace handled with this. + // Being additive for now. + pub bubble: bool, } impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> { @@ -3650,12 +3654,21 @@ impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> { EventContext { view_context, handled: true, + bubble: false, } } pub fn propagate_event(&mut self) { self.handled = false; } + + pub fn bubble_event(&mut self) { + self.bubble = true; + } + + pub fn event_bubbled(&self) -> bool { + self.bubble + } } impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> { diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 17ad10b0be..b3b5017e00 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1293,16 +1293,16 @@ impl LayoutEngine { Default::default() } - pub fn add_node(&mut self, style: LayoutStyle, children: C) -> Result + pub fn add_node(&mut self, style: LayoutStyle, children: C) -> Result where - C: IntoIterator, + C: IntoIterator, { Ok(self .0 .new_with_children(style, &children.into_iter().collect::>())?) } - pub fn add_measured_node(&mut self, style: LayoutStyle, measure: F) -> Result + pub fn add_measured_node(&mut self, style: LayoutStyle, measure: F) -> Result where F: Fn(MeasureParams) -> Size + Sync + Send + 'static, { @@ -1311,7 +1311,7 @@ impl LayoutEngine { .new_leaf_with_measure(style, MeasureFunc::Boxed(Box::new(MeasureFn(measure))))?) } - pub fn compute_layout(&mut self, root: LayoutNodeId, available_space: Vector2F) -> Result<()> { + pub fn compute_layout(&mut self, root: LayoutId, available_space: Vector2F) -> Result<()> { self.0.compute_layout( root, taffy::geometry::Size { @@ -1322,7 +1322,7 @@ impl LayoutEngine { Ok(()) } - pub fn computed_layout(&mut self, node: LayoutNodeId) -> Result { + pub fn computed_layout(&mut self, node: LayoutId) -> Result { Ok(self.0.layout(node)?.into()) } } @@ -1346,7 +1346,7 @@ where } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct EngineLayout { pub bounds: RectF, pub order: u32, @@ -1367,6 +1367,12 @@ pub enum AvailableSpace { MaxContent, } +impl Default for AvailableSpace { + fn default() -> Self { + Self::Pixels(0.) + } +} + impl From for AvailableSpace { fn from(value: taffy::prelude::AvailableSpace) -> Self { match value { @@ -1389,7 +1395,7 @@ impl From<&taffy::tree::Layout> for EngineLayout { } } -pub type LayoutNodeId = taffy::prelude::NodeId; +pub type LayoutId = taffy::prelude::NodeId; pub struct RenderParams { pub view_id: usize, diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index bed066bc0d..9ab622e47f 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -134,12 +134,12 @@ impl ToJson for RectF { } #[derive(Refineable)] -pub struct Point { +pub struct Point { pub x: T, pub y: T, } -impl Clone for Point { +impl Clone for Point { fn clone(&self) -> Self { Self { x: self.x.clone(), @@ -148,7 +148,7 @@ impl Clone for Point { } } -impl Into> for Point { +impl Into> for Point { fn into(self) -> taffy::geometry::Point { taffy::geometry::Point { x: self.x, @@ -158,12 +158,12 @@ impl Into> for Point { } #[derive(Clone, Refineable)] -pub struct Size { +pub struct Size { pub width: T, pub height: T, } -impl From> for Size +impl From> for Size where S: Into, { @@ -175,7 +175,7 @@ where } } -impl Into> for Size +impl Into> for Size where T: Into, { @@ -223,7 +223,7 @@ impl Size { } #[derive(Clone, Default, Refineable)] -pub struct Edges { +pub struct Edges { pub top: T, pub right: T, pub bottom: T, diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 33aeb50bbd..4bd1481f5b 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -28,7 +28,7 @@ pub mod keymap_matcher; pub mod platform; pub use gpui_macros::{test, Element}; pub use window::{ - Axis, EngineLayout, LayoutEngine, LayoutNodeId, RectFExt, SizeConstraint, Vector2FExt, + Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, WindowContext, }; diff --git a/crates/refineable/src/refineable.rs b/crates/refineable/src/refineable.rs index c34293cf98..81cb2ad11f 100644 --- a/crates/refineable/src/refineable.rs +++ b/crates/refineable/src/refineable.rs @@ -1,7 +1,7 @@ pub use derive_refineable::Refineable; pub trait Refineable { - type Refinement; + type Refinement: Default; fn refine(&mut self, refinement: &Self::Refinement); fn refined(mut self, refinement: &Self::Refinement) -> Self