WIP

Nathan Sobo created

Change summary

crates/gpui/src/app.rs           |   2 
crates/gpui/src/app/window.rs    |  30 +
crates/gpui/src/elements/node.rs | 580 +++++++++++++++++++++++----------
3 files changed, 423 insertions(+), 189 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -3339,7 +3339,7 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
         self.element_state::<Tag, T>(element_id, T::default())
     }
 
-    pub fn pixels_per_rem(&self) -> f32 {
+    pub fn rem_pixels(&self) -> f32 {
         16.
     }
 }

crates/gpui/src/app/window.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    elements::AnyRootElement,
+    elements::{node::Axis2d, AnyRootElement},
     geometry::rect::RectF,
     json::ToJson,
     keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
@@ -1248,16 +1248,38 @@ impl Column for Axis {
 }
 
 pub trait Vector2FExt {
+    fn infinity() -> Self;
     fn along(self, axis: Axis) -> f32;
+    fn get(self, axis: Axis2d) -> f32;
+    fn set(&mut self, axis: Axis2d, value: f32) -> Self;
 }
 
 impl Vector2FExt for Vector2F {
+    fn infinity() -> Self {
+        Self::new(f32::INFINITY, f32::INFINITY)
+    }
+
     fn along(self, axis: Axis) -> f32 {
         match axis {
             Axis::Horizontal => self.x(),
             Axis::Vertical => self.y(),
         }
     }
+
+    fn get(self, axis: Axis2d) -> f32 {
+        match axis {
+            Axis2d::X => self.x(),
+            Axis2d::Y => self.y(),
+        }
+    }
+
+    fn set(&mut self, axis: Axis2d, value: f32) -> Self {
+        match axis {
+            Axis2d::X => self.set_x(value),
+            Axis2d::Y => self.set_y(value),
+        }
+        *self
+    }
 }
 
 pub trait RectFExt {
@@ -1290,6 +1312,12 @@ impl SizeConstraint {
             max: size,
         }
     }
+    pub fn loose(max: Vector2F) -> Self {
+        Self {
+            min: Vector2F::zero(),
+            max,
+        }
+    }
 
     pub fn strict_along(axis: Axis, max: f32) -> Self {
         match axis {

crates/gpui/src/elements/node.rs 🔗

@@ -11,17 +11,23 @@ use crate::{
     serde_json::Value,
     text_layout::{Line, ShapedBoundary},
     AnyElement, AppContext, Element, LayoutContext, PaintContext, Quad, SceneBuilder,
-    SizeConstraint, View, ViewContext,
+    SizeConstraint, Vector2FExt, View, ViewContext,
 };
 use derive_more::Add;
 use length::{Length, Rems};
 use log::warn;
 use optional_struct::*;
-use std::{any::Any, borrow::Cow, f32, ops::Range, sync::Arc};
+use std::{
+    any::Any,
+    borrow::Cow,
+    f32,
+    ops::{Add, Range},
+    sync::Arc,
+};
 
 pub struct Node<V: View> {
     style: NodeStyle,
-    content: Vec<AnyElement<V>>,
+    children: Vec<AnyElement<V>>,
 }
 
 pub fn node<V: View>(child: impl Element<V>) -> Node<V> {
@@ -38,7 +44,7 @@ pub fn row<V: View>() -> Node<V> {
             axis: Axis3d::X,
             ..Default::default()
         },
-        content: Default::default(),
+        children: Default::default(),
     }
 }
 
@@ -48,7 +54,7 @@ pub fn stack<V: View>() -> Node<V> {
             axis: Axis3d::Z,
             ..Default::default()
         },
-        content: Default::default(),
+        children: Default::default(),
     }
 }
 
@@ -56,14 +62,14 @@ impl<V: View> Default for Node<V> {
     fn default() -> Self {
         Self {
             style: Default::default(),
-            content: Default::default(),
+            children: Default::default(),
         }
     }
 }
 
 impl<V: View> Node<V> {
     pub fn child(mut self, child: impl Element<V>) -> Self {
-        self.content.push(child.into_any());
+        self.children.push(child.into_any());
         self
     }
 
@@ -72,18 +78,18 @@ impl<V: View> Node<V> {
         I: IntoIterator<Item = E>,
         E: Element<V>,
     {
-        self.content
+        self.children
             .extend(children.into_iter().map(|child| child.into_any()));
         self
     }
 
     pub fn width(mut self, width: impl Into<Length>) -> Self {
-        self.style.width = width.into();
+        self.style.size.width = width.into();
         self
     }
 
     pub fn height(mut self, height: impl Into<Length>) -> Self {
-        self.style.height = height.into();
+        self.style.size.height = height.into();
         self
     }
 
@@ -104,7 +110,7 @@ impl<V: View> Node<V> {
     ) -> Self {
         let top_bottom = top_bottom.into();
         let left_right = left_right.into();
-        self.style.margin = Edges {
+        self.style.margins = Edges {
             top: top_bottom.top,
             bottom: top_bottom.bottom,
             left: left_right.left,
@@ -114,115 +120,122 @@ impl<V: View> Node<V> {
     }
 
     pub fn margin_top(mut self, top: Length) -> Self {
-        self.style.margin.top = top;
+        self.style.margins.top = top;
         self
     }
 
     pub fn margin_bottom(mut self, bottom: Length) -> Self {
-        self.style.margin.bottom = bottom;
+        self.style.margins.bottom = bottom;
         self
     }
 
     pub fn margin_left(mut self, left: impl Into<Length>) -> Self {
-        self.style.margin.left = left.into();
+        self.style.margins.left = left.into();
         self
     }
 
     pub fn margin_right(mut self, right: impl Into<Length>) -> Self {
-        self.style.margin.right = right.into();
+        self.style.margins.right = right.into();
         self
     }
 
-    fn layout_2d_children(
+    fn layout_xy(
         &mut self,
         axis: Axis2d,
-        size: Vector2F,
+        max_size: Vector2F,
+        rem_length: f32,
+        computed_margins: &mut Edges<f32>,
+        computed_padding: &mut Edges<f32>,
         view: &mut V,
         cx: &mut LayoutContext<V>,
     ) -> Vector2F {
-        let mut total_flex: Option<f32> = None;
-        let mut total_size = 0.0;
-        let mut cross_axis_max: f32 = 0.0;
-
-        // First pass: Layout non-flex children only
-        for child in &mut self.content {
-            let child_flex = child.metadata::<NodeStyle>().and_then(|style| match axis {
-                Axis2d::X => style.width.flex(),
-                Axis2d::Y => style.height.flex(),
-            });
-
-            if let Some(child_flex) = child_flex {
-                *total_flex.get_or_insert(0.) += child_flex;
+        *computed_margins = self.style.margins.fixed_pixels(rem_length);
+        *computed_padding = self.style.padding.fixed_pixels(rem_length);
+
+        let padded_max =
+            max_size - computed_margins.size() - self.style.borders.width - computed_padding.size();
+        let mut remaining_length = padded_max.get(axis);
+
+        // Pass 1: Total up flex units and layout inflexible children.
+        //
+        // Consume the remaining length as we layout inflexible children, so that any
+        // remaining length can be distributed among flexible children in the next pass.
+        let mut remaining_flex: f32 = 0.;
+        let mut cross_axis_max: f32 = 0.;
+        let cross_axis = axis.rotate();
+
+        // Fixed children are unconstrained along the primary axis, and constrained to
+        // the padded max size along the cross axis.
+        let mut child_constraint =
+            SizeConstraint::loose(Vector2F::infinity().set(cross_axis, padded_max.get(cross_axis)));
+
+        for child in &mut self.children {
+            if let Some(child_flex) = child
+                .metadata::<NodeStyle>()
+                .and_then(|style| style.flex(axis))
+            {
+                remaining_flex += child_flex;
             } else {
-                match axis {
-                    Axis2d::X => {
-                        let child_constraint =
-                            SizeConstraint::new(Vector2F::zero(), vec2f(f32::INFINITY, size.y()));
-                        let child_size = child.layout(child_constraint, view, cx);
-                        cross_axis_max = cross_axis_max.max(child_size.y());
-                        total_size += child_size.x();
-                    }
-                    Axis2d::Y => {
-                        let child_constraint =
-                            SizeConstraint::new(Vector2F::zero(), vec2f(size.x(), f32::INFINITY));
-                        let child_size = child.layout(child_constraint, view, cx);
-                        cross_axis_max = cross_axis_max.max(child_size.x());
-                        total_size += child_size.y();
-                    }
-                }
+                let child_size = child.layout(child_constraint, view, cx);
+                cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
+                remaining_length -= child_size.get(axis);
             }
         }
 
-        let remaining_space = match axis {
-            Axis2d::X => size.x() - total_size,
-            Axis2d::Y => size.y() - total_size,
-        };
-
-        // Second pass: Layout flexible children
-        if let Some(total_flex) = total_flex {
-            if total_flex > 0. {
-                let space_per_flex = remaining_space.max(0.) / total_flex;
-
-                for child in &mut self.content {
-                    let child_flex = child.metadata::<NodeStyle>().and_then(|style| match axis {
-                        Axis2d::X => style.width.flex(),
-                        Axis2d::Y => style.height.flex(),
-                    });
-                    if let Some(child_flex) = child_flex {
-                        let child_max = space_per_flex * child_flex;
-                        let mut child_constraint = SizeConstraint::new(Vector2F::zero(), size);
-                        match axis {
-                            Axis2d::X => {
-                                child_constraint.min.set_x(0.0);
-                                child_constraint.max.set_x(child_max);
-                            }
-                            Axis2d::Y => {
-                                child_constraint.min.set_y(0.0);
-                                child_constraint.max.set_y(child_max);
-                            }
-                        }
+        // Pass 2: Allocate the remaining space among flexible lengths along the primary axis.
+        if remaining_flex > 0. {
+            // Add flex pixels from margin and padding.
+            *computed_margins.start_mut(axis) += self.style.margins.start(axis).flex_pixels(
+                rem_length,
+                &mut remaining_flex,
+                &mut remaining_length,
+            );
+            *computed_padding.start_mut(axis) += self.style.padding.start(axis).flex_pixels(
+                rem_length,
+                &mut remaining_flex,
+                &mut remaining_length,
+            );
 
-                        let child_size = child.layout(child_constraint, view, cx);
-                        cross_axis_max = match axis {
-                            Axis2d::X => {
-                                total_size += child_size.x();
-                                cross_axis_max.max(child_size.y())
-                            }
-                            Axis2d::Y => {
-                                total_size += child_size.y();
-                                cross_axis_max.max(child_size.x())
-                            }
-                        };
-                    }
+            // Lay out the flexible children
+            let mut child_max = padded_max;
+            for child in &mut self.children {
+                if let Some(child_flex) = child
+                    .metadata::<NodeStyle>()
+                    .and_then(|style| style.flex(axis))
+                {
+                    child_max.set(axis, child_flex / remaining_flex * remaining_length);
+                    let child_size = child.layout(SizeConstraint::loose(child_max), view, cx);
+
+                    remaining_flex -= child_flex;
+                    remaining_length -= child_size.get(axis);
+                    cross_axis_max = child_size.get(cross_axis).max(cross_axis_max);
                 }
             }
+
+            // Add flex pixels from margin and padding.
+            *computed_margins.end_mut(axis) += self.style.margins.end(axis).flex_pixels(
+                rem_length,
+                &mut remaining_flex,
+                &mut remaining_length,
+            );
+            *computed_padding.end_mut(axis) += self.style.padding.end(axis).flex_pixels(
+                rem_length,
+                &mut remaining_flex,
+                &mut remaining_length,
+            );
         }
 
-        let size = match axis {
-            Axis2d::X => vec2f(total_size, cross_axis_max),
-            Axis2d::Y => vec2f(cross_axis_max, total_size),
+        let width = match self.style.size.width {
+            Length::Hug => todo!(),
+            Length::Fixed(_) => todo!(),
+            Length::Auto { flex, min, max } => todo!(),
         };
-        size
+
+        let length = max_size.get(axis) - remaining_length;
+        match axis {
+            Axis2d::X => vec2f(length, cross_axis_max),
+            Axis2d::Y => vec2f(cross_axis_max, length),
+        }
     }
 
     fn paint_2d_children(
@@ -231,7 +244,7 @@ impl<V: View> Node<V> {
         axis: Axis2d,
         bounds: RectF,
         visible_bounds: RectF,
-        size_of_children: &mut Vector2F,
+        layout: &mut NodeLayout,
         view: &mut V,
         cx: &mut ViewContext<V>,
     ) {
@@ -248,13 +261,13 @@ impl<V: View> Node<V> {
         align_child(
             &mut child_origin,
             parent_size,
-            *size_of_children,
+            layout.content_size,
             self.style.align.0,
             align_horizontally,
             align_vertically,
         );
 
-        for child in &mut self.content {
+        for child in &mut self.children {
             // Align each child along the cross axis
             align_horizontally = !align_horizontally;
             align_vertically = !align_vertically;
@@ -295,16 +308,13 @@ impl<V: View> Node<V> {
     // }
 
     fn inset_size(&self, rem_size: f32) -> Vector2F {
-        self.padding_size(rem_size) + self.border_size() + self.margin_size(rem_size)
+        todo!()
+        // self.padding_size(rem_size) + self.border_size() + self.margin_size(rem_size)
     }
 
-    fn margin_size(&self, rem_size: f32) -> Vector2F {
-        // We need to account for auto margins
-        todo!()
-        // vec2f(
-        //     (self.style.margin.left + self.style.margin.right).to_pixels(rem_size),
-        //     (self.style.margin.top + self.style.margin.bottom).to_pixels(rem_size),
-        // )
+    //
+    fn margin_fixed_size(&self, rem_size: f32) -> Vector2F {
+        self.style.margins.fixed().to_pixels(rem_size)
     }
 
     fn padding_size(&self, rem_size: f32) -> Vector2F {
@@ -318,19 +328,19 @@ impl<V: View> Node<V> {
 
     fn border_size(&self) -> Vector2F {
         let mut x = 0.0;
-        if self.style.border.left {
-            x += self.style.border.width;
+        if self.style.borders.left {
+            x += self.style.borders.width;
         }
-        if self.style.border.right {
-            x += self.style.border.width;
+        if self.style.borders.right {
+            x += self.style.borders.width;
         }
 
         let mut y = 0.0;
-        if self.style.border.top {
-            y += self.style.border.width;
+        if self.style.borders.top {
+            y += self.style.borders.width;
         }
-        if self.style.border.bottom {
-            y += self.style.border.width;
+        if self.style.borders.bottom {
+            y += self.style.borders.width;
         }
 
         vec2f(x, y)
@@ -338,7 +348,7 @@ impl<V: View> Node<V> {
 }
 
 impl<V: View> Element<V> for Node<V> {
-    type LayoutState = Vector2F; // Content size
+    type LayoutState = NodeLayout;
     type PaintState = ();
 
     fn layout(
@@ -347,48 +357,23 @@ impl<V: View> Element<V> for Node<V> {
         view: &mut V,
         cx: &mut LayoutContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
-        let mut size = Vector2F::zero();
-        let rem_size = cx.pixels_per_rem();
-        let margin_size = self.margin_size(rem_size);
-        match self.style.width {
-            Length::Hug => size.set_x(f32::INFINITY),
-            Length::Fixed(width) => size.set_x(width.to_pixels(rem_size) + margin_size.x()),
-            Length::Auto { min, max, .. } => size.set_x(constraint.max.x().max(min).min(max)),
-        }
-        match self.style.height {
-            Length::Hug => size.set_y(f32::INFINITY),
-            Length::Fixed(height) => size.set_y(height.to_pixels(rem_size) + margin_size.y()),
-            Length::Auto { min, max, .. } => size.set_y(constraint.max.y().max(min).min(max)),
-        }
-
-        // Impose horizontal constraints
-        if constraint.min.x().is_finite() {
-            size.set_x(size.x().max(constraint.min.x()));
-        }
-        size.set_x(size.x().min(constraint.max.x()));
-
-        // Impose vertical constraints
-        if constraint.min.y().is_finite() {
-            size.set_y(size.y().max(constraint.min.y()));
-        }
-        size.set_y(size.y().min(constraint.max.y()));
-
-        let inset_size = self.inset_size(rem_size);
-        let inner_size = size - inset_size;
-        let size_of_children = match self.style.axis {
-            Axis3d::X => self.layout_2d_children(Axis2d::X, inner_size, view, cx),
-            Axis3d::Y => self.layout_2d_children(Axis2d::Y, inner_size, view, cx),
-            Axis3d::Z => todo!(), // self.layout_stacked_children(inner_constraint, view, cx),
+        let mut layout = NodeLayout::default();
+
+        let size = if let Some(axis) = self.style.axis.to_2d() {
+            self.layout_xy(
+                axis,
+                constraint.max,
+                cx.rem_pixels(),
+                &mut layout.margins,
+                &mut layout.padding,
+                view,
+                cx,
+            )
+        } else {
+            todo!()
         };
 
-        if matches!(self.style.width, Length::Hug) {
-            size.set_x(size_of_children.x() + inset_size.x());
-        }
-        if matches!(self.style.height, Length::Hug) {
-            size.set_y(size_of_children.y() + inset_size.y());
-        }
-
-        (size, size_of_children)
+        (size, layout);
     }
 
     fn paint(
@@ -396,15 +381,39 @@ impl<V: View> Element<V> for Node<V> {
         scene: &mut SceneBuilder,
         bounds: RectF,
         visible_bounds: RectF,
-        size_of_children: &mut Vector2F,
+        layout: &mut NodeLayout,
         view: &mut V,
         cx: &mut PaintContext<V>,
     ) -> Self::PaintState {
-        let rem_size = cx.pixels_per_rem();
-        let margin: Edges<f32> = todo!(); // &self.style.margin.to_pixels(rem_size);
+        let rem_pixels = cx.rem_pixels();
+        // let margin: Edges<f32> = todo!(); // &self.style.margin.to_pixels(rem_size);
+        //
+
+        let size = bounds.size();
+        let mut remaining_flex = layout.flex_size;
+        let mut fixed_size = layout.fixed_size;
+
+        // let margin_left = self.style.margin.left.to_pixels(rem_pixels, size.x() - fixed_size.x() / layout.);
+        // fixed_size +=
+        // let mut origin = bounds.origin();
+        // origin.set_x(
+        //     origin.x()
+        //     ,
+        //             Length::Hug => 0.,
+        //             Length::Fixed(rems) => rems.to_pixels(rem_pixels),
+        //             Length::Auto { flex, min, max } => {
+        //                 flex * (size.x() - fixed_size.x()) / layout.flex_size.x()
+        //             }
+        //         },
+        // );
+
+        let mut low_right = bounds.lower_right();
+
+        let mut remaining_fixed = bounds.size() - layout.fixed_size;
+        let mut remaining_flex = layout.flex_size;
 
         // Account for margins
-        let content_bounds = RectF::from_points(
+        let margin_bounds = RectF::from_points(
             bounds.origin() + vec2f(margin.left, margin.top),
             bounds.lower_right() - vec2f(margin.right, margin.bottom),
         );
@@ -412,7 +421,7 @@ impl<V: View> Element<V> for Node<V> {
         // Paint drop shadow
         for shadow in &self.style.shadows {
             scene.push_shadow(scene::Shadow {
-                bounds: content_bounds + shadow.offset,
+                bounds: margin_bounds + shadow.offset,
                 corner_radius: self.style.corner_radius,
                 sigma: shadow.blur,
                 color: shadow.color,
@@ -432,29 +441,29 @@ impl<V: View> Element<V> for Node<V> {
         // Render the background and/or the border (if it not an overlay border).
         let Fill::Color(fill_color) = self.style.fill;
         let is_fill_visible = !fill_color.is_fully_transparent();
-        if is_fill_visible || self.style.border.is_visible() {
+        if is_fill_visible || self.style.borders.is_visible() {
             scene.push_quad(Quad {
-                bounds: content_bounds,
+                bounds: margin_bounds,
                 background: is_fill_visible.then_some(fill_color),
                 border: scene::Border {
-                    width: self.style.border.width,
-                    color: self.style.border.color,
+                    width: self.style.borders.width,
+                    color: self.style.borders.color,
                     overlay: false,
-                    top: self.style.border.top,
-                    right: self.style.border.right,
-                    bottom: self.style.border.bottom,
-                    left: self.style.border.left,
+                    top: self.style.borders.top,
+                    right: self.style.borders.right,
+                    bottom: self.style.borders.bottom,
+                    left: self.style.borders.left,
                 },
                 corner_radius: self.style.corner_radius,
             });
         }
 
-        if !self.content.is_empty() {
+        if !self.children.is_empty() {
             // Account for padding first.
             let padding: Edges<f32> = todo!(); // &self.style.padding.to_pixels(rem_size);
             let padded_bounds = RectF::from_points(
-                content_bounds.origin() + vec2f(padding.left, padding.top),
-                content_bounds.lower_right() - vec2f(padding.right, padding.top),
+                margin_bounds.origin() + vec2f(padding.left, padding.top),
+                margin_bounds.lower_right() - vec2f(padding.right, padding.top),
             );
 
             match self.style.axis {
@@ -463,7 +472,7 @@ impl<V: View> Element<V> for Node<V> {
                     Axis2d::X,
                     padded_bounds,
                     visible_bounds,
-                    size_of_children,
+                    layout,
                     view,
                     cx,
                 ),
@@ -472,7 +481,7 @@ impl<V: View> Element<V> for Node<V> {
                     Axis2d::Y,
                     padded_bounds,
                     visible_bounds,
-                    size_of_children,
+                    layout,
                     view,
                     cx,
                 ),
@@ -491,7 +500,7 @@ impl<V: View> Element<V> for Node<V> {
         view: &V,
         cx: &ViewContext<V>,
     ) -> Option<RectF> {
-        self.content
+        self.children
             .iter()
             .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
     }
@@ -598,18 +607,40 @@ pub struct NodeStyle {
     gap_x: Gap,
     gap_y: Gap,
 
-    width: Length,
-    height: Length,
-    margin: Edges<Length>,
+    size: Size<Length>,
+    margins: Edges<Length>,
     padding: Edges<Length>,
     text: OptionalTextStyle,
     opacity: f32,
     fill: Fill,
-    border: Border,
+    borders: Border,
     corner_radius: f32, // corner radius matches swift!
     shadows: Vec<Shadow>,
 }
 
+impl NodeStyle {
+    fn flex(&self, axis: Axis2d) -> Option<f32> {
+        let mut sum = None;
+        match axis {
+            Axis2d::X => {
+                sum = optional_add(sum, self.margins.left.flex());
+                sum = optional_add(sum, self.padding.left.flex());
+                sum = optional_add(sum, self.size.width.flex());
+                sum = optional_add(sum, self.padding.right.flex());
+                sum = optional_add(sum, self.margins.right.flex());
+            }
+            Axis2d::Y => {
+                sum = optional_add(sum, self.margins.top.flex());
+                sum = optional_add(sum, self.padding.top.flex());
+                sum = optional_add(sum, self.size.height.flex());
+                sum = optional_add(sum, self.padding.bottom.flex());
+                sum = optional_add(sum, self.margins.bottom.flex());
+            }
+        }
+        sum
+    }
+}
+
 #[optional_struct]
 struct TextStyle {
     size: Rems,
@@ -618,12 +649,44 @@ struct TextStyle {
     style: FontStyle,
 }
 
-#[derive(Add)]
+#[derive(Add, Default, Clone)]
 struct Size<T> {
     width: T,
     height: T,
 }
 
+impl<T: Add<Output = T>> Size<Option<T>> {
+    fn add_assign_optional(&mut self, rhs: Size<Option<T>>) {
+        self.width = optional_add(self.width, rhs.width);
+        self.height = optional_add(self.height, rhs.height);
+    }
+}
+
+impl Size<Length> {
+    pub fn fixed(&self) -> Size<Rems> {
+        Size {
+            width: self.width.fixed().unwrap_or_default(),
+            height: self.height.fixed().unwrap_or_default(),
+        }
+    }
+
+    pub fn flex(&self) -> Vector2F {
+        vec2f(
+            self.width.flex().unwrap_or(0.),
+            self.height.flex().unwrap_or(0.),
+        )
+    }
+}
+
+impl Size<Rems> {
+    pub fn to_pixels(&self, rem_size: f32) -> Vector2F {
+        vec2f(
+            self.width.to_pixels(rem_size),
+            self.height.to_pixels(rem_size),
+        )
+    }
+}
+
 // Sides?
 #[derive(Clone, Default)]
 struct Edges<T> {
@@ -633,6 +696,89 @@ struct Edges<T> {
     right: T,
 }
 
+impl<T> Edges<T> {
+    fn start(&self, axis: Axis2d) -> &T {
+        match axis {
+            Axis2d::X => &self.left,
+            Axis2d::Y => &self.top,
+        }
+    }
+
+    fn start_mut(&mut self, axis: Axis2d) -> &mut T {
+        match axis {
+            Axis2d::X => &mut self.left,
+            Axis2d::Y => &mut self.top,
+        }
+    }
+
+    fn end(&self, axis: Axis2d) -> &T {
+        match axis {
+            Axis2d::X => &self.right,
+            Axis2d::Y => &self.bottom,
+        }
+    }
+
+    fn end_mut(&mut self, axis: Axis2d) -> &mut T {
+        match axis {
+            Axis2d::X => &mut self.right,
+            Axis2d::Y => &mut self.bottom,
+        }
+    }
+}
+
+impl Edges<f32> {
+    fn size(&self) -> Vector2F {
+        vec2f(self.left + self.right, self.top + self.bottom)
+    }
+}
+
+impl Edges<Length> {
+    fn fixed_pixels(&self, rem_length: f32) -> Edges<f32> {
+        Edges {
+            top: self.top.fixed_pixels(rem_length),
+            bottom: self.bottom.fixed_pixels(rem_length),
+            left: self.left.fixed_pixels(rem_length),
+            right: self.right.fixed_pixels(rem_length),
+        }
+    }
+
+    fn flex_pixels(
+        &self,
+        rem_length: f32,
+        remaining_flex: &mut f32,
+        remaining_length: &mut f32,
+    ) -> Edges<f32> {
+        Edges {
+            top: self
+                .top
+                .flex_pixels(rem_length, remaining_flex, remaining_length),
+            bottom: self
+                .bottom
+                .flex_pixels(rem_length, remaining_flex, remaining_length),
+            left: self
+                .left
+                .flex_pixels(rem_length, remaining_flex, remaining_length),
+            right: self
+                .right
+                .flex_pixels(rem_length, remaining_flex, remaining_length),
+        }
+    }
+
+    // pub fn fixed(&self) -> Size<Rems> {
+    //     let mut size = Size::default();
+    //     size.width += self.left.fixed().unwrap_or_default();
+    //     size.width += self.right.fixed().unwrap_or_default();
+    //     size
+    // }
+
+    pub fn flex(&self) -> Vector2F {
+        vec2f(
+            self.left.flex().unwrap_or(0.) + self.right.flex().unwrap_or(0.),
+            self.top.flex().unwrap_or(0.) + self.bottom.flex().unwrap_or(0.),
+        )
+    }
+}
+
 impl Edges<Rems> {
     pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
         Edges {
@@ -688,9 +834,9 @@ impl Border {
 }
 
 pub mod length {
-    use derive_more::{Add, Into};
+    use derive_more::{Add, AddAssign, Into};
 
-    #[derive(Add, Into, Clone, Copy, Default, Debug, PartialEq)]
+    #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)]
     pub struct Rems(f32);
 
     pub fn rems(rems: f32) -> Rems {
@@ -698,8 +844,8 @@ pub mod length {
     }
 
     impl Rems {
-        pub fn to_pixels(&self, root_font_size: f32) -> f32 {
-            self.0 * root_font_size
+        pub fn to_pixels(&self, rem_length: f32) -> f32 {
+            self.0 * rem_length
         }
     }
 
@@ -710,8 +856,8 @@ pub mod length {
         Fixed(Rems),
         Auto {
             flex: f32,
-            min: f32,
-            max: f32,
+            min: Rems,
+            max: Rems,
         },
     }
 
@@ -728,26 +874,59 @@ pub mod length {
     pub fn flex(flex: f32) -> Length {
         Length::Auto {
             flex,
-            min: 0.,
-            max: f32::INFINITY,
+            min: Default::default(),
+            max: rems(f32::INFINITY),
         }
     }
 
-    pub fn constrained(flex: f32, min: Option<f32>, max: Option<f32>) -> Length {
+    pub fn constrained(flex: f32, min: Option<Rems>, max: Option<Rems>) -> Length {
         Length::Auto {
             flex,
-            min: min.unwrap_or(0.),
-            max: max.unwrap_or(f32::INFINITY),
+            min: min.unwrap_or(Default::default()),
+            max: max.unwrap_or(rems(f32::INFINITY)),
         }
     }
 
     impl Length {
+        pub fn flex_pixels(
+            &self,
+            rem_length: f32,
+            remaining_flex: &mut f32,
+            remaining_length: &mut f32,
+        ) -> f32 {
+            match self {
+                Length::Auto { flex, min, max } => {
+                    let flex_length = *remaining_length / *remaining_flex;
+                    let length = (flex * flex_length)
+                        .clamp(min.to_pixels(rem_length), max.to_pixels(rem_length));
+                    *remaining_flex -= flex;
+                    *remaining_length -= length;
+                    length
+                }
+                _ => 0.,
+            }
+        }
+
+        pub fn fixed_pixels(&self, rem: f32) -> f32 {
+            match self {
+                Length::Fixed(rems) => rems.to_pixels(rem),
+                _ => 0.,
+            }
+        }
+
         pub fn flex(&self) -> Option<f32> {
             match self {
                 Length::Auto { flex, .. } => Some(*flex),
                 _ => None,
             }
         }
+
+        pub fn fixed(&self) -> Option<Rems> {
+            match self {
+                Length::Fixed(rems) => Some(*rems),
+                _ => None,
+            }
+        }
     }
 }
 
@@ -779,12 +958,21 @@ impl Axis3d {
 }
 
 #[derive(Clone, Copy, Default)]
-enum Axis2d {
+pub enum Axis2d {
     X,
     #[default]
     Y,
 }
 
+impl Axis2d {
+    fn rotate(self) -> Self {
+        match self {
+            Axis2d::X => Axis2d::Y,
+            Axis2d::Y => Axis2d::X,
+        }
+    }
+}
+
 #[derive(Clone, Copy, Default)]
 enum Overflow {
     #[default]
@@ -853,6 +1041,13 @@ pub fn text<V: View>(text: impl Into<Cow<'static, str>>) -> Node<V> {
     })
 }
 
+#[derive(Default)]
+struct NodeLayout {
+    content_size: Vector2F,
+    margins: Edges<f32>,
+    padding: Edges<f32>,
+}
+
 impl<V: View> Element<V> for Text {
     type LayoutState = TextLayout;
     type PaintState = ();
@@ -864,7 +1059,6 @@ impl<V: View> Element<V> for Text {
         cx: &mut LayoutContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
         // Convert the string and highlight ranges into an iterator of highlighted chunks.
-
         let mut offset = 0;
         let mut highlight_ranges = self
             .highlights
@@ -1127,3 +1321,15 @@ pub struct TextLayout {
     wrap_boundaries: Vec<Vec<ShapedBoundary>>,
     line_height: f32,
 }
+
+fn optional_add<T>(a: Option<T>, b: Option<T>) -> Option<T::Output>
+where
+    T: Add<Output = T>,
+{
+    match (a, b) {
+        (Some(a), Some(b)) => Some(a + b),
+        (Some(a), None) => Some(a),
+        (None, Some(b)) => Some(b),
+        (None, None) => None,
+    }
+}