Przeglądaj źródła

added border and more attributes

Evan Almloff 3 lat temu
rodzic
commit
f6ef9981dd
7 zmienionych plików z 741 dodań i 111 usunięć
  1. 1 1
      Cargo.toml
  2. 30 0
      examples/border.rs
  3. 26 7
      examples/text.rs
  4. 345 91
      src/attributes.rs
  5. 4 1
      src/layout.rs
  6. 9 1
      src/lib.rs
  7. 326 10
      src/render.rs

+ 1 - 1
Cargo.toml

@@ -6,7 +6,7 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-tui = { version = "0.16.0", features = ["crossterm"], default-features = false }
+tui = "0.17.0"
 crossterm = "0.22.1"
 anyhow = "1.0.42"
 thiserror = "1.0.24"

+ 30 - 0
examples/border.rs

@@ -0,0 +1,30 @@
+use dioxus::prelude::*;
+
+fn main() {
+    rink::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+    let (radius, set_radius) = use_state(&cx, || 0);
+
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            justify_content: "center",
+            align_items: "center",
+            background_color: "hsl(248, 53%, 58%)",
+            onwheel: move |w| set_radius((radius + w.delta_y as i8).abs()),
+
+            // the border can either be solid, double, thick, OR rounded
+            // if multable are set only the last style is appiled
+            // to skip a side set the style to none
+            border_style: "solid none solid double",
+            border_width: "thick",
+            border_radius: "{radius}px",
+            border_color: "#0000FF #FF00FF #FF0000 #00FF00",
+
+            "{radius}"
+        }
+    })
+}

+ 26 - 7
examples/text.rs

@@ -53,13 +53,6 @@ fn app(cx: Scope) -> Element {
                 "zib"
                 "zib"
                 "zib"
-                "zib"
-                "zib"
-                "zib"
-                "zib"
-                "zib"
-                "zib"
-                "zib"
             }
             p {
                 background_color: "yellow",
@@ -77,6 +70,32 @@ fn app(cx: Scope) -> Element {
                 background_color: "cyan",
                 "asd"
             }
+            div {
+                font_weight: "bold",
+                color: "#666666",
+                p{
+                    "bold"
+                }
+                p {
+                    font_weight: "normal",
+                    " normal"
+                }
+            }
+            p {
+                font_style: "italic",
+                color: "red",
+                "italic"
+            }
+            p {
+                text_decoration: "underline",
+                color: "rgb(50, 100, 255)",
+                "underline"
+            }
+            p {
+                text_decoration: "line-through",
+                color: "hsl(10, 100%, 70%)",
+                "line-through"
+            }
         }
     })
 }

+ 345 - 91
src/attributes.rs

@@ -30,15 +30,155 @@
 */
 
 use stretch2::{prelude::*, style::PositionType, style::Style};
-use tui::style::Style as TuiStyle;
+use tui::style::{Color, Style as TuiStyle};
 
 pub struct StyleModifer {
     pub style: Style,
     pub tui_style: TuiStyle,
+    pub tui_modifier: TuiModifier,
 }
 
-enum TuiModifier {
-    Text,
+pub struct TuiModifier {
+    // border arrays start at the top and proceed clockwise
+    pub border_colors: [Option<Color>; 4],
+    pub border_types: [BorderType; 4],
+    pub border_widths: [UnitSystem; 4],
+    pub border_radi: [UnitSystem; 4],
+}
+
+#[derive(Clone, Copy)]
+pub enum BorderType {
+    DOTTED,
+    DASHED,
+    SOLID,
+    DOUBLE,
+    GROOVE,
+    RIDGE,
+    INSET,
+    OUTSET,
+    HIDDEN,
+    NONE,
+}
+
+impl Default for TuiModifier {
+    fn default() -> Self {
+        Self {
+            border_colors: [None; 4],
+            border_types: [BorderType::NONE; 4],
+            border_widths: [UnitSystem::Point(0.0); 4],
+            border_radi: [UnitSystem::Point(0.0); 4],
+        }
+    }
+}
+
+fn parse_color(color: &str) -> Option<tui::style::Color> {
+    match color {
+        "red" => Some(Color::Red),
+        "green" => Some(Color::Green),
+        "blue" => Some(Color::Blue),
+        "yellow" => Some(Color::Yellow),
+        "cyan" => Some(Color::Cyan),
+        "magenta" => Some(Color::Magenta),
+        "white" => Some(Color::White),
+        "black" => Some(Color::Black),
+        _ => {
+            if color.len() == 7 && color.starts_with('#') {
+                let mut values = [0, 0, 0];
+                let mut color_ok = true;
+                for i in 0..values.len() {
+                    if let Ok(v) = u8::from_str_radix(&color[(1 + 2 * i)..(1 + 2 * (i + 1))], 16) {
+                        values[i] = v;
+                    } else {
+                        color_ok = false;
+                    }
+                }
+                if color_ok {
+                    Some(Color::Rgb(values[0], values[1], values[2]))
+                } else {
+                    None
+                }
+            } else if color.starts_with("rgb(") {
+                let mut values = [0, 0, 0];
+                let mut color_ok = true;
+                for (v, i) in color[4..]
+                    .trim_end_matches(')')
+                    .split(',')
+                    .zip(0..values.len())
+                {
+                    if let Ok(v) = v.trim().parse() {
+                        values[i] = v;
+                    } else {
+                        color_ok = false;
+                    }
+                }
+                if color_ok {
+                    Some(Color::Rgb(values[0], values[1], values[2]))
+                } else {
+                    None
+                }
+            } else if color.starts_with("hsl(") {
+                let mut values = [0, 0, 0];
+                let mut color_ok = true;
+                for (v, i) in color[4..]
+                    .trim_end_matches(')')
+                    .split(',')
+                    .zip(0..values.len())
+                {
+                    if let Ok(v) = v.trim_end_matches('%').trim().parse() {
+                        values[i] = v;
+                    } else {
+                        color_ok = false;
+                    }
+                }
+                if color_ok {
+                    let [h, s, l] = [
+                        values[0] as f32 / 360.0,
+                        values[1] as f32 / 100.0,
+                        values[2] as f32 / 100.0,
+                    ];
+                    let rgb = if s == 0.0 {
+                        [l as u8; 3]
+                    } else {
+                        fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
+                            if t < 0.0 {
+                                t += 1.0;
+                            }
+                            if t > 1.0 {
+                                t -= 1.0;
+                            }
+                            if t < 1.0 / 6.0 {
+                                p + (q - p) * 6.0 * t
+                            } else if t < 1.0 / 2.0 {
+                                q
+                            } else if t < 2.0 / 3.0 {
+                                p + (q - p) * (2.0 / 3.0 - t) * 6.0
+                            } else {
+                                p
+                            }
+                        }
+
+                        let q = if l < 0.5 {
+                            l * (1.0 + s)
+                        } else {
+                            l + s - l * s
+                        };
+                        let p = 2.0 * l - q;
+                        [
+                            (hue_to_rgb(p, q, h + 1.0 / 3.0) * 255.0) as u8,
+                            (hue_to_rgb(p, q, h) * 255.0) as u8,
+                            (hue_to_rgb(p, q, h - 1.0 / 3.0) * 255.0) as u8,
+                        ]
+                    };
+
+                    Some(Color::Rgb(rgb[0], rgb[1], rgb[2]))
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        }
+    }
 }
 
 /// applies the entire html namespace defined in dioxus-html
@@ -117,7 +257,9 @@ pub fn apply_attributes(
         "clip" => {}
 
         "color" => {
-            // text color
+            if let Some(c) = parse_color(value) {
+                style.tui_style.fg.replace(c);
+            }
         }
 
         "column-count"
@@ -170,14 +312,11 @@ pub fn apply_attributes(
         | "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);
-                }
+            if let Some(v) = parse_value(value){
+                style.style.size.height = match v {
+                    UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
+                    UnitSystem::Point(v)=> Dimension::Points(v),
+                };
             }
         }
         "justify-content" => {
@@ -286,14 +425,11 @@ pub fn apply_attributes(
         "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);
-                }
+            if let Some(v) = parse_value(value){
+                style.style.size.width = match v {
+                    UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
+                    UnitSystem::Point(v)=> Dimension::Points(v),
+                };
             }
         }
         "word-break" => {}
@@ -304,7 +440,8 @@ pub fn apply_attributes(
     }
 }
 
-enum UnitSystem {
+#[derive(Clone, Copy)]
+pub enum UnitSystem {
     Percent(f32),
     Point(f32),
 }
@@ -344,7 +481,6 @@ fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
 }
 
 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,
@@ -376,40 +512,9 @@ fn apply_display(_name: &str, value: &str, style: &mut StyleModifer) {
 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),
-                _ => {
-                    if value.len() == 7 {
-                        let mut values = [0, 0, 0];
-                        let mut color_ok = true;
-                        for i in 0..values.len() {
-                            if let Ok(v) =
-                                u8::from_str_radix(&value[(1 + 2 * i)..(1 + 2 * (i + 1))], 16)
-                            {
-                                values[i] = v;
-                            } else {
-                                color_ok = false;
-                            }
-                        }
-                        if color_ok {
-                            let color = Color::Rgb(values[0], values[1], values[2]);
-                            style.tui_style.bg.replace(color)
-                        } else {
-                            None
-                        }
-                    } else {
-                        None
-                    }
-                }
-            };
+            if let Some(c) = parse_color(value) {
+                style.tui_style.bg.replace(c);
+            }
         }
         "background" => {}
         "background-attachment" => {}
@@ -423,17 +528,63 @@ fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
     }
 }
 
-fn apply_border(name: &str, value: &str, _style: &mut StyleModifer) {
+fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
+    fn parse_border_type(v: &str) -> BorderType {
+        match v {
+            "dotted" => BorderType::DOTTED,
+            "dashed" => BorderType::DASHED,
+            "solid" => BorderType::SOLID,
+            "double" => BorderType::DOUBLE,
+            "groove" => BorderType::GROOVE,
+            "ridge" => BorderType::RIDGE,
+            "inset" => BorderType::INSET,
+            "outset" => BorderType::OUTSET,
+            "none" => BorderType::NONE,
+            "hidden" => BorderType::HIDDEN,
+            _ => todo!(),
+        }
+    }
     match name {
         "border" => {}
         "border-bottom" => {}
-        "border-bottom-color" => {}
-        "border-bottom-left-radius" => {}
-        "border-bottom-right-radius" => {}
-        "border-bottom-style" => {}
-        "border-bottom-width" => {}
+        "border-bottom-color" => {
+            if let Some(c) = parse_color(value) {
+                style.tui_modifier.border_colors[2] = Some(c);
+            }
+        }
+        "border-bottom-left-radius" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_radi[2] = v;
+            }
+        }
+        "border-bottom-right-radius" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_radi[1] = v;
+            }
+        }
+        "border-bottom-style" => style.tui_modifier.border_types[2] = parse_border_type(value),
+        "border-bottom-width" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_widths[2] = v;
+            }
+        }
         "border-collapse" => {}
-        "border-color" => {}
+        "border-color" => {
+            let values: Vec<_> = value.split(' ').collect();
+            if values.len() == 1 {
+                if let Some(c) = parse_color(values[0]) {
+                    for i in 0..4 {
+                        style.tui_modifier.border_colors[i] = Some(c);
+                    }
+                }
+            } else {
+                for (i, v) in values.into_iter().enumerate() {
+                    if let Some(c) = parse_color(v) {
+                        style.tui_modifier.border_colors[i] = Some(c);
+                    }
+                }
+            }
+        }
         "border-image" => {}
         "border-image-outset" => {}
         "border-image-repeat" => {}
@@ -441,28 +592,89 @@ fn apply_border(name: &str, value: &str, _style: &mut StyleModifer) {
         "border-image-source" => {}
         "border-image-width" => {}
         "border-left" => {}
-        "border-left-color" => {}
-        "border-left-style" => {}
-        "border-left-width" => {}
-        "border-radius" => {}
+        "border-left-color" => {
+            if let Some(c) = parse_color(value) {
+                style.tui_modifier.border_colors[3] = Some(c);
+            }
+        }
+        "border-left-style" => style.tui_modifier.border_types[3] = parse_border_type(value),
+        "border-left-width" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_widths[3] = v;
+            }
+        }
+        "border-radius" => {
+            let values: Vec<_> = value.split(' ').collect();
+            if values.len() == 1 {
+                if let Some(r) = parse_value(values[0]) {
+                    for i in 0..4 {
+                        style.tui_modifier.border_radi[i] = r;
+                    }
+                }
+            } else {
+                for (i, v) in values.into_iter().enumerate() {
+                    if let Some(r) = parse_value(v) {
+                        style.tui_modifier.border_radi[i] = r;
+                    }
+                }
+            }
+        }
         "border-right" => {}
-        "border-right-color" => {}
-        "border-right-style" => {}
-        "border-right-width" => {}
+        "border-right-color" => {
+            if let Some(c) = parse_color(value) {
+                style.tui_modifier.border_colors[1] = Some(c);
+            }
+        }
+        "border-right-style" => style.tui_modifier.border_types[1] = parse_border_type(value),
+        "border-right-width" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_widths[1] = v;
+            }
+        }
         "border-spacing" => {}
-        "border-style" => {}
+        "border-style" => {
+            let values: Vec<_> = value.split(' ').collect();
+            if values.len() == 1 {
+                let border = parse_border_type(values[0]);
+                for i in 0..4 {
+                    style.tui_modifier.border_types[i] = border;
+                }
+            } else {
+                for (i, v) in values.into_iter().enumerate() {
+                    style.tui_modifier.border_types[i] = parse_border_type(v);
+                }
+            }
+        }
         "border-top" => {}
-        "border-top-color" => {}
-        "border-top-left-radius" => {}
-        "border-top-right-radius" => {}
-        "border-top-style" => {}
-        "border-top-width" => {}
+        "border-top-color" => {
+            if let Some(c) = parse_color(value) {
+                style.tui_modifier.border_colors[0] = Some(c);
+            }
+        }
+        "border-top-left-radius" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_radi[3] = v;
+            }
+        }
+        "border-top-right-radius" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_radi[0] = v;
+            }
+        }
+        "border-top-style" => style.tui_modifier.border_types[0] = parse_border_type(value),
+        "border-top-width" => {
+            if let Some(v) = parse_value(value) {
+                style.tui_modifier.border_widths[0] = v;
+            }
+        }
         "border-width" => {
-            if let Ok(_px) = value.trim_end_matches("px").parse::<f32>() {
-                // tuistyle = px;
+            if let Some(v) = parse_value(value) {
+                for i in 0..4 {
+                    style.tui_modifier.border_widths[i] = v;
+                }
             }
         }
-        _ => {}
+        _ => (),
     }
 }
 
@@ -516,14 +728,11 @@ fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
             };
         }
         "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);
-                }
+            if let Some(v) = parse_value(value) {
+                style.style.flex_basis = match v {
+                    UnitSystem::Percent(v) => Dimension::Percent(v / 100.0),
+                    UnitSystem::Point(v) => Dimension::Points(v),
+                };
             }
         }
         "flex-flow" => {}
@@ -550,8 +759,27 @@ fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
     }
 }
 
-fn apply_font(_name: &str, _value: &str, _style: &mut StyleModifer) {
-    todo!()
+fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
+    use tui::style::Modifier;
+    match name {
+        "font" => todo!(),
+        "font-family" => todo!(),
+        "font-size" => todo!(),
+        "font-size-adjust" => todo!(),
+        "font-stretch" => todo!(),
+        "font-style" => match value {
+            "italic" => style.tui_style = style.tui_style.add_modifier(Modifier::ITALIC),
+            "oblique" => style.tui_style = style.tui_style.add_modifier(Modifier::ITALIC),
+            _ => (),
+        },
+        "font-variant" => todo!(),
+        "font-weight" => match value {
+            "bold" => style.tui_style = style.tui_style.add_modifier(Modifier::BOLD),
+            "normal" => style.tui_style = style.tui_style.remove_modifier(Modifier::BOLD),
+            _ => (),
+        },
+        _ => (),
+    }
 }
 
 fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
@@ -587,8 +815,34 @@ fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
     }
 }
 
-fn apply_text(_name: &str, _value: &str, _style: &mut StyleModifer) {
-    todo!()
+fn apply_text(name: &str, value: &str, style: &mut StyleModifer) {
+    use tui::style::Modifier;
+
+    match name {
+        "text-align" => todo!(),
+        "text-align-last" => todo!(),
+        "text-decoration" | "text-decoration-line" => {
+            for v in value.split(' ') {
+                match v {
+                    "line-through" => {
+                        style.tui_style = style.tui_style.add_modifier(Modifier::CROSSED_OUT)
+                    }
+                    "underline" => {
+                        style.tui_style = style.tui_style.add_modifier(Modifier::UNDERLINED)
+                    }
+                    _ => (),
+                }
+            }
+        }
+        "text-decoration-color" => todo!(),
+        "text-decoration-style" => todo!(),
+        "text-indent" => todo!(),
+        "text-justify" => todo!(),
+        "text-overflow" => todo!(),
+        "text-shadow" => todo!(),
+        "text-transform" => todo!(),
+        _ => todo!(),
+    }
 }
 
 fn apply_transform(_name: &str, _value: &str, _style: &mut StyleModifer) {

+ 4 - 1
src/layout.rs

@@ -4,7 +4,7 @@ use tui::style::Style as TuiStyle;
 
 use crate::{
     attributes::{apply_attributes, StyleModifer},
-    TuiNode,
+    TuiModifier, TuiNode,
 };
 
 /*
@@ -42,6 +42,7 @@ pub fn collect_layout<'a>(
                 TuiNode {
                     node,
                     block_style: tui::style::Style::default(),
+                    tui_modifier: TuiModifier::default(),
                     layout: layout.new_node(style, &[]).unwrap(),
                 },
             );
@@ -51,6 +52,7 @@ pub fn collect_layout<'a>(
             let mut modifier = StyleModifer {
                 style: Style::default(),
                 tui_style: TuiStyle::default(),
+                tui_modifier: TuiModifier::default(),
             };
 
             for &Attribute { name, value, .. } in el.attributes {
@@ -86,6 +88,7 @@ pub fn collect_layout<'a>(
                 TuiNode {
                     node,
                     block_style: modifier.tui_style,
+                    tui_modifier: modifier.tui_modifier,
                     layout: layout.new_node(modifier.style, &child_layout).unwrap(),
                 },
             );

+ 9 - 1
src/lib.rs

@@ -43,6 +43,7 @@ pub fn launch(app: Component<()>) {
 pub struct TuiNode<'a> {
     pub layout: stretch2::node::Node,
     pub block_style: TuiStyle,
+    pub tui_modifier: TuiModifier,
     pub node: &'a VNode<'a>,
 }
 
@@ -132,7 +133,14 @@ pub fn render_vdom(
 
                     // resolve events before rendering
                     events = handler.get_events(vdom, &layout, &mut nodes, root_node);
-                    render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
+                    render::render_vnode(
+                        frame,
+                        &layout,
+                        &mut nodes,
+                        vdom,
+                        root_node,
+                        &TuiStyle::default(),
+                    );
                     assert!(nodes.is_empty());
                 })?;
 

+ 326 - 10
src/render.rs

@@ -9,11 +9,320 @@ use tui::{
     backend::CrosstermBackend,
     buffer::Buffer,
     layout::Rect,
-    style::Style as TuiStyle,
-    widgets::{Block, Widget},
+    style::{Color, Style as TuiStyle},
+    widgets::Widget,
 };
 
-use crate::TuiNode;
+use crate::{BorderType, TuiNode, UnitSystem};
+
+const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
+
+impl<'a> Widget for TuiNode<'a> {
+    fn render(self, area: Rect, buf: &mut Buffer) {
+        use tui::symbols::line::*;
+
+        enum Direction {
+            Left,
+            Right,
+            Up,
+            Down,
+        }
+
+        fn draw(
+            buf: &mut Buffer,
+            points_history: [[i32; 2]; 3],
+            symbols: &Set,
+            pos: [u16; 2],
+            color: &Option<Color>,
+        ) {
+            let [before, current, after] = points_history;
+            let start_dir = match [before[0] - current[0], before[1] - current[1]] {
+                [1, 0] => Direction::Right,
+                [-1, 0] => Direction::Left,
+                [0, 1] => Direction::Down,
+                [0, -1] => Direction::Up,
+                [a, b] => {
+                    panic!(
+                        "draw({:?} {:?} {:?}) {}, {} no cell adjacent",
+                        before, current, after, a, b
+                    )
+                }
+            };
+            let end_dir = match [after[0] - current[0], after[1] - current[1]] {
+                [1, 0] => Direction::Right,
+                [-1, 0] => Direction::Left,
+                [0, 1] => Direction::Down,
+                [0, -1] => Direction::Up,
+                [a, b] => {
+                    panic!(
+                        "draw({:?} {:?} {:?}) {}, {} no cell adjacent",
+                        before, current, after, a, b
+                    )
+                }
+            };
+
+            let cell = buf.get_mut(
+                (current[0] + pos[0] as i32) as u16,
+                (current[1] + pos[1] as i32) as u16,
+            );
+            if let Some(c) = color {
+                cell.fg = *c;
+            }
+            cell.symbol = match [start_dir, end_dir] {
+                [Direction::Down, Direction::Up] => symbols.vertical,
+                [Direction::Down, Direction::Right] => symbols.top_left,
+                [Direction::Down, Direction::Left] => symbols.top_right,
+                [Direction::Up, Direction::Down] => symbols.vertical,
+                [Direction::Up, Direction::Right] => symbols.bottom_left,
+                [Direction::Up, Direction::Left] => symbols.bottom_right,
+                [Direction::Right, Direction::Left] => symbols.horizontal,
+                [Direction::Right, Direction::Up] => symbols.bottom_left,
+                [Direction::Right, Direction::Down] => symbols.top_left,
+                [Direction::Left, Direction::Up] => symbols.bottom_right,
+                [Direction::Left, Direction::Right] => symbols.horizontal,
+                [Direction::Left, Direction::Down] => symbols.top_right,
+                _ => panic!(
+                    "{:?} {:?} {:?} cannont connect cell to itself",
+                    before, current, after
+                ),
+            }
+            .to_string();
+        }
+
+        fn draw_arc(
+            pos: [u16; 2],
+            starting_angle: f32,
+            arc_angle: f32,
+            radius: f32,
+            symbols: &Set,
+            buf: &mut Buffer,
+            color: &Option<Color>,
+        ) {
+            if radius < 0.0 {
+                return;
+            }
+
+            let num_points = (radius * arc_angle) as i32;
+            let starting_point = [
+                (starting_angle.cos() * (radius * RADIUS_MULTIPLIER[0])) as i32,
+                (starting_angle.sin() * (radius * RADIUS_MULTIPLIER[1])) as i32,
+            ];
+            let mut points_history = [
+                [0, 0],
+                {
+                    // change the x or y value based on which one is changing quicker
+                    let ddx = -starting_angle.sin();
+                    let ddy = starting_angle.cos();
+                    if ddx.abs() > ddy.abs() {
+                        [starting_point[0] - ddx.signum() as i32, starting_point[1]]
+                    } else {
+                        [starting_point[0], starting_point[1] - ddy.signum() as i32]
+                    }
+                },
+                starting_point,
+            ];
+
+            for i in 1..=num_points {
+                let angle = (i as f32 / num_points as f32) * arc_angle + starting_angle;
+                let x = angle.cos() * radius * RADIUS_MULTIPLIER[0];
+                let y = angle.sin() * radius * RADIUS_MULTIPLIER[1];
+                let new = [x as i32, y as i32];
+
+                if new != points_history[2] {
+                    points_history = [points_history[1], points_history[2], new];
+
+                    let dx = points_history[2][0] - points_history[1][0];
+                    let dy = points_history[2][1] - points_history[1][1];
+                    // fill diagonals
+                    if dx != 0 && dy != 0 {
+                        let connecting_point = match [dx, dy] {
+                            [1, 1] => [points_history[1][0] + 1, points_history[1][1]],
+                            [1, -1] => [points_history[1][0], points_history[1][1] - 1],
+                            [-1, 1] => [points_history[1][0], points_history[1][1] + 1],
+                            [-1, -1] => [points_history[1][0] - 1, points_history[1][1]],
+                            _ => todo!(),
+                        };
+                        draw(
+                            buf,
+                            [points_history[0], points_history[1], connecting_point],
+                            &symbols,
+                            pos,
+                            color,
+                        );
+                        points_history = [points_history[1], connecting_point, points_history[2]];
+                    }
+
+                    draw(buf, points_history, &symbols, pos, color);
+                }
+            }
+
+            points_history = [points_history[1], points_history[2], {
+                // change the x or y value based on which one is changing quicker
+                let ddx = -(starting_angle + arc_angle).sin();
+                let ddy = (starting_angle + arc_angle).cos();
+                if ddx.abs() > ddy.abs() {
+                    [
+                        points_history[2][0] + ddx.signum() as i32,
+                        points_history[2][1],
+                    ]
+                } else {
+                    [
+                        points_history[2][0],
+                        points_history[2][1] + ddy.signum() as i32,
+                    ]
+                }
+            }];
+
+            draw(buf, points_history, &symbols, pos, color);
+        }
+
+        if area.area() == 0 {
+            return;
+        }
+
+        for i in 0..4 {
+            // the radius for the curve between this line and the next
+            let r = match self.tui_modifier.border_types[(i + 1) % 4] {
+                BorderType::HIDDEN => 0.0,
+                BorderType::NONE => 0.0,
+                _ => match self.tui_modifier.border_radi[i] {
+                    UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
+                    UnitSystem::Point(p) => p,
+                }
+                .abs()
+                .min((area.width as f32 / RADIUS_MULTIPLIER[0]) / 2.0)
+                .min((area.height as f32 / RADIUS_MULTIPLIER[1]) / 2.0),
+            };
+            let radius = [
+                (r * RADIUS_MULTIPLIER[0]) as u16,
+                (r * RADIUS_MULTIPLIER[1]) as u16,
+            ];
+
+            // the radius for the curve between this line and the last
+            let last_idx = if i == 0 { 3 } else { i - 1 };
+            let last_r = match self.tui_modifier.border_types[last_idx] {
+                BorderType::HIDDEN => 0.0,
+                BorderType::NONE => 0.0,
+                _ => match self.tui_modifier.border_radi[last_idx] {
+                    UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
+                    UnitSystem::Point(p) => p,
+                }
+                .abs()
+                .min((area.width as f32 / RADIUS_MULTIPLIER[0]) / 2.0)
+                .min((area.height as f32 / RADIUS_MULTIPLIER[1]) / 2.0),
+            };
+            let last_radius = [
+                (last_r * RADIUS_MULTIPLIER[0]) as u16,
+                (last_r * RADIUS_MULTIPLIER[1]) as u16,
+            ];
+
+            let symbols = match self.tui_modifier.border_types[i] {
+                BorderType::DOTTED => NORMAL,
+                BorderType::DASHED => NORMAL,
+                BorderType::SOLID => NORMAL,
+                BorderType::DOUBLE => DOUBLE,
+                BorderType::GROOVE => NORMAL,
+                BorderType::RIDGE => NORMAL,
+                BorderType::INSET => NORMAL,
+                BorderType::OUTSET => NORMAL,
+                BorderType::HIDDEN => continue,
+                BorderType::NONE => continue,
+            };
+
+            let color = self.tui_modifier.border_colors[i].or(self.block_style.fg);
+
+            match i {
+                0 => {
+                    for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
+                        let cell = buf.get_mut(x, area.top());
+                        if let Some(c) = color {
+                            cell.fg = c;
+                        }
+                        cell.symbol = symbols.horizontal.to_string();
+                    }
+                }
+                1 => {
+                    for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
+                        let cell = buf.get_mut(area.right() - 1, y);
+                        if let Some(c) = color {
+                            cell.fg = c;
+                        }
+                        cell.symbol = symbols.vertical.to_string();
+                    }
+                }
+                2 => {
+                    for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
+                        let cell = buf.get_mut(x, area.bottom() - 1);
+                        if let Some(c) = color {
+                            cell.fg = c;
+                        }
+                        cell.symbol = symbols.horizontal.to_string();
+                    }
+                }
+                3 => {
+                    for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
+                        let cell = buf.get_mut(area.left(), y);
+                        if let Some(c) = color {
+                            cell.fg = c;
+                        }
+                        cell.symbol = symbols.vertical.to_string();
+                    }
+                }
+                _ => (),
+            }
+
+            match i {
+                0 => draw_arc(
+                    [area.right() - radius[0] - 1, area.top() + radius[1]],
+                    std::f32::consts::FRAC_PI_2 * 3.0,
+                    std::f32::consts::FRAC_PI_2,
+                    r,
+                    &symbols,
+                    buf,
+                    &color,
+                ),
+                1 => draw_arc(
+                    [area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
+                    0.0,
+                    std::f32::consts::FRAC_PI_2,
+                    r,
+                    &symbols,
+                    buf,
+                    &color,
+                ),
+                2 => draw_arc(
+                    [area.left() + radius[0], area.bottom() - radius[1] - 1],
+                    std::f32::consts::FRAC_PI_2,
+                    std::f32::consts::FRAC_PI_2,
+                    r,
+                    &symbols,
+                    buf,
+                    &color,
+                ),
+                3 => draw_arc(
+                    [area.left() + radius[0], area.top() + radius[1]],
+                    std::f32::consts::PI,
+                    std::f32::consts::FRAC_PI_2,
+                    r,
+                    &symbols,
+                    buf,
+                    &color,
+                ),
+                _ => panic!("more than 4 sides?"),
+            }
+        }
+
+        // todo: only render inside borders
+        for x in area.left()..area.right() {
+            for y in area.top()..area.bottom() {
+                let cell = buf.get_mut(x, y);
+                if let Some(c) = self.block_style.bg {
+                    cell.bg = c;
+                }
+            }
+        }
+    }
+}
 
 pub fn render_vnode<'a>(
     frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
@@ -21,11 +330,13 @@ pub fn render_vnode<'a>(
     layouts: &mut HashMap<ElementId, TuiNode<'a>>,
     vdom: &'a VirtualDom,
     node: &'a VNode<'a>,
+    // this holds the parents syle state for styled text rendering and potentially transparentcy
+    style: &TuiStyle,
 ) {
     match node {
         VNode::Fragment(f) => {
             for child in f.children {
-                render_vnode(frame, layout, layouts, vdom, child);
+                render_vnode(frame, layout, layouts, vdom, child, style);
             }
             return;
         }
@@ -33,7 +344,7 @@ pub fn render_vnode<'a>(
         VNode::Component(vcomp) => {
             let idx = vcomp.scope.get().unwrap();
             let new_node = vdom.get_scope(idx).unwrap().root_node();
-            render_vnode(frame, layout, layouts, vdom, new_node);
+            render_vnode(frame, layout, layouts, vdom, new_node, style);
             return;
         }
 
@@ -55,11 +366,12 @@ pub fn render_vnode<'a>(
             #[derive(Default)]
             struct Label<'a> {
                 text: &'a str,
+                style: TuiStyle,
             }
 
             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());
+                    buf.set_string(area.left(), area.top(), self.text, self.style);
                 }
             }
 
@@ -67,7 +379,10 @@ pub fn render_vnode<'a>(
 
             // Block::default().
 
-            let label = Label { text: t.text };
+            let label = Label {
+                text: t.text,
+                style: *style,
+            };
             let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
 
             // the renderer will panic if a node is rendered out of range even if the size is zero
@@ -76,16 +391,17 @@ pub fn render_vnode<'a>(
             }
         }
         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);
 
+            let new_style = style.patch(node.block_style);
+
             // the renderer will panic if a node is rendered out of range even if the size is zero
             if area.width > 0 && area.height > 0 {
-                frame.render_widget(block, area);
+                frame.render_widget(node, area);
             }
 
             for el in el.children {
-                render_vnode(frame, layout, layouts, vdom, el);
+                render_vnode(frame, layout, layouts, vdom, el, &new_style);
             }
         }
         VNode::Fragment(_) => todo!(),