Bläddra i källkod

feat: wire up stretch into place

Jonathan Kelley 3 år sedan
förälder
incheckning
dc34805ee6
14 ändrade filer med 1447 tillägg och 274 borttagningar
  1. 0 3
      .vscode/settings.json
  2. 8 7
      Cargo.toml
  3. 85 0
      examples/frame.rs
  4. 53 0
      examples/layouts.rs
  5. 31 0
      examples/list.rs
  6. 63 0
      examples/quadrants.rs
  7. 34 0
      examples/strecher.rs
  8. 85 0
      examples/text.rs
  9. 610 0
      src/attributes.rs
  10. 88 0
      src/layout.rs
  11. 146 264
      src/lib.rs
  12. 97 0
      src/render.rs
  13. 45 0
      test.html
  14. 102 0
      tests/margin.rs

+ 0 - 3
.vscode/settings.json

@@ -1,3 +0,0 @@
-{
-  "rust-analyzer.inlayHints.enable": true
-}

+ 8 - 7
Cargo.toml

@@ -6,12 +6,13 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-stretch = "0.3.2"
-tui = { version = "0.15.0", features = ["crossterm"] }
-crossterm = "0.20.0"
+tui = { version = "0.16.0", features = ["crossterm"] }
+crossterm = "0.22.1"
 anyhow = "1.0.42"
 thiserror = "1.0.24"
-dioxus = { path = "../../dioxus", features = ["core"] }
-hecs = "0.6.0"
-tui-template = { path = "../../Tinkering/tui-builder" }
-# tui-template = { git = "https://github.com/jkelleyrtp/tui-builder.git" }
+dioxus = { path = "../../dioxus" }
+hecs = "0.7.3"
+ctrlc = "3.2.1"
+bumpalo = { version = "3.8.0", features = ["boxed"] }
+stretch2 = { path = "../../Tinkering/stretch2" }
+# stretch2 = { git = "https://github.com/elbaro/stretch2.git" }

+ 85 - 0
examples/frame.rs

@@ -0,0 +1,85 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+            // justify_content: "center",
+            // align_items: "center",
+            // flex_direction: "row",
+            // background_color: "red",
+
+            p {
+                background_color: "black",
+                flex_direction: "column",
+                justify_content: "center",
+                align_items: "center",
+                // height: "10%",
+                "hi"
+                "hi"
+                "hi"
+            }
+
+            li {
+                background_color: "red",
+                flex_direction: "column",
+                justify_content: "center",
+                align_items: "center",
+                // height: "10%",
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+            }
+            li {
+                background_color: "blue",
+                flex_direction: "column",
+                justify_content: "center",
+                align_items: "center",
+                // height: "10%",
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+            }
+            p {
+                background_color: "yellow",
+                "asd"
+            }
+            p {
+                background_color: "green",
+                "asd"
+            }
+            p {
+                background_color: "white",
+                "asd"
+            }
+            p {
+                background_color: "cyan",
+                "asd"
+            }
+        }
+    })
+}

+ 53 - 0
examples/layouts.rs

@@ -0,0 +1,53 @@
+use std::collections::HashMap;
+
+use dioxus::{core::ElementId, prelude::*};
+use rink::TuiNode;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    let mut layout = stretch2::Stretch::new();
+    let mut nodes = HashMap::new();
+    rink::collect_layout(&mut layout, &mut nodes, &dom, dom.base_scope().root_node());
+
+    let node = nodes
+        .remove(&dom.base_scope().root_node().mounted_id())
+        .unwrap();
+
+    layout
+        .compute_layout(node.layout, stretch2::geometry::Size::undefined())
+        .unwrap();
+
+    for (id, node) in nodes.drain() {
+        println!("{:?}", layout.layout(node.layout));
+    }
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+
+            div {
+                "hi"
+            }
+            div {
+                "bi"
+                "bi"
+            }
+        }
+    })
+}
+
+// fn print_layout(mut nodes: HashMap<ElementId, TuiNode>, node: &VNode) {
+//     match node {
+//         VNode::Text(_) => todo!(),
+//         VNode::Element(_) => todo!(),
+//         VNode::Fragment(_) => todo!(),
+//         VNode::Component(_) => todo!(),
+//         VNode::Placeholder(_) => todo!(),
+//     }
+// }

+ 31 - 0
examples/list.rs

@@ -0,0 +1,31 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+            border_width: "1px",
+
+            h1 { height: "2px", color: "green",
+                "that's awesome!"
+            }
+
+            ul {
+                flex_direction: "column",
+                padding_left: "3px",
+                (0..10).map(|i| rsx!(
+                    "> hello {i}"
+                ))
+            }
+        }
+    })
+}

+ 63 - 0
examples/quadrants.rs

@@ -0,0 +1,63 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+
+            div {
+                width: "100%",
+                height: "50%",
+                flex_direction: "row",
+                div {
+                    border_width: "1px",
+                    width: "50%",
+                    height: "100%",
+                    background_color: "red",
+                    justify_content: "center",
+                    align_items: "center",
+                    "[A]"
+                }
+                div {
+                    width: "50%",
+                    height: "100%",
+                    background_color: "black",
+                    justify_content: "center",
+                    align_items: "center",
+                    "[B]"
+                }
+            }
+
+            div {
+                width: "100%",
+                height: "50%",
+                flex_direction: "row",
+                div {
+                    width: "50%",
+                    height: "100%",
+                    background_color: "green",
+                    justify_content: "center",
+                    align_items: "center",
+                    "[C]"
+                }
+                div {
+                    width: "50%",
+                    height: "100%",
+                    background_color: "blue",
+                    justify_content: "center",
+                    align_items: "center",
+                    "[D]"
+                }
+            }
+        }
+    })
+}

+ 34 - 0
examples/strecher.rs

@@ -0,0 +1,34 @@
+use stretch2::prelude::*;
+
+fn main() -> Result<(), Error> {
+    let mut stretch = Stretch::new();
+
+    let child = stretch.new_node(
+        Style {
+            size: Size {
+                width: Dimension::Percent(0.5),
+                height: Dimension::Auto,
+            },
+            ..Default::default()
+        },
+        &[],
+    )?;
+
+    let node = stretch.new_node(
+        Style {
+            size: Size {
+                width: Dimension::Points(100.0),
+                height: Dimension::Points(100.0),
+            },
+            justify_content: JustifyContent::Center,
+            ..Default::default()
+        },
+        &[child],
+    )?;
+
+    stretch.compute_layout(node, Size::undefined())?;
+    println!("node: {:#?}", stretch.layout(node)?);
+    println!("child: {:#?}", stretch.layout(child)?);
+
+    Ok(())
+}

+ 85 - 0
examples/text.rs

@@ -0,0 +1,85 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+            // justify_content: "center",
+            // align_items: "center",
+            // flex_direction: "row",
+            // background_color: "red",
+
+            p {
+                background_color: "black",
+                flex_direction: "column",
+                justify_content: "center",
+                align_items: "center",
+                // height: "10%",
+                "hi"
+                "hi"
+                "hi"
+            }
+
+            li {
+                background_color: "red",
+                flex_direction: "column",
+                justify_content: "center",
+                align_items: "center",
+                // height: "10%",
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+                "bib"
+            }
+            li {
+                background_color: "blue",
+                flex_direction: "column",
+                justify_content: "center",
+                align_items: "center",
+                // height: "10%",
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+                "zib"
+            }
+            p {
+                background_color: "yellow",
+                "asd"
+            }
+            p {
+                background_color: "green",
+                "asd"
+            }
+            p {
+                background_color: "white",
+                "asd"
+            }
+            p {
+                background_color: "cyan",
+                "asd"
+            }
+        }
+    })
+}

+ 610 - 0
src/attributes.rs

@@ -0,0 +1,610 @@
+/*
+- [ ] pub display: Display,
+- [ ] pub position_type: PositionType,
+- [ ] pub direction: Direction,
+
+- [x] pub flex_direction: FlexDirection,
+- [x] pub flex_wrap: FlexWrap,
+- [x] pub flex_grow: f32,
+- [x] pub flex_shrink: f32,
+- [x] pub flex_basis: Dimension,
+
+- [ ] pub overflow: Overflow,
+
+- [x] pub align_items: AlignItems,
+- [x] pub align_self: AlignSelf,
+- [x] pub align_content: AlignContent,
+
+- [ ] pub margin: Rect<Dimension>,
+- [ ] pub padding: Rect<Dimension>,
+
+- [x] pub justify_content: JustifyContent,
+- [ ] pub position: Rect<Dimension>,
+- [ ] pub border: Rect<Dimension>,
+- [ ] pub size: Size<Dimension>,
+
+- [ ] pub min_size: Size<Dimension>,
+- [ ] pub max_size: Size<Dimension>,
+- [ ] pub aspect_ratio: Number,
+*/
+
+use stretch2::{prelude::*, style::Style};
+use tui::style::Style as TuiStyle;
+
+pub struct StyleModifer {
+    pub style: Style,
+    pub tui_style: TuiStyle,
+}
+
+enum TuiModifier {
+    Text,
+}
+
+/// applies the entire html namespace defined in dioxus-html
+pub fn apply_attributes(
+    //
+    name: &str,
+    value: &str,
+    style: &mut StyleModifer,
+) {
+    match name {
+        "align-content"
+        | "align-items"
+        | "align-self" => apply_align(name, value, style),
+
+        "animation"
+        | "animation-delay"
+        | "animation-direction"
+        | "animation-duration"
+        | "animation-fill-mode"
+        | "animation-iteration-count"
+        | "animation-name"
+        | "animation-play-state"
+        | "animation-timing-function" => apply_animation(name, value, style),
+
+        "backface-visibility" => {}
+
+        "background"
+        | "background-attachment"
+        | "background-clip"
+        | "background-color"
+        | "background-image"
+        | "background-origin"
+        | "background-position"
+        | "background-repeat"
+        | "background-size" => apply_background(name, value, style),
+
+        "border"
+        | "border-bottom"
+        | "border-bottom-color"
+        | "border-bottom-left-radius"
+        | "border-bottom-right-radius"
+        | "border-bottom-style"
+        | "border-bottom-width"
+        | "border-collapse"
+        | "border-color"
+        | "border-image"
+        | "border-image-outset"
+        | "border-image-repeat"
+        | "border-image-slice"
+        | "border-image-source"
+        | "border-image-width"
+        | "border-left"
+        | "border-left-color"
+        | "border-left-style"
+        | "border-left-width"
+        | "border-radius"
+        | "border-right"
+        | "border-right-color"
+        | "border-right-style"
+        | "border-right-width"
+        | "border-spacing"
+        | "border-style"
+        | "border-top"
+        | "border-top-color"
+        | "border-top-left-radius"
+        | "border-top-right-radius"
+        | "border-top-style"
+        | "border-top-width"
+        | "border-width" => apply_border(name, value, style),
+
+        "bottom" => {}
+        "box-shadow" => {}
+        "box-sizing" => {}
+        "caption-side" => {}
+        "clear" => {}
+        "clip" => {}
+
+        "color" => {
+            // text color
+        }
+
+        "column-count"
+        | "column-fill"
+        | "column-gap"
+        | "column-rule"
+        | "column-rule-color"
+        | "column-rule-style"
+        | "column-rule-width"
+        | "column-span"
+        // add column-width
+        | "column-width" => apply_column(name, value, style),
+
+        "columns" => {}
+
+        "content" => {}
+        "counter-increment" => {}
+        "counter-reset" => {}
+
+        "cursor" => {}
+        "direction" => {}
+
+        "display" => apply_display(name, value, style),
+
+        "empty-cells" => {}
+
+        "flex"
+        | "flex-basis"
+        | "flex-direction"
+        | "flex-flow"
+        | "flex-grow"
+        | "flex-shrink"
+        | "flex-wrap" => apply_flex(name, value, style),
+
+        "float" => {}
+
+        "font"
+        | "font-family"
+        | "font-size"
+        | "font-size-adjust"
+        | "font-stretch"
+        | "font-style"
+        | "font-variant"
+        | "font-weight" => apply_font(name, value, style),
+
+        "height" => {
+            if value.ends_with("%") {
+                if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
+                    style.style.size.height = Dimension::Percent(pct / 100.0);
+                }
+            } else if value.ends_with("px") {
+                if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                    style.style.size.height = Dimension::Points(px);
+                }
+            }
+        }
+        "justify-content" => {
+            use JustifyContent::*;
+            style.style.justify_content = match value {
+                "flex-start" => FlexStart,
+                "flex-end" => FlexEnd,
+                "center" => Center,
+                "space-between" => SpaceBetween,
+                "space-around" => SpaceAround,
+                "space-evenly" => SpaceEvenly,
+                _ => FlexStart,
+            };
+        }
+        "left" => {}
+        "letter-spacing" => {}
+        "line-height" => {}
+
+        "list-style"
+        | "list-style-image"
+        | "list-style-position"
+        | "list-style-type" => {}
+
+        "margin"
+        | "margin-bottom"
+        | "margin-left"
+        | "margin-right"
+        | "margin-top" => apply_margin(name, value, style),
+
+        "max-height" => {}
+        "max-width" => {}
+        "min-height" => {}
+        "min-width" => {}
+
+        "opacity" => {}
+        "order" => {}
+        "outline" => {}
+
+        "outline-color"
+        | "outline-offset"
+        | "outline-style"
+        | "outline-width" => {}
+
+        "overflow"
+        | "overflow-x"
+        | "overflow-y" => apply_overflow(name, value, style),
+
+        "padding"
+        | "padding-bottom"
+        | "padding-left"
+        | "padding-right"
+        | "padding-top" => apply_padding(name, value, style),
+
+        "page-break-after"
+        | "page-break-before"
+        | "page-break-inside" => {}
+
+        "perspective"
+        | "perspective-origin" => {}
+
+        "position" => {}
+        "pointer-events" => {}
+        "quotes" => {}
+        "resize" => {}
+        "right" => {}
+        "tab-size" => {}
+        "table-layout" => {}
+
+        "text-align"
+        | "text-align-last"
+        | "text-decoration"
+        | "text-decoration-color"
+        | "text-decoration-line"
+        | "text-decoration-style"
+        | "text-indent"
+        | "text-justify"
+        | "text-overflow"
+        | "text-shadow"
+        | "text-transform" => apply_text(name, value, style),
+
+        "top" => {}
+
+        "transform"
+        | "transform-origin"
+        | "transform-style" => apply_transform(name, value, style),
+
+        "transition"
+        | "transition-delay"
+        | "transition-duration"
+        | "transition-property"
+        | "transition-timing-function" => apply_transition(name, value, style),
+
+        "vertical-align" => {}
+        "visibility" => {}
+        "white-space" => {}
+        "width" => {
+            if value.ends_with("%") {
+                if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
+                    style.style.size.width = Dimension::Percent(pct / 100.0);
+                }
+            } else if value.ends_with("px") {
+                if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                    style.style.size.width = Dimension::Points(px);
+                }
+            }
+        }
+        "word-break" => {}
+        "word-spacing" => {}
+        "word-wrap" => {}
+        "z-index" => {}
+        _ => {}
+    }
+}
+
+fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        // todo: add more overflow support to stretch2
+        "overflow" | "overflow-x" | "overflow-y" => {
+            style.style.overflow = match value {
+                "auto" => Overflow::Visible,
+                "hidden" => Overflow::Hidden,
+                "scroll" => Overflow::Scroll,
+                "visible" => Overflow::Visible,
+                _ => Overflow::Visible,
+            };
+        }
+        _ => {}
+    }
+}
+
+fn apply_display(name: &str, value: &str, style: &mut StyleModifer) {
+    use stretch2::style::Display;
+    style.style.display = match value {
+        "flex" => Display::Flex,
+        "block" => Display::None,
+        _ => Display::Flex,
+    }
+
+    // TODO: there are way more variants
+    // stretch needs to be updated to handle them
+    //
+    // "block" => Display::Block,
+    // "inline" => Display::Inline,
+    // "inline-block" => Display::InlineBlock,
+    // "inline-table" => Display::InlineTable,
+    // "list-item" => Display::ListItem,
+    // "run-in" => Display::RunIn,
+    // "table" => Display::Table,
+    // "table-caption" => Display::TableCaption,
+    // "table-cell" => Display::TableCell,
+    // "table-column" => Display::TableColumn,
+    // "table-column-group" => Display::TableColumnGroup,
+    // "table-footer-group" => Display::TableFooterGroup,
+    // "table-header-group" => Display::TableHeaderGroup,
+    // "table-row" => Display::TableRow,
+    // "table-row-group" => Display::TableRowGroup,
+    // "none" => Display::None,
+    // _ => Display::Inline,
+}
+
+fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        "background-color" => {
+            use tui::style::Color;
+            match value {
+                "red" => style.tui_style.bg.replace(Color::Red),
+                "green" => style.tui_style.bg.replace(Color::Green),
+                "blue" => style.tui_style.bg.replace(Color::Blue),
+                "yellow" => style.tui_style.bg.replace(Color::Yellow),
+                "cyan" => style.tui_style.bg.replace(Color::Cyan),
+                "magenta" => style.tui_style.bg.replace(Color::Magenta),
+                "white" => style.tui_style.bg.replace(Color::White),
+                "black" => style.tui_style.bg.replace(Color::Black),
+                _ => None,
+            };
+        }
+        "background" => {}
+        "background-attachment" => {}
+        "background-clip" => {}
+        "background-image" => {}
+        "background-origin" => {}
+        "background-position" => {}
+        "background-repeat" => {}
+        "background-size" => {}
+        _ => {}
+    }
+}
+
+fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        "border" => {}
+        "border-bottom" => {}
+        "border-bottom-color" => {}
+        "border-bottom-left-radius" => {}
+        "border-bottom-right-radius" => {}
+        "border-bottom-style" => {}
+        "border-bottom-width" => {}
+        "border-collapse" => {}
+        "border-color" => {}
+        "border-image" => {}
+        "border-image-outset" => {}
+        "border-image-repeat" => {}
+        "border-image-slice" => {}
+        "border-image-source" => {}
+        "border-image-width" => {}
+        "border-left" => {}
+        "border-left-color" => {}
+        "border-left-style" => {}
+        "border-left-width" => {}
+        "border-radius" => {}
+        "border-right" => {}
+        "border-right-color" => {}
+        "border-right-style" => {}
+        "border-right-width" => {}
+        "border-spacing" => {}
+        "border-style" => {}
+        "border-top" => {}
+        "border-top-color" => {}
+        "border-top-left-radius" => {}
+        "border-top-right-radius" => {}
+        "border-top-style" => {}
+        "border-top-width" => {}
+        "border-width" => {
+            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                // tuistyle = px;
+            }
+        }
+        _ => {}
+    }
+}
+
+fn apply_animation(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        "animation" => {}
+        "animation-delay" => {}
+        "animation-direction =>{}" => {}
+        "animation-duration" => {}
+        "animation-fill-mode" => {}
+        "animation-itera =>{}tion-count" => {}
+        "animation-name" => {}
+        "animation-play-state" => {}
+        "animation-timing-function" => {}
+        _ => {}
+    }
+}
+
+fn apply_column(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        "column-count" => {}
+        "column-fill" => {}
+        "column-gap" => {}
+        "column-rule" => {}
+        "column-rule-color" => {}
+        "column-rule-style" => {}
+        "column-rule-width" => {}
+        "column-span" => {}
+        "column-width" => {}
+        _ => {}
+    }
+}
+
+fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
+    // - [x] pub flex_direction: FlexDirection,
+    // - [x] pub flex_wrap: FlexWrap,
+    // - [x] pub flex_grow: f32,
+    // - [x] pub flex_shrink: f32,
+    // - [x] pub flex_basis: Dimension,
+
+    match name {
+        "flex" => {}
+        "flex-direction" => {
+            use FlexDirection::*;
+            style.style.flex_direction = match value {
+                "row" => Row,
+                "row-reverse" => RowReverse,
+                "column" => Column,
+                "column-reverse" => ColumnReverse,
+                _ => Row,
+            };
+        }
+        "flex-basis" => {
+            if value.ends_with("%") {
+                if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
+                    style.style.flex_basis = Dimension::Percent(pct / 100.0);
+                }
+            } else if value.ends_with("px") {
+                if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                    style.style.flex_basis = Dimension::Points(px);
+                }
+            }
+        }
+        "flex-flow" => {}
+        "flex-grow" => {
+            if let Ok(val) = value.parse::<f32>() {
+                style.style.flex_grow = val;
+            }
+        }
+        "flex-shrink" => {
+            if let Ok(px) = value.parse::<f32>() {
+                style.style.flex_shrink = px;
+            }
+        }
+        "flex-wrap" => {
+            use FlexWrap::*;
+            style.style.flex_wrap = match value {
+                "nowrap" => NoWrap,
+                "wrap" => Wrap,
+                "wrap-reverse" => WrapReverse,
+                _ => NoWrap,
+            };
+        }
+        _ => {}
+    }
+}
+
+fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
+    todo!()
+}
+
+fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
+    // // left
+    // start: stretch::style::Dimension::Points(10f32),
+
+    // // right?
+    // end: stretch::style::Dimension::Points(10f32),
+
+    // // top?
+    // // top: stretch::style::Dimension::Points(10f32),
+
+    // // bottom?
+    // // bottom: stretch::style::Dimension::Points(10f32),
+
+    match name {
+        "padding" => {
+            if name.ends_with("px") {
+                if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                    style.style.padding.bottom = Dimension::Points(px);
+                    style.style.padding.top = Dimension::Points(px);
+                    style.style.padding.start = Dimension::Points(px);
+                    style.style.padding.end = Dimension::Points(px);
+                }
+            } else if name.ends_with("%") {
+                if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
+                    //
+                }
+            }
+        }
+        "padding-bottom" => {
+            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                style.style.padding.bottom = Dimension::Points(px);
+            }
+        }
+        "padding-left" => {
+            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                style.style.padding.start = Dimension::Points(px);
+            }
+        }
+        "padding-right" => {
+            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                style.style.padding.end = Dimension::Points(px);
+            }
+        }
+        "padding-top" => {
+            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+                style.style.padding.top = Dimension::Points(px);
+            }
+        }
+        _ => {}
+    }
+}
+
+fn apply_text(name: &str, value: &str, style: &mut StyleModifer) {
+    todo!()
+}
+
+fn apply_transform(name: &str, value: &str, style: &mut StyleModifer) {
+    todo!()
+}
+
+fn apply_transition(name: &str, value: &str, style: &mut StyleModifer) {
+    todo!()
+}
+
+fn apply_align(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        "align-items" => {
+            use AlignItems::*;
+            style.style.align_items = match value {
+                "flex-start" => FlexStart,
+                "flex-end" => FlexEnd,
+                "center" => Center,
+                "baseline" => Baseline,
+                "stretch" => Stretch,
+                _ => FlexStart,
+            };
+        }
+        "align-content" => {
+            use AlignContent::*;
+            style.style.align_content = match value {
+                "flex-start" => FlexStart,
+                "flex-end" => FlexEnd,
+                "center" => Center,
+                "space-between" => SpaceBetween,
+                "space-around" => SpaceAround,
+                _ => FlexStart,
+            };
+        }
+        "align-self" => {
+            use AlignSelf::*;
+            style.style.align_self = match value {
+                "auto" => Auto,
+                "flex-start" => FlexStart,
+                "flex-end" => FlexEnd,
+                "center" => Center,
+                "baseline" => Baseline,
+                "stretch" => Stretch,
+                _ => Auto,
+            };
+        }
+        _ => {}
+    }
+}
+
+pub fn apply_size(name: &str, value: &str, style: &mut StyleModifer) {
+    //
+}
+
+pub fn apply_margin(name: &str, value: &str, style: &mut StyleModifer) {
+    match name {
+        "margin" => {}
+        "margin-bottom" => {}
+        "margin-left" => {}
+        "margin-right" => {}
+        "margin-top" => {}
+        _ => {}
+    }
+}

+ 88 - 0
src/layout.rs

@@ -0,0 +1,88 @@
+use dioxus::core::*;
+use std::collections::HashMap;
+use tui::style::Style as TuiStyle;
+
+use crate::{
+    attributes::{apply_attributes, StyleModifer},
+    TuiNode,
+};
+
+pub fn collect_layout<'a>(
+    layout: &mut stretch2::Stretch,
+    nodes: &mut HashMap<ElementId, TuiNode<'a>>,
+    vdom: &'a VirtualDom,
+    node: &'a VNode<'a>,
+) {
+    use stretch2::prelude::*;
+
+    match node {
+        VNode::Text(t) => {
+            //
+            let id = t.id.get().unwrap();
+            let char_len = t.text.chars().count();
+
+            let style = Style {
+                size: Size {
+                    // characters are 1 point tall
+                    height: Dimension::Points(1.0),
+
+                    // text is as long as it is declared
+                    width: Dimension::Points(char_len as f32),
+                },
+
+                ..Default::default()
+            };
+
+            nodes.insert(
+                id,
+                TuiNode {
+                    node,
+                    block_style: tui::style::Style::default(),
+                    layout: layout.new_node(style, &[]).unwrap(),
+                },
+            );
+        }
+        VNode::Element(el) => {
+            // gather up all the styles from the attribute list
+            let mut modifier = StyleModifer {
+                style: Style::default(),
+                tui_style: TuiStyle::default(),
+            };
+
+            for &Attribute { name, value, .. } in el.attributes {
+                apply_attributes(name, value, &mut modifier);
+            }
+
+            // Layout the children
+            for child in el.children {
+                collect_layout(layout, nodes, vdom, child);
+            }
+
+            // Set all direct nodes as our children
+            let mut child_layout = vec![];
+            for el in el.children {
+                let ite = ElementIdIterator::new(vdom, el);
+                for node in ite {
+                    child_layout.push(nodes[&node.mounted_id()].layout)
+                }
+            }
+
+            nodes.insert(
+                node.mounted_id(),
+                TuiNode {
+                    node,
+                    block_style: modifier.tui_style,
+                    layout: layout.new_node(modifier.style, &child_layout).unwrap(),
+                },
+            );
+        }
+        VNode::Fragment(el) => {
+            //
+            for child in el.children {
+                collect_layout(layout, nodes, vdom, child);
+            }
+        }
+        VNode::Component(_) => todo!(),
+        VNode::Placeholder(_) => todo!(),
+    };
+}

+ 146 - 264
src/lib.rs

@@ -1,270 +1,152 @@
 use anyhow::Result;
+use crossterm::{
+    event::{DisableMouseCapture, EnableMouseCapture},
+    execute,
+    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
+};
 use dioxus::core::*;
-use tui::backend::CrosstermBackend;
-use tui::layout::{Layout, Rect};
-use tui::style::{Modifier, Style as TuiStyle};
-use tui::text::{Span, Spans};
-use tui::widgets::{Block, Paragraph};
-use tui_template::tuiapp::CrosstermFrame;
-struct RinkDom {
-    vdom: VirtualDom,
+use std::{collections::HashMap, io};
+use stretch2::{prelude::Size, Stretch};
+use tui::{backend::CrosstermBackend, style::Style as TuiStyle, Terminal};
+
+mod attributes;
+mod layout;
+mod render;
+
+pub use attributes::*;
+pub use layout::*;
+pub use render::*;
+
+pub struct TuiNode<'a> {
+    pub layout: stretch2::node::Node,
+    pub block_style: TuiStyle,
+    pub node: &'a VNode<'a>,
 }
 
-impl RinkDom {
-    pub fn new(app: FC<()>) -> Self {
-        Self {
-            vdom: VirtualDom::new(app),
-        }
-    }
-
-    fn render_vnode<'a>(
-        &self,
-        f: &mut CrosstermFrame,
-        node: &'a VNode<'a>,
-        state: &mut RenderState<'a>,
-    ) -> Rect {
-        match &node.kind {
-            VNodeKind::Fragment(_) => todo!(),
-            VNodeKind::Component(_) => todo!(),
-            VNodeKind::Suspended { node } => todo!(),
-
-            VNodeKind::Text(te) => {
-                let span = Span::styled(te.text, TuiStyle::default());
-
-                let mut m = Modifier::empty();
-
-                for style in &state.current_styles {
-                    match style {
-                        Styles::Bold => m = m | Modifier::BOLD,
-                        Styles::Italic => m = m | Modifier::ITALIC,
-                        Styles::Strikethrough => m = m | Modifier::CROSSED_OUT,
-                        Styles::Emphasis => m = m | Modifier::ITALIC,
-                        Styles::Underline => m = m | Modifier::UNDERLINED,
-                    }
-                }
-
-                let style = TuiStyle::default().add_modifier(m);
-                let span = span.styled_graphemes(style);
-                let cur_block = state.block_stack.last_mut().unwrap();
-
-                // Paragraph
-
-                // f.render_widget(span);
-            }
-            VNodeKind::Element(el) => {
-                //
-                let mut new_layout = false;
-
-                // all of our supported styles
-
-                match el.tag_name {
-                    // obviously semantically not really correct
-                    "div" => {
-                        state.layouts.push(Layout::default());
-                        new_layout = true;
-                    }
-
-                    "title" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => {
-                        let mut block = state.block_stack.pop().unwrap();
-                        let children = el.children;
-
-                        if let (1, Some(VNodeKind::Text(te))) =
-                            (children.len(), children.get(0).map(|f| &f.kind))
-                        {
-                            block = block.title(vec![Span::from(te.text)]);
-                        }
-
-                        state.block_stack.push(block);
-                    }
-
-                    "span" | "header" => {}
-
-                    "footer" => {}
-
-                    "p" => {
-                        state.layouts.push(Layout::default());
-                        new_layout = true;
-                    }
-
-                    // elements that style for whatever reason
-                    "em" => state.current_styles.push(Styles::Emphasis),
-                    "i" => state.current_styles.push(Styles::Italic),
-                    "b" => state.current_styles.push(Styles::Bold),
-                    "u" => state.current_styles.push(Styles::Underline),
-                    "strike" => state.current_styles.push(Styles::Strikethrough),
-
-                    "li" => {}
-                    "ul" => {}
-                    "ol" => {}
-                    "code" => {}
-                    "hr" => {}
-
-                    // Tables
-                    "table" => {}
-                    "tr" => {}
-                    "th" => {}
-                    "td" => {}
-
-                    // Inputs
-                    "input" => {}
-                    "label" => {}
-
-                    _ => {}
-                }
-
-                let cur_layout = state.layouts.last_mut().unwrap();
-                let cur_block = state.block_stack.last_mut().unwrap();
-                let mut cur_style = TuiStyle::default();
-
-                for attr in el.attributes {
-                    if attr.namespace == Some("style") {
-                        match attr.name {
-                            "width" => {}
-                            "height" => {}
-
-                            "background" => {
-                                //
-                                // cur_style.bg
-                                // cur_block.style()
-                            }
-                            "background-color" => {}
-
-                            "border" => {}
-                            "border-bottom" => {}
-                            "border-bottom-color" => {}
-                            "border-bottom-style" => {}
-                            "border-bottom-width" => {}
-                            "border-color" => {}
-                            "border-left" => {}
-                            "border-left-color" => {}
-                            "border-left-style" => {}
-                            "border-left-width" => {}
-                            "border-right" => {}
-                            "border-right-color" => {}
-                            "border-right-style" => {}
-                            "border-right-width" => {}
-                            "border-style" => {}
-                            "border-top" => {}
-                            "border-top-color" => {}
-                            "border-top-style" => {}
-                            "border-top-width" => {}
-                            "border-width" => {}
-
-                            "clear" => {}
-                            "clip" => {}
-                            "color" => {}
-                            "cursor" => {}
-                            "display" => {}
-                            "filter" => {}
-                            "float" => {}
-                            "font" => {}
-                            "font-family" => {}
-                            "font-size" => {}
-                            "font-variant" => {}
-                            "font-weight" => {}
-
-                            "left" => {}
-                            "letter-spacing" => {}
-                            "line-height" => {}
-                            "list-style" => {}
-                            "list-style-image" => {}
-                            "list-style-position" => {}
-                            "list-style-type" => {}
-                            "margin" => {}
-                            "margin-bottom" => {}
-                            "margin-left" => {}
-                            "margin-right" => {}
-                            "margin-top" => {}
-                            "overflow" => {}
-                            "padding" => {}
-                            "padding-bottom" => {}
-                            "padding-left" => {}
-                            "padding-right" => {}
-                            "padding-top" => {}
-                            "position" => {}
-                            "stroke-dasharray" => {}
-                            "stroke-dashoffset" => {}
-                            "text-align" => {}
-                            "text-decoration" => {}
-                            "text-indent" => {}
-                            "text-transform" => {}
-                            "top" => {}
-                            "vertical-align" => {}
-                            "visibility" => {}
-                            "z-index" => {}
-
-                            "page-break-after"
-                            | "page-break-before"
-                            | "background-position"
-                            | "background-attachment"
-                            | "background-image"
-                            | "background-repeat"
-                            | _ => {}
-                        }
-                    }
-                }
-
-                for child in el.children {}
-            }
-        }
-        Rect::new(0, 0, 0, 0)
-    }
-
-    fn render_text(&self, f: &mut CrosstermFrame, node: &VNode) {}
-
-    fn render_fragment(&self, f: &mut CrosstermFrame) {}
+pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
+    /*
+     -> collect all the nodes with their layout
+     -> solve their layout
+     -> render the nodes in the right place with tui/crosstream
+     -> while rendering, apply styling
+
+     use simd to compare lines for diffing?
+
+    */
+    let mut layout = Stretch::new();
+    let mut nodes = HashMap::new();
+
+    let root_node = vdom.base_scope().root_node();
+    layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
+
+    /*
+    Get the terminal to calcualte the layout from
+    */
+    enable_raw_mode().unwrap();
+    ctrlc::set_handler(move || {
+        disable_raw_mode().unwrap();
+    })
+    .expect("Error setting Ctrl-C handler");
+    let mut stdout = std::io::stdout();
+    execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
+    let backend = CrosstermBackend::new(io::stdout());
+    let mut terminal = Terminal::new(backend).unwrap();
+
+    let dims = terminal.size().unwrap();
+    let width = dims.width;
+    let height = dims.height;
+
+    /*
+    Compute the layout given th terminal size
+    */
+    let node_id = root_node.try_mounted_id().unwrap();
+    let root_layout = nodes[&node_id].layout;
+    layout.compute_layout(
+        root_layout,
+        Size {
+            width: stretch2::prelude::Number::Defined(width as f32),
+            height: stretch2::prelude::Number::Defined(height as f32),
+        },
+    )?;
+
+    terminal.draw(|frame| {
+        //
+        render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
+        assert!(nodes.is_empty());
+    })?;
+
+    std::thread::sleep(std::time::Duration::from_millis(5000));
+
+    disable_raw_mode()?;
+    execute!(
+        terminal.backend_mut(),
+        LeaveAlternateScreen,
+        DisableMouseCapture
+    )?;
+    terminal.show_cursor()?;
+
+    Ok(())
 }
 
-impl<'a> tui_template::tuiapp::TuiApp for RinkDom {
-    fn render(&mut self, frame: &mut CrosstermFrame) {
-        let base_scope = self.vdom.base_scope();
-        let root = base_scope.root();
-
-        let mut render_state = RenderState::new();
-        self.render_vnode(frame, root, &mut render_state);
-    }
-
-    fn event_handler(&self, action: tui_template::crossterm::event::Event) -> Result<()> {
-        todo!()
-    }
-
-    fn handle_key(&mut self, key: tui_template::crossterm::event::KeyEvent) {
-        todo!()
-    }
-
-    fn tick(&mut self) {
-        todo!()
-    }
-
-    fn should_quit(&self) -> bool {
-        todo!()
-    }
-}
-struct RenderState<'a> {
-    block_stack: Vec<Block<'a>>,
-
-    layouts: Vec<Layout>,
-
-    /// All the current styles applied through the "style" tag
-    current_styles: Vec<Styles>,
-}
-
-// I don't think we can do any of these?
-enum Styles {
-    Bold,
-    Italic,
-    Strikethrough,
-    Emphasis,
-    Underline,
-}
-
-impl<'a> RenderState<'a> {
-    fn new() -> Self {
-        let block_stack = Vec::new();
-        Self {
-            block_stack,
-            current_styles: Vec::new(),
-            layouts: Vec::new(),
-        }
-    }
-}
+// enum InputEvent {
+//     UserInput(KeyEvent),
+//     Close,
+//     Tick,
+// }
+
+// // Setup input handling
+// let (tx, rx) = mpsc::channel();
+// let tick_rate = Duration::from_millis(100);
+// thread::spawn(move || {
+//     let mut last_tick = Instant::now();
+//     loop {
+//         // poll for tick rate duration, if no events, sent tick event.
+//         let timeout = tick_rate
+//             .checked_sub(last_tick.elapsed())
+//             .unwrap_or_else(|| Duration::from_secs(0));
+
+//         if event::poll(timeout).unwrap() {
+//             if let TermEvent::Key(key) = event::read().unwrap() {
+//                 tx.send(InputEvent::UserInput(key)).unwrap();
+//             }
+//         }
+
+//         // if last_tick.elapsed() >= tick_rate {
+//         //     tx.send(InputEvent::Tick).unwrap();
+//         //     last_tick = Instant::now();
+//         // }
+//     }
+// });
+
+// terminal.clear()?;
+
+// // loop {
+// terminal.draw(|frame| {
+//     // draw the frame
+//     let size = frame.size();
+//     let root_node = vdom.base_scope().root_node();
+//     let block = render_vnode(frame, vdom, root_node);
+
+//     frame.render_widget(block, size);
+// })?;
+
+// // // terminal.draw(|f| ui::draw(f, &mut app))?;
+// match rx.recv()? {
+//     InputEvent::UserInput(event) => match event.code {
+//         KeyCode::Char('q') => {
+//             disable_raw_mode()?;
+//             execute!(
+//                 terminal.backend_mut(),
+//                 LeaveAlternateScreen,
+//                 DisableMouseCapture
+//             )?;
+//             terminal.show_cursor()?;
+//             // break;
+//         }
+//         _ => {} // handle event
+//     },
+//     InputEvent::Tick => {} // tick
+//     InputEvent::Close => {
+//         // break;
+//     }
+// };

+ 97 - 0
src/render.rs

@@ -0,0 +1,97 @@
+use anyhow::Result;
+use crossterm::{
+    event,
+    event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
+    execute,
+    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
+};
+use dioxus::core::*;
+use std::{
+    collections::HashMap,
+    io::{self, Stdout},
+    sync::mpsc,
+    thread,
+    time::{Duration, Instant},
+};
+use stretch2::{
+    geometry::Point,
+    prelude::{Layout, Size},
+    style::Style as StretchStyle,
+    Stretch,
+};
+use tui::{
+    backend::CrosstermBackend,
+    buffer::Buffer,
+    layout::Rect,
+    style::Style as TuiStyle,
+    text::Span,
+    widgets::{canvas::Label, Block, BorderType, Borders, Widget},
+    Terminal,
+};
+
+use crate::TuiNode;
+
+pub fn render_vnode<'a>(
+    frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
+    layout: &Stretch,
+    layouts: &mut HashMap<ElementId, TuiNode<'a>>,
+    vdom: &'a VirtualDom,
+    node: &'a VNode<'a>,
+) {
+    match node {
+        VNode::Fragment(f) => {
+            for child in f.children {
+                render_vnode(frame, layout, layouts, vdom, child);
+            }
+            return;
+        }
+        VNode::Component(_) => todo!(),
+
+        VNode::Placeholder(_) | VNode::Element(_) | VNode::Text(_) => {}
+    }
+
+    let id = node.try_mounted_id().unwrap();
+    let node = layouts.remove(&id).unwrap();
+
+    let Layout { location, size, .. } = layout.layout(node.layout).unwrap();
+
+    let Point { x, y } = location;
+    let Size { width, height } = size;
+
+    match node.node {
+        VNode::Text(t) => {
+            #[derive(Default)]
+            struct Label<'a> {
+                text: &'a str,
+            }
+
+            impl<'a> Widget for Label<'a> {
+                fn render(self, area: Rect, buf: &mut Buffer) {
+                    buf.set_string(area.left(), area.top(), self.text, TuiStyle::default());
+                }
+            }
+
+            // let s = Span::raw(t.text);
+
+            // Block::default().
+
+            let label = Label { text: t.text };
+            let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
+
+            frame.render_widget(label, area);
+        }
+        VNode::Element(el) => {
+            let block = Block::default().style(node.block_style);
+            let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
+
+            frame.render_widget(block, area);
+
+            for el in el.children {
+                render_vnode(frame, layout, layouts, vdom, el);
+            }
+        }
+        VNode::Fragment(_) => todo!(),
+        VNode::Component(_) => todo!(),
+        VNode::Placeholder(_) => todo!(),
+    }
+}

+ 45 - 0
test.html

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>Test</title>
+    <style>
+        html,
+        body {
+            height: 100%;
+        }
+        
+        .container {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            /* margin: auto; */
+        }
+        
+        .smaller {
+            height: 50%;
+            justify-content: center;
+            align-items: center;
+            color: violet;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="container">
+        <div class="smaller">
+            hello world
+            <div style="color: red;">
+                goodbye
+                <div>
+                    asdasdasd
+                </div>
+            </div>
+        </div>
+    </div>
+</body>
+
+</html>

+ 102 - 0
tests/margin.rs

@@ -0,0 +1,102 @@
+use stretch2 as stretch;
+
+#[test]
+fn margin_and_flex_row() {
+    let mut stretch = stretch::Stretch::new();
+    let node0 = stretch
+        .new_node(
+            stretch::style::Style {
+                flex_grow: 1f32,
+                margin: stretch::geometry::Rect {
+                    start: stretch::style::Dimension::Points(10f32),
+                    end: stretch::style::Dimension::Points(10f32),
+                    ..Default::default()
+                },
+                ..Default::default()
+            },
+            &[],
+        )
+        .unwrap();
+    let node = stretch
+        .new_node(
+            stretch::style::Style {
+                size: stretch::geometry::Size {
+                    width: stretch::style::Dimension::Points(100f32),
+                    height: stretch::style::Dimension::Points(100f32),
+                    ..Default::default()
+                },
+                ..Default::default()
+            },
+            &[node0],
+        )
+        .unwrap();
+    stretch
+        .compute_layout(node, stretch::geometry::Size::undefined())
+        .unwrap();
+    assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
+    assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
+    assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
+    assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
+    assert_eq!(stretch.layout(node0).unwrap().size.width, 80f32);
+    assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
+    assert_eq!(stretch.layout(node0).unwrap().location.x, 10f32);
+    assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
+}
+
+#[test]
+fn margin_and_flex_row2() {
+    let mut stretch = stretch::Stretch::new();
+    let node0 = stretch
+        .new_node(
+            stretch::style::Style {
+                flex_grow: 1f32,
+                margin: stretch::geometry::Rect {
+                    // left
+                    start: stretch::style::Dimension::Points(10f32),
+
+                    // right?
+                    end: stretch::style::Dimension::Points(10f32),
+
+                    // top?
+                    // top: stretch::style::Dimension::Points(10f32),
+
+                    // bottom?
+                    // bottom: stretch::style::Dimension::Points(10f32),
+                    ..Default::default()
+                },
+                ..Default::default()
+            },
+            &[],
+        )
+        .unwrap();
+
+    let node = stretch
+        .new_node(
+            stretch::style::Style {
+                size: stretch::geometry::Size {
+                    width: stretch::style::Dimension::Points(100f32),
+                    height: stretch::style::Dimension::Points(100f32),
+                    ..Default::default()
+                },
+                ..Default::default()
+            },
+            &[node0],
+        )
+        .unwrap();
+
+    stretch
+        .compute_layout(node, stretch::geometry::Size::undefined())
+        .unwrap();
+
+    assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
+    assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
+    assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
+    assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
+
+    dbg!(stretch.layout(node0));
+
+    // assert_eq!(stretch.layout(node0).unwrap().size.width, 80f32);
+    // assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
+    // assert_eq!(stretch.layout(node0).unwrap().location.x, 10f32);
+    // assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
+}