Checkpoint

Nathan Sobo created

Change summary

crates/gpui/playground/src/adapter.rs        | 132 +++++++++++----------
crates/gpui/playground/src/element.rs        |  32 +++--
crates/gpui/playground/src/hoverable.rs      |   2 
crates/gpui/playground/src/layout_context.rs |   8 -
crates/gpui/playground/src/text.rs           | 109 +++++++++---------
crates/gpui/playground/src/view.rs           |   8 +
6 files changed, 152 insertions(+), 139 deletions(-)

Detailed changes

crates/gpui/playground/src/adapter.rs 🔗

@@ -1,70 +1,78 @@
-// use crate::element::{LayoutContext, PaintContext};
-// use gpui::{geometry::rect::RectF, LayoutEngine};
-// use util::ResultExt;
+use crate::{layout_context::LayoutContext, paint_context::PaintContext};
+use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
+use util::ResultExt;
 
-// use crate::element::AnyElement;
+/// Makes a new, playground-style element into a legacy element.
+pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
 
-// pub struct Adapter<V>(pub(crate) AnyElement<V>);
+impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
+    type LayoutState = Option<(LayoutEngine, LayoutId)>;
+    type PaintState = ();
 
-// impl<V: 'static> gpui::Element<V> for Adapter<V> {
-//     type LayoutState = Option<LayoutEngine>;
-//     type PaintState = ();
+    fn layout(
+        &mut self,
+        constraint: gpui::SizeConstraint,
+        view: &mut V,
+        cx: &mut gpui::LayoutContext<V>,
+    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+        cx.push_layout_engine(LayoutEngine::new());
 
-//     fn layout(
-//         &mut self,
-//         constraint: gpui::SizeConstraint,
-//         view: &mut V,
-//         cx: &mut LayoutContext<V>,
-//     ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
-//         cx.push_layout_engine(LayoutEngine::new());
-//         let node = self.0.layout(view, cx).log_err();
+        let size = constraint.max;
+        let mut cx = LayoutContext::new(cx);
+        let layout_id = self.0.layout(view, &mut cx).log_err();
+        if let Some(layout_id) = layout_id {
+            cx.layout_engine()
+                .unwrap()
+                .compute_layout(layout_id, constraint.max)
+                .log_err();
+        }
 
-//         if let Some(node) = node {
-//             let layout_engine = cx.layout_engine().unwrap();
-//             layout_engine.compute_layout(node, constraint.max).log_err();
-//         }
-//         let layout_engine = cx.pop_layout_engine();
-//         debug_assert!(layout_engine.is_some());
-//         (constraint.max, layout_engine)
-//     }
+        let layout_engine = cx.pop_layout_engine();
+        debug_assert!(layout_engine.is_some(),
+            "unexpected layout stack state. is there an unmatched pop_layout_engine in the called code?"
+        );
 
-//     fn paint(
-//         &mut self,
-//         scene: &mut gpui::SceneBuilder,
-//         bounds: RectF,
-//         visible_bounds: RectF,
-//         layout_engine: &mut Option<LayoutEngine>,
-//         view: &mut V,
-//         legacy_cx: &mut gpui::PaintContext<V>,
-//     ) -> Self::PaintState {
-//         legacy_cx.push_layout_engine(layout_engine.take().unwrap());
-//         let mut cx = PaintContext::new(legacy_cx, scene);
-//         self.0.paint(view, &mut cx).log_err();
-//         *layout_engine = legacy_cx.pop_layout_engine();
-//         debug_assert!(layout_engine.is_some());
-//     }
+        (constraint.max, layout_engine.zip(layout_id))
+    }
 
-//     fn rect_for_text_range(
-//         &self,
-//         range_utf16: std::ops::Range<usize>,
-//         bounds: RectF,
-//         visible_bounds: RectF,
-//         layout: &Self::LayoutState,
-//         paint: &Self::PaintState,
-//         view: &V,
-//         cx: &gpui::ViewContext<V>,
-//     ) -> Option<RectF> {
-//         todo!("implement before merging to main")
-//     }
+    fn paint(
+        &mut self,
+        scene: &mut gpui::SceneBuilder,
+        bounds: RectF,
+        visible_bounds: RectF,
+        layout_data: &mut Option<(LayoutEngine, LayoutId)>,
+        view: &mut V,
+        legacy_cx: &mut gpui::PaintContext<V>,
+    ) -> Self::PaintState {
+        let (layout_engine, layout_id) = layout_data.take().unwrap();
+        legacy_cx.push_layout_engine(layout_engine);
+        let mut cx = PaintContext::new(legacy_cx, scene);
+        self.0.paint(view, layout_id, &mut cx);
+        *layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id));
+        debug_assert!(layout_data.is_some());
+    }
 
-//     fn debug(
-//         &self,
-//         bounds: RectF,
-//         layout: &Self::LayoutState,
-//         paint: &Self::PaintState,
-//         view: &V,
-//         cx: &gpui::ViewContext<V>,
-//     ) -> gpui::serde_json::Value {
-//         todo!("implement before merging to main")
-//     }
-// }
+    fn rect_for_text_range(
+        &self,
+        range_utf16: std::ops::Range<usize>,
+        bounds: RectF,
+        visible_bounds: RectF,
+        layout: &Self::LayoutState,
+        paint: &Self::PaintState,
+        view: &V,
+        cx: &gpui::ViewContext<V>,
+    ) -> Option<RectF> {
+        todo!("implement before merging to main")
+    }
+
+    fn debug(
+        &self,
+        bounds: RectF,
+        layout: &Self::LayoutState,
+        paint: &Self::PaintState,
+        view: &V,
+        cx: &gpui::ViewContext<V>,
+    ) -> gpui::serde_json::Value {
+        todo!("implement before merging to main")
+    }
+}

crates/gpui/playground/src/element.rs 🔗

@@ -44,7 +44,7 @@ impl<V: 'static, D> Layout<V, D> {
     }
 }
 
-pub trait Element<V> {
+pub trait Element<V: 'static>: 'static {
     type Layout;
 
     fn layout(
@@ -63,30 +63,33 @@ pub trait Element<V> {
     ) where
         Self: Sized;
 
-    fn into_any(mut self) -> AnyElement<V>
+    fn into_any(self) -> AnyElement<V>
     where
-        Self: Sized,
+        Self: 'static + Sized,
     {
-        AnyElement(Box::new(ElementWithLayout {
+        AnyElement(Box::new(ElementState {
             element: self,
             layout: None,
         }))
     }
 }
 
-trait ElementTraitObject<V> {
+/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
+trait ElementStateObject<V> {
     fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
     fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>);
 }
 
-struct ElementWithLayout<V, E: Element<V>> {
+/// A wrapper around an element that stores its layout state.
+struct ElementState<V: 'static, E: Element<V>> {
     element: E,
     layout: Option<Layout<V, E::Layout>>,
 }
 
-impl<V, E: Element<V>> ElementTraitObject<V> for ElementWithLayout<V, E> {
+/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
+impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
     fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
-        let layout = Element::layout(self, view, cx)?;
+        let layout = self.element.layout(view, cx)?;
         let layout_id = layout.id;
         self.layout = Some(layout);
         Ok(layout_id)
@@ -94,23 +97,24 @@ impl<V, E: Element<V>> ElementTraitObject<V> for ElementWithLayout<V, E> {
 
     fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
         let layout = self.layout.as_mut().expect("paint called before layout");
-        Element::paint(self, view, layout, cx);
+        self.element.paint(view, layout, cx)
     }
 }
 
-pub struct AnyElement<V>(Box<dyn ElementTraitObject<V>>);
+/// A dynamic element.
+pub struct AnyElement<V>(Box<dyn ElementStateObject<V>>);
 
 impl<V> AnyElement<V> {
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+    pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
         self.0.layout(view, cx)
     }
 
-    fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
+    pub fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
         self.0.paint(view, layout_id, cx)
     }
 }
 
-pub trait ParentElement<V> {
+pub trait ParentElement<V: 'static> {
     fn children_mut(&mut self) -> &mut Vec<AnyElement<V>>;
 
     fn child(mut self, child: impl IntoElement<V>) -> Self
@@ -136,7 +140,7 @@ pub trait ParentElement<V> {
     }
 }
 
-pub trait IntoElement<V> {
+pub trait IntoElement<V: 'static> {
     type Element: Element<V>;
 
     fn into_element(self) -> Self::Element;

crates/gpui/playground/src/hoverable.rs 🔗

@@ -9,7 +9,7 @@ use gpui::platform::MouseMovedEvent;
 use refineable::Refineable;
 use std::{cell::Cell, marker::PhantomData};
 
-pub struct Hoverable<V, E: Element<V> + Styleable> {
+pub struct Hoverable<V: 'static, E: Element<V> + Styleable> {
     hovered: Cell<bool>,
     child_style: StyleRefinement,
     hovered_style: StyleRefinement,

crates/gpui/playground/src/layout_context.rs 🔗

@@ -11,7 +11,6 @@ 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> {
@@ -33,11 +32,8 @@ impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
 }
 
 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 new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
+        Self { legacy_cx }
     }
 
     pub fn add_layout_node<D>(

crates/gpui/playground/src/text.rs 🔗

@@ -2,12 +2,10 @@ use crate::{
     element::{Element, IntoElement, Layout},
     layout_context::LayoutContext,
     paint_context::PaintContext,
-    style::Style,
 };
 use anyhow::Result;
-use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
+use gpui::text_layout::LineLayout;
 use parking_lot::Mutex;
-use refineable::Refineable;
 use std::sync::Arc;
 
 impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
@@ -30,39 +28,40 @@ impl<V: 'static> Element<V> for Text {
         view: &mut V,
         cx: &mut LayoutContext<V>,
     ) -> Result<Layout<V, Self::Layout>> {
-        let rem_size = cx.rem_pixels();
-        let fonts = cx.platform().fonts();
-        let text_style = cx.text_style();
-        let line_height = cx.font_cache().line_height(text_style.font_size);
-        let layout_engine = cx.layout_engine().expect("no layout engine present");
-        let text = self.text.clone();
-        let layout = Arc::new(Mutex::new(None));
-
-        let style: Style = Style::default().refined(&self.metadata.style);
-        let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
-            let layout = layout.clone();
-            move |params| {
-                let line_layout = fonts.layout_line(
-                    text.as_ref(),
-                    text_style.font_size,
-                    &[(text.len(), text_style.to_run())],
-                );
-
-                let size = Size {
-                    width: line_layout.width,
-                    height: line_height,
-                };
-
-                layout.lock().replace(TextLayout {
-                    line_layout: Arc::new(line_layout),
-                    line_height,
-                });
-
-                size
-            }
-        })?;
-
-        Ok((node_id, layout))
+        // let rem_size = cx.rem_pixels();
+        // let fonts = cx.platform().fonts();
+        // let text_style = cx.text_style();
+        // let line_height = cx.font_cache().line_height(text_style.font_size);
+        // let layout_engine = cx.layout_engine().expect("no layout engine present");
+        // let text = self.text.clone();
+        // let layout = Arc::new(Mutex::new(None));
+
+        // let style: Style = Style::default().refined(&self.metadata.style);
+        // let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
+        //     let layout = layout.clone();
+        //     move |params| {
+        //         let line_layout = fonts.layout_line(
+        //             text.as_ref(),
+        //             text_style.font_size,
+        //             &[(text.len(), text_style.to_run())],
+        //         );
+
+        //         let size = Size {
+        //             width: line_layout.width,
+        //             height: line_height,
+        //         };
+
+        //         layout.lock().replace(TextLayout {
+        //             line_layout: Arc::new(line_layout),
+        //             line_height,
+        //         });
+
+        //         size
+        //     }
+        // })?;
+
+        // Ok((node_id, layout))
+        todo!()
     }
 
     fn paint<'a>(
@@ -71,24 +70,26 @@ impl<V: 'static> Element<V> for Text {
         layout: &mut Layout<V, Self::Layout>,
         cx: &mut PaintContext<V>,
     ) {
-        let element_layout_lock = layout.from_element.lock();
-        let element_layout = element_layout_lock
-            .as_ref()
-            .expect("layout has not been performed");
-        let line_layout = element_layout.line_layout.clone();
-        let line_height = element_layout.line_height;
-        drop(element_layout_lock);
-
-        let text_style = cx.text_style();
-        let line =
-            gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
-        line.paint(
-            cx.scene,
-            layout.from_engine.bounds.origin(),
-            layout.from_engine.bounds,
-            line_height,
-            cx.legacy_cx,
-        );
+        // ) {
+        //     let element_layout_lock = layout.from_element.lock();
+        //     let element_layout = element_layout_lock
+        //         .as_ref()
+        //         .expect("layout has not been performed");
+        //     let line_layout = element_layout.line_layout.clone();
+        //     let line_height = element_layout.line_height;
+        //     drop(element_layout_lock);
+
+        //     let text_style = cx.text_style();
+        //     let line =
+        //         gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
+        //     line.paint(
+        //         cx.scene,
+        //         layout.from_engine.bounds.origin(),
+        //         layout.from_engine.bounds,
+        //         line_height,
+        //         cx.legacy_cx,
+        //     );
+        todo!()
     }
 }
 

crates/gpui/playground/src/view.rs 🔗

@@ -1,4 +1,7 @@
-use crate::element::{AnyElement, Element};
+use crate::{
+    adapter::AdapterElement,
+    element::{AnyElement, Element},
+};
 use gpui::ViewContext;
 
 pub fn view<F, E>(mut render: F) -> ViewFn
@@ -17,6 +20,7 @@ impl gpui::Entity for ViewFn {
 
 impl gpui::View for ViewFn {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
-        (self.0)(cx).adapt().into_any()
+        use gpui::Element as _;
+        AdapterElement((self.0)(cx)).into_any()
     }
 }