tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 788HTML 740Markdown 12TOML 62536 Sutichaykuna

Initial refactor of API

d67533315ff301646c34a4680b0d50c9a4628f0a · ruwaq Emil Sjölander · (7 wata ñawpaq) · tayta 4249f1b25 · kay commitpi sach'ata qhaway

Semántico sayk'uy · 24

src/algo.rs

src/layout.rs

  • qullusqa struct Node pub

src/lib.rs

src/node.rs

src/result.rs

src/style.rs

6 qillqakuna · +213 −175

diff --git a/src/algo.rs b/src/algo.rs
index 573010a6b..eac45f340 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -5,27 +5,29 @@ use libm::F32Ext;
 
 use core::f32;
 
-use crate::layout;
-use crate::ref_eq::ref_eq;
+use std::rc::Rc;
+use std::cell::RefCell;
 
-use crate::style;
+use crate::node::InternalNode;
 use crate::style::*;
+use crate::result;
+use crate::result::Result;
+
+use crate::ref_eq::ref_eq;
 
 use crate::number::Number::*;
 use crate::number::*;
 
-use crate::result::Result;
-
 use crate::geometry::{Point, Rect, Size};
 
 #[derive(Debug, Clone)]
 pub struct ComputeResult {
     pub size: Size<f32>,
-    pub children: Vec<layout::Node>,
+    pub children: Vec<result::Layout>,
 }
 
 struct FlexItem<'a> {
-    node: &'a style::Node,
+    node: &'a Rc<RefCell<InternalNode>>,
 
     size: Size<Number>,
     min_size: Size<Number>,
@@ -61,16 +63,16 @@ struct FlexLine<'a> {
     offset_cross: f32,
 }
 
-pub fn compute(root: &style::Node, size: Size<Number>) -> Result<layout::Node> {
-    let has_root_min_max = root.min_size.width.is_defined()
-        || root.min_size.height.is_defined()
-        || root.max_size.width.is_defined()
-        || root.max_size.height.is_defined();
+pub(crate) fn compute(root: &InternalNode, size: Size<Number>) -> Result<result::Layout> {
+    let has_root_min_max = root.style.min_size.width.is_defined()
+        || root.style.min_size.height.is_defined()
+        || root.style.max_size.width.is_defined()
+        || root.style.max_size.height.is_defined();
 
     let result = if has_root_min_max {
         let first_pass = compute_internal(
             root,
-            Size { width: root.size.width.resolve(size.width), height: root.size.height.resolve(size.height) },
+            Size { width: root.style.size.width.resolve(size.width), height: root.style.size.height.resolve(size.height) },
             size,
             false,
         )?;
@@ -81,14 +83,14 @@ pub fn compute(root: &style::Node, size: Size<Number>) -> Result<layout::Node> {
                 width: first_pass
                     .size
                     .width
-                    .maybe_max(root.min_size.width.resolve(size.width))
-                    .maybe_min(root.max_size.width.resolve(size.width))
+                    .maybe_max(root.style.min_size.width.resolve(size.width))
+                    .maybe_min(root.style.max_size.width.resolve(size.width))
                     .to_number(),
                 height: first_pass
                     .size
                     .height
-                    .maybe_max(root.min_size.height.resolve(size.height))
-                    .maybe_min(root.max_size.height.resolve(size.height))
+                    .maybe_max(root.style.min_size.height.resolve(size.height))
+                    .maybe_min(root.style.max_size.height.resolve(size.height))
                     .to_number(),
             },
             size,
@@ -97,13 +99,13 @@ pub fn compute(root: &style::Node, size: Size<Number>) -> Result<layout::Node> {
     } else {
         compute_internal(
             root,
-            Size { width: root.size.width.resolve(size.width), height: root.size.height.resolve(size.height) },
+            Size { width: root.style.size.width.resolve(size.width), height: root.style.size.height.resolve(size.height) },
             size,
             true,
         )?
     };
 
-    let mut layout = layout::Node {
+    let mut layout = result::Layout {
         order: 0,
         size: Size { width: result.size.width, height: result.size.height },
         location: Point { x: 0.0, y: 0.0 },
@@ -114,7 +116,7 @@ pub fn compute(root: &style::Node, size: Size<Number>) -> Result<layout::Node> {
     Ok(layout)
 }
 
-fn round_layout(layout: &mut layout::Node, abs_x: f32, abs_y: f32) {
+fn round_layout(layout: &mut result::Layout, abs_x: f32, abs_y: f32) {
     let abs_x = abs_x + layout.location.x;
     let abs_y = abs_y + layout.location.y;
 
@@ -126,7 +128,7 @@ fn round_layout(layout: &mut layout::Node, abs_x: f32, abs_y: f32) {
 }
 
 fn compute_internal(
-    node: &style::Node,
+    node: &InternalNode,
     node_size: Size<Number>,
     parent_size: Size<Number>,
     perform_layout: bool,
@@ -167,7 +169,7 @@ fn compute_internal(
 
         if let Some(ref measure) = node.measure {
             let result = ComputeResult { size: measure(node_size)?, children: vec![] };
-            node.layout_cache.replace(Some(LayoutCache {
+            node.layout_cache.replace(Some(result::Cache {
                 node_size,
                 parent_size,
                 perform_layout,
@@ -180,14 +182,14 @@ fn compute_internal(
     // Define some general constants we will need for the remainder
     // of the algorithm.
 
-    let dir = node.flex_direction;
+    let dir = node.style.flex_direction;
     let is_row = dir.is_row();
     let is_column = dir.is_column();
-    let is_wrap_reverse = node.flex_wrap == FlexWrap::WrapReverse;
+    let is_wrap_reverse = node.style.flex_wrap == FlexWrap::WrapReverse;
 
-    let margin = node.margin.map(|n| n.resolve(parent_size.width).or_else(0.0));
-    let padding = node.padding.map(|n| n.resolve(parent_size.width).or_else(0.0));
-    let border = node.border.map(|n| n.resolve(parent_size.width).or_else(0.0));
+    let margin = node.style.margin.map(|n| n.resolve(parent_size.width).or_else(0.0));
+    let padding = node.style.padding.map(|n| n.resolve(parent_size.width).or_else(0.0));
+    let border = node.style.border.map(|n| n.resolve(parent_size.width).or_else(0.0));
 
     let padding_border = Rect {
         start: padding.start + border.start,
@@ -224,30 +226,30 @@ fn compute_internal(
     let mut flex_items: Vec<FlexItem> = node
         .children
         .iter()
-        .filter(|child| child.position_type != PositionType::Absolute)
-        .filter(|child| child.display != Display::None)
+        .filter(|child| child.borrow().style.position_type != PositionType::Absolute)
+        .filter(|child| child.borrow().style.display != Display::None)
         .map(|child| FlexItem {
             node: child,
 
             size: Size {
-                width: child.size.width.resolve(node_inner_size.width),
-                height: child.size.height.resolve(node_inner_size.height),
+                width: child.borrow().style.size.width.resolve(node_inner_size.width),
+                height: child.borrow().style.size.height.resolve(node_inner_size.height),
             },
 
             min_size: Size {
-                width: child.min_size.width.resolve(node_inner_size.width),
-                height: child.min_size.height.resolve(node_inner_size.height),
+                width: child.borrow().style.min_size.width.resolve(node_inner_size.width),
+                height: child.borrow().style.min_size.height.resolve(node_inner_size.height),
             },
 
             max_size: Size {
-                width: child.max_size.width.resolve(node_inner_size.width),
-                height: child.max_size.height.resolve(node_inner_size.height),
+                width: child.borrow().style.max_size.width.resolve(node_inner_size.width),
+                height: child.borrow().style.max_size.height.resolve(node_inner_size.height),
             },
 
-            position: child.position.map(|p| p.resolve(node_inner_size.width)),
-            margin: child.margin.map(|m| m.resolve(node_inner_size.width).or_else(0.0)),
-            padding: child.padding.map(|p| p.resolve(node_inner_size.width).or_else(0.0)),
-            border: child.border.map(|b| b.resolve(node_inner_size.width).or_else(0.0)),
+            position: child.borrow().style.position.map(|p| p.resolve(node_inner_size.width)),
+            margin: child.borrow().style.margin.map(|m| m.resolve(node_inner_size.width).or_else(0.0)),
+            padding: child.borrow().style.padding.map(|p| p.resolve(node_inner_size.width).or_else(0.0)),
+            border: child.borrow().style.border.map(|b| b.resolve(node_inner_size.width).or_else(0.0)),
 
             flex_basis: 0.0,
             inner_flex_basis: 0.0,
@@ -271,7 +273,7 @@ fn compute_internal(
     flex_items.iter_mut().try_for_each(|child| -> Result<()> {
         // A. If the item has a definite used flex basis, that’s the flex base size.
 
-        let flex_basis = child.node.flex_basis.resolve(node_inner_size.main(dir));
+        let flex_basis = child.node.borrow().style.flex_basis.resolve(node_inner_size.main(dir));
         if flex_basis.is_defined() {
             child.flex_basis = flex_basis.or_else(0.0);
             return Ok(());
@@ -282,9 +284,9 @@ fn compute_internal(
         //    then the flex base size is calculated from its inner
         //    cross size and the flex item’s intrinsic aspect ratio.
 
-        if let Defined(ratio) = child.node.aspect_ratio {
+        if let Defined(ratio) = child.node.borrow().style.aspect_ratio {
             if let Defined(cross) = node_size.cross(dir) {
-                if child.node.flex_basis == Dimension::Auto {
+                if child.node.borrow().style.flex_basis == Dimension::Auto {
                     child.flex_basis = cross * ratio;
                     return Ok(());
                 }
@@ -315,7 +317,7 @@ fn compute_internal(
         //    flex item’s cross size. The flex base size is the item’s resulting main size.
 
         let width: Number = if !child.size.width.is_defined()
-            && child.node.align_self(node) == style::AlignSelf::Stretch
+            && child.node.borrow().style.align_self(&node.style) == AlignSelf::Stretch
             && is_column
         {
             available_space.width
@@ -324,14 +326,14 @@ fn compute_internal(
         };
 
         let height: Number =
-            if !child.size.height.is_defined() && child.node.align_self(node) == style::AlignSelf::Stretch && is_row {
+            if !child.size.height.is_defined() && child.node.borrow().style.align_self(&node.style) == AlignSelf::Stretch && is_row {
                 available_space.height
             } else {
                 child.size.height
             };
 
         child.flex_basis = compute_internal(
-            child.node,
+            &child.node.borrow(),
             Size {
                 width: width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
                 height: height.maybe_max(child.min_size.height).maybe_min(child.max_size.height),
@@ -358,7 +360,7 @@ fn compute_internal(
         // webkit handled various scenarios. Can probably be solved better by passing in
         // min-content max-content constraints from the top
         let min_main =
-            compute_internal(child.node, Size { width: Undefined, height: Undefined }, available_space, false)?
+            compute_internal(&child.node.borrow(), Size { width: Undefined, height: Undefined }, available_space, false)?
                 .size
                 .main(dir)
                 .maybe_max(child.min_size.main(dir))
@@ -395,7 +397,7 @@ fn compute_internal(
         let mut lines: Vec<FlexLine> = vec![];
         let mut line_length = 0.0;
 
-        if node.flex_wrap == FlexWrap::NoWrap {
+        if node.style.flex_wrap == FlexWrap::NoWrap {
             lines.push(FlexLine { items: flex_items, cross_size: 0.0, offset_cross: 0.0 });
         } else {
             let mut line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
@@ -449,7 +451,7 @@ fn compute_internal(
                 child.target_size.set_main(
                     dir,
                     compute_internal(
-                        child.node,
+                        &child.node.borrow(),
                         Size {
                             width: child.size.width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
                             height: child.size.height.maybe_max(child.min_size.height).maybe_min(child.max_size.height),
@@ -471,7 +473,7 @@ fn compute_internal(
 
             child.outer_target_size.set_main(dir, child.target_size.main(dir) + child.margin.main(dir));
 
-            if (child.node.flex_grow == 0.0 && child.node.flex_shrink == 0.0)
+            if (child.node.borrow().style.flex_grow == 0.0 && child.node.borrow().style.flex_shrink == 0.0)
                 || (growing && child.flex_basis > child.hypothetical_inner_size.main(dir))
                 || (shrinking && child.flex_basis < child.hypothetical_inner_size.main(dir))
             {
@@ -528,8 +530,8 @@ fn compute_internal(
                 })
                 .sum();
 
-            let sum_flex_grow: f32 = unfrozen.iter().map(|item| item.node.flex_grow).sum();
-            let sum_flex_shrink: f32 = unfrozen.iter().map(|item| item.node.flex_shrink).sum();
+            let sum_flex_grow: f32 = unfrozen.iter().map(|item| item.node.borrow().style.flex_grow).sum();
+            let sum_flex_shrink: f32 = unfrozen.iter().map(|item| item.node.borrow().style.flex_shrink).sum();
 
             let free_space = if growing && sum_flex_grow < 1.0 {
                 (initial_free_space * sum_flex_grow).maybe_min(node_inner_size.main(dir) - used_space)
@@ -563,15 +565,15 @@ fn compute_internal(
                     unfrozen.iter_mut().for_each(|child| {
                         child
                             .target_size
-                            .set_main(dir, child.flex_basis + free_space * (child.node.flex_grow / sum_flex_grow));
+                            .set_main(dir, child.flex_basis + free_space * (child.node.borrow().style.flex_grow / sum_flex_grow));
                     });
                 } else if shrinking && sum_flex_shrink > 0.0 {
                     let sum_scaled_shrink_factor: f32 =
-                        unfrozen.iter().map(|child| child.inner_flex_basis * child.node.flex_shrink).sum();
+                        unfrozen.iter().map(|child| child.inner_flex_basis * child.node.borrow().style.flex_shrink).sum();
 
                     if sum_scaled_shrink_factor > 0.0 {
                         unfrozen.iter_mut().for_each(|child| {
-                            let scaled_shrink_factor = child.inner_flex_basis * child.node.flex_shrink;
+                            let scaled_shrink_factor = child.inner_flex_basis * child.node.borrow().style.flex_shrink;
                             child.target_size.set_main(
                                 dir,
                                 child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor),
@@ -592,8 +594,8 @@ fn compute_internal(
                 // webkit handled various scenarios. Can probably be solved better by passing in
                 // min-content max-content constraints from the top. Need to figure out correct thing to do here as
                 // just piling on more conditionals.
-                let min_main = if is_row && child.node.measure.is_none() {
-                    compute_internal(child.node, Size { width: Undefined, height: Undefined }, available_space, false)?
+                let min_main = if is_row && child.node.borrow().measure.is_none() {
+                    compute_internal(&child.node.borrow(), Size { width: Undefined, height: Undefined }, available_space, false)?
                         .size
                         .width
                         .maybe_min(child.size.width)
@@ -665,7 +667,7 @@ fn compute_internal(
             child.hypothetical_inner_size.set_cross(
                 dir,
                 compute_internal(
-                    child.node,
+                    &child.node.borrow(),
                     Size {
                         width: if is_row { child.target_size.width.to_number() } else { child_cross },
                         height: if is_row { child_cross } else { child.target_size.height.to_number() },
@@ -693,7 +695,7 @@ fn compute_internal(
     // TODO - probably should move this somewhere else as it doesn't make a ton of sense here but we need it below
     // TODO - This is expensive and should only be done if we really require a baseline. aka, make it lazy
 
-    fn calc_baseline(layout: &layout::Node) -> f32 {
+    fn calc_baseline(layout: &result::Layout) -> f32 {
         if layout.children.is_empty() {
             layout.size.height
         } else {
@@ -704,7 +706,7 @@ fn compute_internal(
     flex_lines.iter_mut().try_for_each(|line| {
         line.items.iter_mut().try_for_each(|child| -> Result<()> {
             let result = compute_internal(
-                child.node,
+                &child.node.borrow(),
                 Size {
                     width: if is_row {
                         child.target_size.width.to_number()
@@ -724,7 +726,7 @@ fn compute_internal(
                 true,
             )?;
 
-            child.baseline = calc_baseline(&layout::Node {
+            child.baseline = calc_baseline(&result::Layout {
                 order: node.children.iter().position(|n| ref_eq(n, child.node)).unwrap() as u32,
                 size: result.size,
                 location: Point { x: 0.0, y: 0.0 },
@@ -764,10 +766,10 @@ fn compute_internal(
                 .items
                 .iter()
                 .map(|child| {
-                    if child.node.align_self(node) == AlignSelf::Baseline
-                        && child.node.cross_margin_start(dir) != Dimension::Auto
-                        && child.node.cross_margin_end(dir) != Dimension::Auto
-                        && child.node.cross_size(dir) == Dimension::Auto
+                    if child.node.borrow().style.align_self(&node.style) == AlignSelf::Baseline
+                        && child.node.borrow().style.cross_margin_start(dir) != Dimension::Auto
+                        && child.node.borrow().style.cross_margin_end(dir) != Dimension::Auto
+                        && child.node.borrow().style.cross_size(dir) == Dimension::Auto
                     {
                         max_baseline - child.baseline + child.hypothetical_outer_size.cross(dir)
                     } else {
@@ -784,7 +786,7 @@ fn compute_internal(
     //    by equal amounts such that the sum of their cross sizes exactly equals the
     //    flex container’s inner cross size.
 
-    if node.align_content == AlignContent::Stretch && node_size.cross(dir).is_defined() {
+    if node.style.align_content == AlignContent::Stretch && node_size.cross(dir).is_defined() {
         let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
         let inner_cross = (node_size.cross(dir) - padding_border.cross(dir)).or_else(0.0);
 
@@ -828,10 +830,10 @@ fn compute_internal(
         line.items.iter_mut().for_each(|child| {
             child.target_size.set_cross(
                 dir,
-                if child.node.align_self(node) == AlignSelf::Stretch
-                    && child.node.cross_margin_start(dir) != Dimension::Auto
-                    && child.node.cross_margin_end(dir) != Dimension::Auto
-                    && child.node.cross_size(dir) == Dimension::Auto
+                if child.node.borrow().style.align_self(&node.style) == AlignSelf::Stretch
+                    && child.node.borrow().style.cross_margin_start(dir) != Dimension::Auto
+                    && child.node.borrow().style.cross_margin_end(dir) != Dimension::Auto
+                    && child.node.borrow().style.cross_size(dir) == Dimension::Auto
                 {
                     (line_cross_size - child.margin.cross(dir))
                         .maybe_max(child.min_size.cross(dir))
@@ -859,10 +861,10 @@ fn compute_internal(
         let mut num_auto_margins = 0;
 
         line.items.iter_mut().for_each(|child| {
-            if child.node.main_margin_start(dir) == Dimension::Auto {
+            if child.node.borrow().style.main_margin_start(dir) == Dimension::Auto {
                 num_auto_margins += 1;
             }
-            if child.node.main_margin_end(dir) == Dimension::Auto {
+            if child.node.borrow().style.main_margin_end(dir) == Dimension::Auto {
                 num_auto_margins += 1;
             }
         });
@@ -871,14 +873,14 @@ fn compute_internal(
             let margin = free_space / num_auto_margins as f32;
 
             line.items.iter_mut().for_each(|child| {
-                if child.node.main_margin_start(dir) == Dimension::Auto {
+                if child.node.borrow().style.main_margin_start(dir) == Dimension::Auto {
                     if is_row {
                         child.margin.start = margin;
                     } else {
                         child.margin.top = margin;
                     }
                 }
-                if child.node.main_margin_end(dir) == Dimension::Auto {
+                if child.node.borrow().style.main_margin_end(dir) == Dimension::Auto {
                     if is_row {
                         child.margin.end = margin;
                     } else {
@@ -893,7 +895,7 @@ fn compute_internal(
             let justify_item = |(i, child): (usize, &mut FlexItem)| {
                 let is_first = i == 0;
 
-                child.offset_main = match node.justify_content {
+                child.offset_main = match node.style.justify_content {
                     JustifyContent::FlexStart => {
                         if layout_reverse && is_first {
                             free_space
@@ -958,8 +960,8 @@ fn compute_internal(
         line.items.iter_mut().for_each(|child| {
             let free_space = line_cross_size - child.outer_target_size.cross(dir);
 
-            if child.node.cross_margin_start(dir) == Dimension::Auto
-                && child.node.cross_margin_end(dir) == Dimension::Auto
+            if child.node.borrow().style.cross_margin_start(dir) == Dimension::Auto
+                && child.node.borrow().style.cross_margin_end(dir) == Dimension::Auto
             {
                 if is_row {
                     child.margin.top = free_space / 2.0;
@@ -968,13 +970,13 @@ fn compute_internal(
                     child.margin.start = free_space / 2.0;
                     child.margin.end = free_space / 2.0;
                 }
-            } else if child.node.cross_margin_start(dir) == Dimension::Auto {
+            } else if child.node.borrow().style.cross_margin_start(dir) == Dimension::Auto {
                 if is_row {
                     child.margin.top = free_space;
                 } else {
                     child.margin.start = free_space;
                 }
-            } else if child.node.cross_margin_end(dir) == Dimension::Auto {
+            } else if child.node.borrow().style.cross_margin_end(dir) == Dimension::Auto {
                 if is_row {
                     child.margin.bottom = free_space;
                 } else {
@@ -984,7 +986,7 @@ fn compute_internal(
                 // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
                 //     cross-axis margins are auto.
 
-                child.offset_cross = match child.node.align_self(node) {
+                child.offset_cross = match child.node.borrow().style.align_self(&node.style) {
                     AlignSelf::Auto => 0.0, // Should never happen
                     AlignSelf::FlexStart => {
                         if is_wrap_reverse {
@@ -1040,7 +1042,7 @@ fn compute_internal(
     // layout we are done now.
     if !perform_layout {
         let result = ComputeResult { size: container_size, children: vec![] };
-        node.layout_cache.replace(Some(LayoutCache { node_size, parent_size, perform_layout, result: result.clone() }));
+        node.layout_cache.replace(Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() }));
         return Ok(result);
     }
 
@@ -1052,7 +1054,7 @@ fn compute_internal(
     let align_line = |(i, line): (usize, &mut FlexLine)| {
         let is_first = i == 0;
 
-        line.offset_cross = match node.align_content {
+        line.offset_cross = match node.style.align_content {
             AlignContent::FlexStart => {
                 if is_first && is_wrap_reverse {
                     free_space
@@ -1099,18 +1101,18 @@ fn compute_internal(
     }
 
     // Do a final layout pass and gather the resulting layouts
-    let mut children: Vec<layout::Node> = {
-        let mut lines: Vec<Vec<layout::Node>> = vec![];
+    let mut children: Vec<result::Layout> = {
+        let mut lines: Vec<Vec<result::Layout>> = vec![];
         let mut total_offset_cross = padding_border.cross_start(dir);
 
         let layout_line = |line: &mut FlexLine| -> Result<()> {
-            let mut children: Vec<layout::Node> = vec![];
+            let mut children: Vec<result::Layout> = vec![];
             let mut total_offset_main = padding_border.main_start(dir);
             let line_offset_cross = line.offset_cross;
 
             let layout_item = |child: &mut FlexItem| -> Result<()> {
                 let result = compute_internal(
-                    child.node,
+                    &child.node.borrow(),
                     child.target_size.map(|s| s.to_number()),
                     container_size.map(|s| s.to_number()),
                     true,
@@ -1127,7 +1129,7 @@ fn compute_internal(
                     + child.margin.cross_start(dir)
                     + (child.position.cross_start(dir).or_else(0.0) - child.position.cross_end(dir).or_else(0.0));
 
-                children.push(layout::Node {
+                children.push(result::Layout {
                     order: node.children.iter().position(|n| ref_eq(n, child.node)).unwrap() as u32,
                     size: result.size,
                     location: Point {
@@ -1173,42 +1175,42 @@ fn compute_internal(
     };
 
     // Before returning we perform absolute layout on all absolutely positioned children
-    let absolute_children: Vec<Result<layout::Node>> = node
+    let absolute_children: Vec<Result<result::Layout>> = node
         .children
         .iter()
         .enumerate()
-        .filter(|(_, child)| child.position_type == PositionType::Absolute)
+        .filter(|(_, child)| child.borrow().style.position_type == PositionType::Absolute)
         .map(|(order, child)| {
             let container_width = container_size.width.to_number();
             let container_height = container_size.height.to_number();
 
-            let start = child.position.start.resolve(container_width) + child.margin.start.resolve(container_width);
-            let end = child.position.end.resolve(container_width) + child.margin.end.resolve(container_width);
-            let top = child.position.top.resolve(container_height) + child.margin.top.resolve(container_height);
+            let start = child.borrow().style.position.start.resolve(container_width) + child.borrow().style.margin.start.resolve(container_width);
+            let end = child.borrow().style.position.end.resolve(container_width) + child.borrow().style.margin.end.resolve(container_width);
+            let top = child.borrow().style.position.top.resolve(container_height) + child.borrow().style.margin.top.resolve(container_height);
             let bottom =
-                child.position.bottom.resolve(container_height) + child.margin.bottom.resolve(container_height);
+                child.borrow().style.position.bottom.resolve(container_height) + child.borrow().style.margin.bottom.resolve(container_height);
 
             let (start_main, end_main) = if is_row { (start, end) } else { (top, bottom) };
             let (start_cross, end_cross) = if is_row { (top, bottom) } else { (start, end) };
 
-            let width = child
+            let width = child.borrow().style
                 .size
                 .width
                 .resolve(container_width)
-                .maybe_max(child.min_size.width.resolve(container_width))
-                .maybe_min(child.max_size.width.resolve(container_width))
+                .maybe_max(child.borrow().style.min_size.width.resolve(container_width))
+                .maybe_min(child.borrow().style.max_size.width.resolve(container_width))
                 .or_else(if start.is_defined() && end.is_defined() {
                     container_width - start - end
                 } else {
                     Undefined
                 });
 
-            let height = child
+            let height = child.borrow().style
                 .size
                 .height
                 .resolve(container_height)
-                .maybe_max(child.min_size.height.resolve(container_height))
-                .maybe_min(child.max_size.height.resolve(container_height))
+                .maybe_max(child.borrow().style.min_size.height.resolve(container_height))
+                .maybe_min(child.borrow().style.max_size.height.resolve(container_height))
                 .or_else(if top.is_defined() && bottom.is_defined() {
                     container_height - top - bottom
                 } else {
@@ -1216,7 +1218,7 @@ fn compute_internal(
                 });
 
             let result = compute_internal(
-                child,
+                &child.borrow(),
                 Size { width, height },
                 Size { width: container_width, height: container_height },
                 true,
@@ -1226,22 +1228,22 @@ fn compute_internal(
                 - result
                     .size
                     .main(dir)
-                    .maybe_max(child.min_main_size(dir).resolve(node_inner_size.main(dir)))
-                    .maybe_min(child.max_main_size(dir).resolve(node_inner_size.main(dir)));
+                    .maybe_max(child.borrow().style.min_main_size(dir).resolve(node_inner_size.main(dir)))
+                    .maybe_min(child.borrow().style.max_main_size(dir).resolve(node_inner_size.main(dir)));
 
             let free_cross_space = container_size.cross(dir)
                 - result
                     .size
                     .cross(dir)
-                    .maybe_max(child.min_cross_size(dir).resolve(node_inner_size.cross(dir)))
-                    .maybe_min(child.max_cross_size(dir).resolve(node_inner_size.cross(dir)));
+                    .maybe_max(child.borrow().style.min_cross_size(dir).resolve(node_inner_size.cross(dir)))
+                    .maybe_min(child.borrow().style.max_cross_size(dir).resolve(node_inner_size.cross(dir)));
 
             let offset_main = if start_main.is_defined() {
                 start_main.or_else(0.0) + border.main_start(dir)
             } else if end_main.is_defined() {
                 free_main_space - end_main.or_else(0.0) - border.main_end(dir)
             } else {
-                match node.justify_content {
+                match node.style.justify_content {
                     JustifyContent::SpaceBetween | JustifyContent::FlexStart => padding_border.main_start(dir),
                     JustifyContent::FlexEnd => free_main_space - padding_border.main_end(dir),
                     JustifyContent::SpaceEvenly | JustifyContent::SpaceAround | JustifyContent::Center => {
@@ -1255,7 +1257,7 @@ fn compute_internal(
             } else if end_cross.is_defined() {
                 free_cross_space - end_cross.or_else(0.0) - border.cross_end(dir)
             } else {
-                match child.align_self(node) {
+                match child.borrow().style.align_self(&node.style) {
                     AlignSelf::Auto => 0.0, // Should never happen
                     AlignSelf::FlexStart => {
                         if is_wrap_reverse {
@@ -1283,7 +1285,7 @@ fn compute_internal(
                 }
             };
 
-            Ok(layout::Node {
+            Ok(result::Layout {
                 order: order as u32,
                 size: result.size,
                 location: Point {
@@ -1299,21 +1301,21 @@ fn compute_internal(
         children.push(abs_child?);
     }
 
-    fn hidden_layout(node: &style::Node, order: u32) -> layout::Node {
-        layout::Node {
+    fn hidden_layout(node: &InternalNode, order: u32) -> result::Layout {
+        result::Layout {
             order,
             size: Size { width: 0.0, height: 0.0 },
             location: Point { x: 0.0, y: 0.0 },
-            children: node.children.iter().enumerate().map(|(order, child)| hidden_layout(child, order as _)).collect(),
+            children: node.children.iter().enumerate().map(|(order, child)| hidden_layout(&child.borrow(), order as _)).collect(),
         }
     }
 
-    let mut hidden_children: Vec<layout::Node> = node
+    let mut hidden_children: Vec<result::Layout> = node
         .children
         .iter()
         .enumerate()
-        .filter(|(_, child)| child.display == Display::None)
-        .map(|(order, child)| hidden_layout(child, order as _))
+        .filter(|(_, child)| child.borrow().style.display == Display::None)
+        .map(|(order, child)| hidden_layout(&child.borrow(), order as _))
         .collect();
 
     children.append(&mut hidden_children);
@@ -1321,6 +1323,6 @@ fn compute_internal(
     children.sort_by(|c1, c2| c1.order.cmp(&c2.order));
 
     let result = ComputeResult { size: container_size, children };
-    node.layout_cache.replace(Some(LayoutCache { node_size, parent_size, perform_layout, result: result.clone() }));
+    node.layout_cache.replace(Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() }));
     Ok(result)
 }
diff --git a/src/layout.rs b/src/layout.rs
deleted file mode 100644
index 98f6798ae..000000000
--- a/src/layout.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#[cfg(not(feature = "std"))]
-use alloc::vec::Vec;
-
-use crate::geometry::{Point, Size};
-
-#[derive(Debug, Clone)]
-pub struct Node {
-    pub(crate) order: u32,
-    pub size: Size<f32>,
-    pub location: Point<f32>,
-    pub children: Vec<Node>,
-}
diff --git a/src/lib.rs b/src/lib.rs
index fc6369c67..3d5ca17b8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,11 +4,10 @@
 extern crate alloc;
 
 pub mod geometry;
-pub mod layout;
-pub mod number;
 pub mod result;
+pub mod number;
 pub mod style;
+pub mod node;
 
 mod algo;
 mod ref_eq;
-pub use crate::algo::compute;
diff --git a/src/node.rs b/src/node.rs
new file mode 100644
index 000000000..f66144d7f
--- /dev/null
+++ b/src/node.rs
@@ -0,0 +1,65 @@
+#[cfg(not(feature = "std"))]
+use alloc::{vec, vec::Vec};
+
+use std::rc::Rc;
+use std::rc::Weak;
+use std::cell::RefCell;
+
+use crate::style::*;
+use crate::algo;
+use crate::geometry::Size;
+use crate::number::Number;
+use crate::result::{Layout, Cache, Result};
+
+type MeasureFunc = Box<Fn(Size<Number>) -> Result<Size<f32>>>;
+
+pub(crate) struct InternalNode {
+    pub(crate) style: Style,
+    pub(crate) parents: Vec<Weak<RefCell<InternalNode>>>,
+    pub(crate) children: Vec<Rc<RefCell<InternalNode>>>,
+    pub(crate) measure: Option<MeasureFunc>,
+    pub(crate) layout_cache: RefCell<Option<Cache>>,
+}
+
+pub struct Node(Rc<RefCell<InternalNode>>);
+
+impl Node {
+    pub fn new(style: Style, children: Vec<&Node>) -> Node {
+        let mut parent = Node(Rc::new(RefCell::new(InternalNode {
+            style: style,
+            parents: vec![],
+            children: vec![],
+            measure: None,
+            layout_cache: RefCell::new(None),
+        })));
+        
+        for child in children {
+            parent.add_child(child);
+        }
+        
+        parent
+    }
+    
+    pub fn add_child(&mut self, child: &Node) {
+        child.0.borrow_mut().parents.push(Rc::downgrade(&self.0));
+        self.0.borrow_mut().children.push(Rc::clone(&child.0));
+    }
+    
+    // pub fn children(&self) -> Vec<Node> {}
+    
+    // pub fn mark_dirty(&mut self) {}
+    
+    // pub fn set_style(&mut self, Style) {}
+    
+    // pub fn set_children(&mut self, Vec<Node>) {}
+    
+    // pub fn add_child(&mut self, Node) {}
+    
+    // pub fn remove_child(&mut self, Node) -> Node {}
+    
+    // pub fn replace_child(&mut self, Node, u32) -> Node {}
+    
+    pub fn compute_layout(&self, size: Size<Number>) -> Result<Layout> {
+      algo::compute(&self.0.borrow(), size)
+    }
+}
diff --git a/src/result.rs b/src/result.rs
index c5d2510ad..5167e2fec 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -1,3 +1,27 @@
+#[cfg(not(feature = "std"))]
+use alloc::vec::Vec;
+
 use core::any::Any;
 
+use crate::geometry::{Point, Size};
+use crate::number::Number;
+use crate::algo::ComputeResult;
+
 pub type Result<T> = core::result::Result<T, Box<Any>>;
+
+#[derive(Debug, Clone)]
+pub struct Layout {
+    pub(crate) order: u32,
+    pub size: Size<f32>,
+    pub location: Point<f32>,
+    pub children: Vec<Layout>,
+}
+
+#[derive(Debug, Clone)]
+pub struct Cache {
+    pub node_size: Size<Number>,
+    pub parent_size: Size<Number>,
+    pub perform_layout: bool,
+
+    pub result: ComputeResult,
+}
diff --git a/src/style.rs b/src/style.rs
index 2ca373f71..6ac803685 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,12 +1,8 @@
 #[cfg(not(feature = "std"))]
 use alloc::boxed::Box;
-#[cfg(not(feature = "std"))]
-use alloc::{vec, vec::Vec};
 
-use crate::algo;
 use crate::geometry::{Rect, Size};
 use crate::number::Number;
-use crate::result::Result;
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum AlignItems {
@@ -206,96 +202,60 @@ impl Default for Size<Dimension> {
     }
 }
 
-type MeasureFunc = Box<Fn(Size<Number>) -> Result<Size<f32>>>;
-
-#[derive(Debug, Clone)]
-pub struct LayoutCache {
-    pub node_size: Size<Number>,
-    pub parent_size: Size<Number>,
-    pub perform_layout: bool,
-
-    pub result: algo::ComputeResult,
-}
-
-pub struct Node {
+#[derive(Debug)]
+pub struct Style {
     pub display: Display,
-
     pub position_type: PositionType,
     pub direction: Direction,
     pub flex_direction: FlexDirection,
-
     pub flex_wrap: FlexWrap,
     pub overflow: Overflow,
-
     pub align_items: AlignItems,
     pub align_self: AlignSelf,
     pub align_content: AlignContent,
-
     pub justify_content: JustifyContent,
-
     pub position: Rect<Dimension>,
     pub margin: Rect<Dimension>,
     pub padding: Rect<Dimension>,
     pub border: Rect<Dimension>,
-
     pub flex_grow: f32,
     pub flex_shrink: f32,
     pub flex_basis: Dimension,
-
     pub size: Size<Dimension>,
     pub min_size: Size<Dimension>,
     pub max_size: Size<Dimension>,
-
     pub aspect_ratio: Number,
-    pub measure: Option<MeasureFunc>,
-
-    pub children: Vec<Node>,
-
-    pub layout_cache: core::cell::RefCell<Option<LayoutCache>>,
 }
 
-impl Default for Node {
-    fn default() -> Node {
-        Node {
+impl Default for Style {
+    fn default() -> Style {
+        Style {
             display: Default::default(),
-
             position_type: Default::default(),
             direction: Default::default(),
             flex_direction: Default::default(),
-
             flex_wrap: Default::default(),
             overflow: Default::default(),
-
             align_items: Default::default(),
             align_self: Default::default(),
             align_content: Default::default(),
-
             justify_content: Default::default(),
-
             position: Default::default(),
             margin: Default::default(),
             padding: Default::default(),
             border: Default::default(),
-
             flex_grow: 0.0,
             flex_shrink: 1.0,
             flex_basis: Dimension::Auto,
-
             size: Default::default(),
             min_size: Default::default(),
             max_size: Default::default(),
-
             aspect_ratio: Default::default(),
-            measure: None,
-
-            children: vec![],
-
-            layout_cache: core::cell::RefCell::new(None),
         }
     }
 }
 
-impl Node {
+impl Style {
     pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension {
         match direction {
             FlexDirection::Row | FlexDirection::RowReverse => self.min_size.width,
@@ -359,7 +319,7 @@ impl Node {
         }
     }
 
-    pub(crate) fn align_self(&self, parent: &Node) -> AlignSelf {
+    pub(crate) fn align_self(&self, parent: &Style) -> AlignSelf {
         if self.align_self == AlignSelf::Auto {
             match parent.align_items {
                 AlignItems::FlexStart => AlignSelf::FlexStart,