WIP

Nathan Sobo created

Change summary

crates/gpui3/src/elements.rs      |  2 
crates/gpui3/src/elements/img.rs  |  2 
crates/gpui3/src/elements/text.rs | 17 +++--
crates/gpui3/src/geometry.rs      |  9 +++
crates/gpui3/src/gpui3.rs         |  8 ++
crates/gpui3/src/style.rs         |  1 
crates/gpui3/src/taffy.rs         | 92 ++++++++++++++++++++++++++------
crates/gpui3/src/window.rs        | 10 +-
8 files changed, 107 insertions(+), 34 deletions(-)

Detailed changes

crates/gpui3/src/elements/img.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{Element, Layout, LayoutId, Result, Style, Styled};
 use refineable::RefinementCascade;
 use std::marker::PhantomData;
-use util::{arc_cow::ArcCow, ResultExt};
+use util::arc_cow::ArcCow;
 
 pub struct Img<S> {
     style: RefinementCascade<Style>,

crates/gpui3/src/elements/text.rs 🔗

@@ -1,6 +1,5 @@
 use crate::{
-    AnyElement, Element, IntoAnyElement, Layout, LayoutId, Line, LineLayout, Pixels, Result, Size,
-    ViewContext,
+    AnyElement, Element, IntoAnyElement, Layout, LayoutId, Line, Pixels, Result, Size, ViewContext,
 };
 use parking_lot::Mutex;
 use std::{marker::PhantomData, sync::Arc};
@@ -42,16 +41,18 @@ impl<S: 'static> Element for Text<S> {
     ) -> Result<(LayoutId, Self::FrameState)> {
         let text_system = cx.text_system().clone();
         let text_style = cx.text_style();
-        let line_height = cx.text_system().line_height(text_style.font_size);
+        let font_size = text_style.font_size * cx.rem_size();
+        let line_height = cx.text_system().line_height(font_size);
         let text = self.text.clone();
         let paint_state = Arc::new(Mutex::new(None));
 
-        let layout_id = cx.request_measured_layout(Default::default(), cx.rem_size(), {
+        let rem_size = cx.rem_size();
+        let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
             let frame_state = paint_state.clone();
             move |_, _| {
                 let line_layout = text_system.layout_str(
                     text.as_ref(),
-                    text_style.font_size,
+                    font_size,
                     &[(text.len(), text_style.to_run())],
                 );
 
@@ -78,7 +79,7 @@ impl<S: 'static> Element for Text<S> {
         _: &mut Self::State,
         paint_state: &mut Self::FrameState,
         cx: &mut ViewContext<S>,
-    ) {
+    ) -> Result<()> {
         let bounds = layout.bounds;
 
         let line;
@@ -96,7 +97,9 @@ impl<S: 'static> Element for Text<S> {
 
         // todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder.");
         let visible_bounds = bounds;
-        line.paint(bounds.origin, visible_bounds, line_height, cx.legacy_cx);
+        line.paint(bounds.origin, visible_bounds, line_height, cx);
+
+        Ok(())
     }
 }
 

crates/gpui3/src/geometry.rs 🔗

@@ -79,6 +79,15 @@ pub fn size<T: Clone + Debug>(width: T, height: T) -> Size<T> {
     Size { width, height }
 }
 
+impl<T: Clone + Debug> Size<T> {
+    pub fn map<U: Clone + Debug, F: Fn(T) -> U>(&self, f: F) -> Size<U> {
+        Size {
+            width: f(self.width.clone()),
+            height: f(self.height.clone()),
+        }
+    }
+}
+
 impl From<Size<Option<Pixels>>> for Size<Option<f32>> {
     fn from(val: Size<Option<Pixels>>) -> Self {
         Size {

crates/gpui3/src/gpui3.rs 🔗

@@ -31,8 +31,8 @@ pub use smol::Timer;
 use std::ops::{Deref, DerefMut};
 pub use style::*;
 pub use styled::*;
-pub use taffy::LayoutId;
 use taffy::TaffyLayoutEngine;
+pub use taffy::{AvailableSpace, LayoutId};
 pub use text_system::*;
 pub use util::arc_cow::ArcCow;
 pub use window::*;
@@ -55,6 +55,12 @@ pub trait Context {
 #[derive(Clone, Eq, PartialEq)]
 pub struct SharedString(ArcCow<'static, str>);
 
+impl Default for SharedString {
+    fn default() -> Self {
+        Self(ArcCow::Owned("".into()))
+    }
+}
+
 impl AsRef<str> for SharedString {
     fn as_ref(&self) -> &str {
         &self.0

crates/gpui3/src/style.rs 🔗

@@ -3,7 +3,6 @@ use super::{
     Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, RunStyle, SharedString, Size,
     SizeRefinement, ViewContext, WindowContext,
 };
-use crate::{FontCache, TextSystem};
 use refineable::Refineable;
 pub use taffy::style::{
     AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,

crates/gpui3/src/taffy.rs 🔗

@@ -4,9 +4,24 @@ use super::{
 };
 use std::fmt::Debug;
 pub use taffy::tree::NodeId as LayoutId;
-use taffy::{style::AvailableSpace, tree::MeasureFunc, *};
+use taffy::{
+    geometry::Size as TaffySize,
+    style::AvailableSpace as TaffyAvailableSpace,
+    tree::{Measurable, MeasureFunc},
+    Taffy,
+};
 pub struct TaffyLayoutEngine(Taffy);
 
+#[derive(Copy, Clone, Debug)]
+pub enum AvailableSpace {
+    /// The amount of space available is the specified number of pixels
+    Definite(Pixels),
+    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
+    MinContent,
+    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
+    MaxContent,
+}
+
 impl TaffyLayoutEngine {
     pub fn new() -> Self {
         TaffyLayoutEngine(Taffy::new())
@@ -30,12 +45,17 @@ impl TaffyLayoutEngine {
         &mut self,
         style: Style,
         rem_size: Pixels,
-        measure: impl FnOnce(Size<Option<Pixels>>, Size<AvailableSpace>) + 'static,
+        measure: impl Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>
+            + Send
+            + Sync
+            + 'static,
     ) -> Result<LayoutId> {
         let style = style.to_taffy(rem_size);
 
-        self.0
-            .new_leaf_with_measure(style, Box::new(Measureable(measure)))
+        let measurable = Box::new(Measureable(measure)) as Box<dyn Measurable>;
+        Ok(self
+            .0
+            .new_leaf_with_measure(style, MeasureFunc::Boxed(measurable))?)
     }
 
     pub fn layout(&mut self, id: LayoutId) -> Result<Layout> {
@@ -47,14 +67,18 @@ struct Measureable<F>(F);
 
 impl<F> taffy::tree::Measurable for Measureable<F>
 where
-    F: Send + Sync + FnOnce(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>,
+    F: Send + Sync + Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>,
 {
     fn measure(
         &self,
-        known_dimensions: taffy::prelude::Size<Option<f32>>,
-        available_space: taffy::prelude::Size<AvailableSpace>,
-    ) -> taffy::prelude::Size<f32> {
-        (self.0)(known_dimensions.into(), available_space.into()).into()
+        known_dimensions: TaffySize<Option<f32>>,
+        available_space: TaffySize<TaffyAvailableSpace>,
+    ) -> TaffySize<f32> {
+        let known_dimensions: Size<Option<f32>> = known_dimensions.into();
+        let known_dimensions: Size<Option<Pixels>> = known_dimensions.map(|d| d.map(Into::into));
+        let available_space = available_space.into();
+        let size = (self.0)(known_dimensions, available_space);
+        size.into()
     }
 }
 
@@ -232,14 +256,48 @@ where
     }
 }
 
-impl<S, T: Clone + Default + Debug> From<taffy::geometry::Size<S>> for Size<T>
-where
-    S: Into<T>,
-{
-    fn from(value: taffy::geometry::Size<S>) -> Self {
-        Self {
-            width: value.width.into(),
-            height: value.height.into(),
+impl<T: Into<U>, U: Clone + Debug> From<TaffySize<T>> for Size<U> {
+    fn from(taffy_size: taffy::geometry::Size<T>) -> Self {
+        Size {
+            width: taffy_size.width.into(),
+            height: taffy_size.height.into(),
+        }
+    }
+}
+
+impl<T: Into<U> + Clone + Debug, U> From<Size<T>> for taffy::geometry::Size<U> {
+    fn from(size: Size<T>) -> Self {
+        taffy::geometry::Size {
+            width: size.width.into(),
+            height: size.height.into(),
+        }
+    }
+}
+
+// impl From<TaffySize<Option<f32>>> for Size<Option<Pixels>> {
+//     fn from(value: TaffySize<Option<f32>>) -> Self {
+//         Self {
+//             width: value.width.map(Into::into),
+//             height: value.height.map(Into::into),
+//         }
+//     }
+// }
+
+// impl From<TaffySize<TaffyAvailableSpace>> for Size<AvailableSpace> {
+//     fn from(taffy_size: TaffySize<TaffyAvailableSpace>) -> Self {
+//         Size {
+//             width: From::from(taffy_size.width),
+//             height: From::from(taffy_size.height),
+//         }
+//     }
+// }
+
+impl From<TaffyAvailableSpace> for AvailableSpace {
+    fn from(space: TaffyAvailableSpace) -> Self {
+        match space {
+            TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
+            TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
+            TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
         }
     }
 }

crates/gpui3/src/window.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement};
+use crate::{AvailableSpace, PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement};
 
 use super::{
     px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference,
@@ -11,7 +11,6 @@ use std::{
     any::{Any, TypeId},
     marker::PhantomData,
 };
-use taffy::style::AvailableSpace;
 
 pub struct AnyWindow {}
 
@@ -75,7 +74,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 
     pub fn request_measured_layout<
-        F: FnOnce(Size<Option<Pixels>>, Size<AvailableSpace>) + 'static,
+        F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels> + Send + Sync + 'static,
     >(
         &mut self,
         style: Style,
@@ -107,11 +106,12 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.text_style_stack.pop();
     }
 
-    pub fn text_style(&self) -> &Vec<TextStyle> {
-        let style = TextStyleRefinement::default();
+    pub fn text_style(&self) -> TextStyle {
+        let mut style = TextStyle::default();
         for refinement in &self.window.text_style_stack {
             style.refine(refinement);
         }
+        style
     }
 
     pub fn mouse_position(&self) -> Point<Pixels> {