Sfoglia il codice sorgente

refactored borders

Evan Almloff 3 anni fa
parent
commit
a4fdb22197
5 ha cambiato i file con 324 aggiunte e 215 eliminazioni
  1. 152 67
      src/attributes.rs
  2. 1 2
      src/layout.rs
  3. 142 113
      src/render.rs
  4. 27 31
      src/style.rs
  5. 2 2
      src/widget.rs

+ 152 - 67
src/attributes.rs

@@ -30,7 +30,6 @@
 */
 
 use stretch2::{prelude::*, style::PositionType, style::Style};
-use tui::style::{Color, Style as TuiStyle};
 
 use crate::style::{RinkColor, RinkStyle};
 
@@ -40,16 +39,50 @@ pub struct StyleModifer {
     pub tui_modifier: TuiModifier,
 }
 
+#[derive(Default)]
 pub struct TuiModifier {
-    // border arrays start at the top and proceed clockwise
-    pub border_colors: [Option<RinkColor>; 4],
-    pub border_types: [BorderType; 4],
-    pub border_widths: [UnitSystem; 4],
-    pub border_radi: [UnitSystem; 4],
+    pub borders: Borders,
+}
+
+#[derive(Default)]
+pub struct Borders {
+    pub top: BorderEdge,
+    pub right: BorderEdge,
+    pub bottom: BorderEdge,
+    pub left: BorderEdge,
+}
+
+impl Borders {
+    fn slice(&mut self) -> [&mut BorderEdge; 4] {
+        [
+            &mut self.top,
+            &mut self.right,
+            &mut self.bottom,
+            &mut self.left,
+        ]
+    }
+}
+
+pub struct BorderEdge {
+    pub color: Option<RinkColor>,
+    pub style: BorderStyle,
+    pub width: UnitSystem,
+    pub radius: UnitSystem,
+}
+
+impl Default for BorderEdge {
+    fn default() -> Self {
+        Self {
+            color: None,
+            style: BorderStyle::NONE,
+            width: UnitSystem::Point(0.0),
+            radius: UnitSystem::Point(0.0),
+        }
+    }
 }
 
 #[derive(Clone, Copy)]
-pub enum BorderType {
+pub enum BorderStyle {
     DOTTED,
     DASHED,
     SOLID,
@@ -62,13 +95,30 @@ pub enum BorderType {
     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],
+impl BorderStyle {
+    pub fn symbol_set(&self) -> Option<tui::symbols::line::Set> {
+        use tui::symbols::line::*;
+        const DASHED: Set = Set {
+            horizontal: "╌",
+            vertical: "╎",
+            ..NORMAL
+        };
+        const DOTTED: Set = Set {
+            horizontal: "┈",
+            vertical: "┊",
+            ..NORMAL
+        };
+        match self {
+            BorderStyle::DOTTED => Some(DOTTED),
+            BorderStyle::DASHED => Some(DASHED),
+            BorderStyle::SOLID => Some(NORMAL),
+            BorderStyle::DOUBLE => Some(DOUBLE),
+            BorderStyle::GROOVE => Some(NORMAL),
+            BorderStyle::RIDGE => Some(NORMAL),
+            BorderStyle::INSET => Some(NORMAL),
+            BorderStyle::OUTSET => Some(NORMAL),
+            BorderStyle::HIDDEN => None,
+            BorderStyle::NONE => None,
         }
     }
 }
@@ -345,8 +395,8 @@ fn parse_value(value: &str) -> Option<UnitSystem> {
         } else {
             None
         }
-    } else if value.ends_with("%") {
-        if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
+    } else if value.ends_with('%') {
+        if let Ok(pct) = value.trim_end_matches('%').parse::<f32>() {
             Some(UnitSystem::Percent(pct))
         } else {
             None
@@ -421,18 +471,18 @@ fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
 }
 
 fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
-    fn parse_border_type(v: &str) -> BorderType {
+    fn parse_border_style(v: &str) -> BorderStyle {
         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,
+            "dotted" => BorderStyle::DOTTED,
+            "dashed" => BorderStyle::DASHED,
+            "solid" => BorderStyle::SOLID,
+            "double" => BorderStyle::DOUBLE,
+            "groove" => BorderStyle::GROOVE,
+            "ridge" => BorderStyle::RIDGE,
+            "inset" => BorderStyle::INSET,
+            "outset" => BorderStyle::OUTSET,
+            "none" => BorderStyle::NONE,
+            "hidden" => BorderStyle::HIDDEN,
             _ => todo!(),
         }
     }
@@ -441,23 +491,25 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
         "border-bottom" => {}
         "border-bottom-color" => {
             if let Ok(c) = value.parse() {
-                style.tui_modifier.border_colors[2] = Some(c);
+                style.tui_modifier.borders.bottom.color = Some(c);
             }
         }
         "border-bottom-left-radius" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_radi[2] = v;
+                style.tui_modifier.borders.left.radius = v;
             }
         }
         "border-bottom-right-radius" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_radi[1] = v;
+                style.tui_modifier.borders.right.radius = v;
             }
         }
-        "border-bottom-style" => style.tui_modifier.border_types[2] = parse_border_type(value),
+        "border-bottom-style" => {
+            style.tui_modifier.borders.bottom.style = parse_border_style(value)
+        }
         "border-bottom-width" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_widths[2] = v;
+                style.tui_modifier.borders.bottom.width = v;
             }
         }
         "border-collapse" => {}
@@ -465,14 +517,20 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
             let values: Vec<_> = value.split(' ').collect();
             if values.len() == 1 {
                 if let Ok(c) = values[0].parse() {
-                    for i in 0..4 {
-                        style.tui_modifier.border_colors[i] = Some(c);
-                    }
+                    style
+                        .tui_modifier
+                        .borders
+                        .slice()
+                        .iter_mut()
+                        .for_each(|b| b.color = Some(c));
                 }
             } else {
-                for (i, v) in values.into_iter().enumerate() {
+                for (v, b) in values
+                    .into_iter()
+                    .zip(style.tui_modifier.borders.slice().iter_mut())
+                {
                     if let Ok(c) = v.parse() {
-                        style.tui_modifier.border_colors[i] = Some(c);
+                        b.color = Some(c);
                     }
                 }
             }
@@ -486,27 +544,33 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
         "border-left" => {}
         "border-left-color" => {
             if let Ok(c) = value.parse() {
-                style.tui_modifier.border_colors[3] = Some(c);
+                style.tui_modifier.borders.left.color = Some(c);
             }
         }
-        "border-left-style" => style.tui_modifier.border_types[3] = parse_border_type(value),
+        "border-left-style" => style.tui_modifier.borders.left.style = parse_border_style(value),
         "border-left-width" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_widths[3] = v;
+                style.tui_modifier.borders.left.width = 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;
-                    }
+                    style
+                        .tui_modifier
+                        .borders
+                        .slice()
+                        .iter_mut()
+                        .for_each(|b| b.radius = r);
                 }
             } else {
-                for (i, v) in values.into_iter().enumerate() {
+                for (v, b) in values
+                    .into_iter()
+                    .zip(style.tui_modifier.borders.slice().iter_mut())
+                {
                     if let Some(r) = parse_value(v) {
-                        style.tui_modifier.border_radi[i] = r;
+                        b.radius = r;
                     }
                 }
             }
@@ -514,55 +578,76 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
         "border-right" => {}
         "border-right-color" => {
             if let Ok(c) = value.parse() {
-                style.tui_modifier.border_colors[1] = Some(c);
+                style.tui_modifier.borders.right.color = Some(c);
             }
         }
-        "border-right-style" => style.tui_modifier.border_types[1] = parse_border_type(value),
+        "border-right-style" => style.tui_modifier.borders.right.style = parse_border_style(value),
         "border-right-width" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_widths[1] = v;
+                style.tui_modifier.borders.right.width = v;
             }
         }
         "border-spacing" => {}
         "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;
-                }
+                let border_style = parse_border_style(values[0]);
+                style
+                    .tui_modifier
+                    .borders
+                    .slice()
+                    .iter_mut()
+                    .for_each(|b| b.style = border_style);
             } else {
-                for (i, v) in values.into_iter().enumerate() {
-                    style.tui_modifier.border_types[i] = parse_border_type(v);
+                for (v, b) in values
+                    .into_iter()
+                    .zip(style.tui_modifier.borders.slice().iter_mut())
+                {
+                    b.style = parse_border_style(v);
                 }
             }
         }
         "border-top" => {}
         "border-top-color" => {
             if let Ok(c) = value.parse() {
-                style.tui_modifier.border_colors[0] = Some(c);
+                style.tui_modifier.borders.top.color = Some(c);
             }
         }
         "border-top-left-radius" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_radi[3] = v;
+                style.tui_modifier.borders.left.radius = v;
             }
         }
         "border-top-right-radius" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_radi[0] = v;
+                style.tui_modifier.borders.right.radius = v;
             }
         }
-        "border-top-style" => style.tui_modifier.border_types[0] = parse_border_type(value),
+        "border-top-style" => style.tui_modifier.borders.top.style = parse_border_style(value),
         "border-top-width" => {
             if let Some(v) = parse_value(value) {
-                style.tui_modifier.border_widths[0] = v;
+                style.tui_modifier.borders.top.width = v;
             }
         }
         "border-width" => {
-            if let Some(v) = parse_value(value) {
-                for i in 0..4 {
-                    style.tui_modifier.border_widths[i] = v;
+            let values: Vec<_> = value.split(' ').collect();
+            if values.len() == 1 {
+                if let Some(w) = parse_value(values[0]) {
+                    style
+                        .tui_modifier
+                        .borders
+                        .slice()
+                        .iter_mut()
+                        .for_each(|b| b.width = w);
+                }
+            } else {
+                for (v, b) in values
+                    .into_iter()
+                    .zip(style.tui_modifier.borders.slice().iter_mut())
+                {
+                    if let Some(w) = parse_value(v) {
+                        b.width = w;
+                    }
                 }
             }
         }
@@ -654,11 +739,11 @@ fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
 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" => (),
+        "font-family" => (),
+        "font-size" => (),
+        "font-size-adjust" => (),
+        "font-stretch" => (),
         "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),

+ 1 - 2
src/layout.rs

@@ -1,6 +1,5 @@
 use dioxus::core::*;
 use std::collections::HashMap;
-use tui::style::Style as TuiStyle;
 
 use crate::{
     attributes::{apply_attributes, StyleModifer},
@@ -88,7 +87,7 @@ pub fn collect_layout<'a>(
                 node.mounted_id(),
                 TuiNode {
                     node,
-                    block_style: modifier.tui_style.into(),
+                    block_style: modifier.tui_style,
                     tui_modifier: modifier.tui_modifier,
                     layout: layout.new_node(modifier.style, &child_layout).unwrap(),
                 },

+ 142 - 113
src/render.rs

@@ -10,7 +10,7 @@ use tui::{backend::CrosstermBackend, layout::Rect};
 use crate::{
     style::{RinkColor, RinkStyle},
     widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
-    BorderType, Config, TuiNode, UnitSystem,
+    BorderEdge, BorderStyle, Config, TuiNode, UnitSystem,
 };
 
 const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
@@ -72,10 +72,6 @@ pub fn render_vnode<'a>(
                 }
             }
 
-            // let s = Span::raw(t.text);
-
-            // Block::default().
-
             let label = Label {
                 text: t.text,
                 style: *style,
@@ -190,7 +186,7 @@ impl<'a> RinkWidget for TuiNode<'a> {
             arc_angle: f32,
             radius: f32,
             symbols: &Set,
-            mut buf: &mut RinkBuffer,
+            buf: &mut RinkBuffer,
             color: &Option<RinkColor>,
         ) {
             if radius < 0.0 {
@@ -202,6 +198,7 @@ impl<'a> RinkWidget for TuiNode<'a> {
                 (starting_angle.cos() * (radius * RADIUS_MULTIPLIER[0])) as i32,
                 (starting_angle.sin() * (radius * RADIUS_MULTIPLIER[1])) as i32,
             ];
+            // keep track of the last 3 point to allow filling diagonals
             let mut points_history = [
                 [0, 0],
                 {
@@ -238,16 +235,16 @@ impl<'a> RinkWidget for TuiNode<'a> {
                             _ => todo!(),
                         };
                         draw(
-                            &mut buf,
+                            buf,
                             [points_history[0], points_history[1], connecting_point],
-                            &symbols,
+                            symbols,
                             pos,
                             color,
                         );
                         points_history = [points_history[1], connecting_point, points_history[2]];
                     }
 
-                    draw(&mut buf, points_history, &symbols, pos, color);
+                    draw(buf, points_history, symbols, pos, color);
                 }
             }
 
@@ -268,7 +265,21 @@ impl<'a> RinkWidget for TuiNode<'a> {
                 }
             }];
 
-            draw(&mut buf, points_history, &symbols, pos, color);
+            draw(buf, points_history, symbols, pos, color);
+        }
+
+        fn get_radius(border: &BorderEdge, area: Rect) -> f32 {
+            match border.style {
+                BorderStyle::HIDDEN => 0.0,
+                BorderStyle::NONE => 0.0,
+                _ => match border.radius {
+                    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),
+            }
         }
 
         if area.area() == 0 {
@@ -286,128 +297,146 @@ impl<'a> RinkWidget for TuiNode<'a> {
             }
         }
 
-        for i in 0..4 {
+        let borders = self.tui_modifier.borders;
+
+        let last_edge = &borders.left;
+        let current_edge = &borders.top;
+        if let Some(symbols) = current_edge.style.symbol_set() {
             // 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 r = get_radius(current_edge, area);
             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_r = get_radius(last_edge, area);
             let last_radius = [
                 (last_r * RADIUS_MULTIPLIER[0]) as u16,
                 (last_r * RADIUS_MULTIPLIER[1]) as u16,
             ];
+            let color = current_edge.color.or(self.block_style.fg);
+            let mut new_cell = RinkCell::default();
+            if let Some(c) = color {
+                new_cell.fg = c;
+            }
+            for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
+                new_cell.symbol = symbols.horizontal.to_string();
+                buf.set(x, area.top(), &new_cell);
+            }
+            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,
+                &mut buf,
+                &color,
+            );
+        }
 
-            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);
+        let last_edge = &borders.top;
+        let current_edge = &borders.right;
+        if let Some(symbols) = current_edge.style.symbol_set() {
+            // the radius for the curve between this line and the next
+            let r = get_radius(current_edge, area);
+            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_r = get_radius(last_edge, area);
+            let last_radius = [
+                (last_r * RADIUS_MULTIPLIER[0]) as u16,
+                (last_r * RADIUS_MULTIPLIER[1]) as u16,
+            ];
+            let color = current_edge.color.or(self.block_style.fg);
+            let mut new_cell = RinkCell::default();
+            if let Some(c) = color {
+                new_cell.fg = c;
+            }
+            for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
+                new_cell.symbol = symbols.vertical.to_string();
+                buf.set(area.right() - 1, y, &new_cell);
+            }
+            draw_arc(
+                [area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
+                0.0,
+                std::f32::consts::FRAC_PI_2,
+                r,
+                &symbols,
+                &mut buf,
+                &color,
+            );
+        }
 
+        let last_edge = &borders.right;
+        let current_edge = &borders.bottom;
+        if let Some(symbols) = current_edge.style.symbol_set() {
+            // the radius for the curve between this line and the next
+            let r = get_radius(current_edge, area);
+            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_r = get_radius(last_edge, area);
+            let last_radius = [
+                (last_r * RADIUS_MULTIPLIER[0]) as u16,
+                (last_r * RADIUS_MULTIPLIER[1]) as u16,
+            ];
+            let color = current_edge.color.or(self.block_style.fg);
             let mut new_cell = RinkCell::default();
             if let Some(c) = color {
                 new_cell.fg = c;
             }
-            match i {
-                0 => {
-                    for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
-                        new_cell.symbol = symbols.horizontal.to_string();
-                        buf.set(x, area.top(), &new_cell);
-                    }
-                }
-                1 => {
-                    for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
-                        new_cell.symbol = symbols.vertical.to_string();
-                        buf.set(area.right() - 1, y, &new_cell);
-                    }
-                }
-                2 => {
-                    for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
-                        new_cell.symbol = symbols.horizontal.to_string();
-                        buf.set(x, area.bottom() - 1, &new_cell);
-                    }
-                }
-                3 => {
-                    for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
-                        new_cell.symbol = symbols.vertical.to_string();
-                        buf.set(area.left(), y, &new_cell);
-                    }
-                }
-                _ => (),
+            for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
+                new_cell.symbol = symbols.horizontal.to_string();
+                buf.set(x, area.bottom() - 1, &new_cell);
             }
+            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,
+                &mut buf,
+                &color,
+            );
+        }
 
-            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,
-                    &mut 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,
-                    &mut 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,
-                    &mut 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,
-                    &mut buf,
-                    &color,
-                ),
-                _ => panic!("more than 4 sides?"),
+        let last_edge = &borders.bottom;
+        let current_edge = &borders.left;
+        if let Some(symbols) = current_edge.style.symbol_set() {
+            // the radius for the curve between this line and the next
+            let r = get_radius(current_edge, area);
+            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_r = get_radius(last_edge, area);
+            let last_radius = [
+                (last_r * RADIUS_MULTIPLIER[0]) as u16,
+                (last_r * RADIUS_MULTIPLIER[1]) as u16,
+            ];
+            let color = current_edge.color.or(self.block_style.fg);
+            let mut new_cell = RinkCell::default();
+            if let Some(c) = color {
+                new_cell.fg = c;
             }
+            for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
+                new_cell.symbol = symbols.vertical.to_string();
+                buf.set(area.left(), y, &new_cell);
+            }
+            draw_arc(
+                [area.left() + radius[0], area.top() + radius[1]],
+                std::f32::consts::PI,
+                std::f32::consts::FRAC_PI_2,
+                r,
+                &symbols,
+                &mut buf,
+                &color,
+            );
         }
     }
 }

+ 27 - 31
src/style.rs

@@ -23,26 +23,23 @@ impl RinkColor {
     pub fn blend(self, other: Color) -> Color {
         if self.color == Color::Reset {
             Color::Reset
+        } else if self.alpha == 0.0 {
+            other
         } else {
-            if self.alpha == 0.0 {
-                other
-            } else {
-                let [sr, sg, sb] = to_rgb(self.color);
-                let [or, og, ob] = to_rgb(other);
-                let (sr, sg, sb, sa) = (
-                    sr as f32 / 255.0,
-                    sg as f32 / 255.0,
-                    sb as f32 / 255.0,
-                    self.alpha,
-                );
-                let (or, og, ob) = (or as f32 / 255.0, og as f32 / 255.0, ob as f32 / 255.0);
-                let c = Color::Rgb(
-                    (255.0 * (sr * sa + or * (1.0 - sa))) as u8,
-                    (255.0 * (sg * sa + og * (1.0 - sa))) as u8,
-                    (255.0 * (sb * sa + ob * (1.0 - sa))) as u8,
-                );
-                c
-            }
+            let [sr, sg, sb] = to_rgb(self.color);
+            let [or, og, ob] = to_rgb(other);
+            let (sr, sg, sb, sa) = (
+                sr as f32 / 255.0,
+                sg as f32 / 255.0,
+                sb as f32 / 255.0,
+                self.alpha,
+            );
+            let (or, og, ob) = (or as f32 / 255.0, og as f32 / 255.0, ob as f32 / 255.0);
+            Color::Rgb(
+                (255.0 * (sr * sa + or * (1.0 - sa))) as u8,
+                (255.0 * (sg * sa + og * (1.0 - sa))) as u8,
+                (255.0 * (sb * sa + ob * (1.0 - sa))) as u8,
+            )
         }
     }
 }
@@ -52,8 +49,8 @@ fn parse_value(
     current_max_output: f32,
     required_max_output: f32,
 ) -> Result<f32, ParseFloatError> {
-    if v.ends_with('%') {
-        Ok((v[..v.len() - 1].trim().parse::<f32>()? / 100.0) * required_max_output)
+    if let Some(stripped) = v.strip_suffix('%') {
+        Ok((stripped.trim().parse::<f32>()? / 100.0) * required_max_output)
     } else {
         Ok((v.trim().parse::<f32>()? / current_max_output) * required_max_output)
     }
@@ -224,8 +221,8 @@ impl FromStr for RinkColor {
                         color: c,
                         alpha: 1.0,
                     })
-                } else if color.starts_with("rgb(") {
-                    let color_values = color[4..].trim_end_matches(')');
+                } else if let Some(stripped) = color.strip_prefix("rgb(") {
+                    let color_values = stripped.trim_end_matches(')');
                     if color.matches(',').count() == 3 {
                         let (alpha, rgb_values) =
                             color_values.rsplit_once(',').ok_or(ParseColorError)?;
@@ -240,8 +237,8 @@ impl FromStr for RinkColor {
                             alpha: 1.0,
                         })
                     }
-                } else if color.starts_with("rgba(") {
-                    let color_values = color[5..].trim_end_matches(')');
+                } else if let Some(stripped) = color.strip_prefix("rgba(") {
+                    let color_values = stripped.trim_end_matches(')');
                     if color.matches(',').count() == 3 {
                         let (rgb_values, alpha) =
                             color_values.rsplit_once(',').ok_or(ParseColorError)?;
@@ -256,8 +253,8 @@ impl FromStr for RinkColor {
                             alpha: 1.0,
                         })
                     }
-                } else if color.starts_with("hsl(") {
-                    let color_values = color[4..].trim_end_matches(')');
+                } else if let Some(stripped) = color.strip_prefix("hsl(") {
+                    let color_values = stripped.trim_end_matches(')');
                     if color.matches(',').count() == 3 {
                         let (rgb_values, alpha) =
                             color_values.rsplit_once(',').ok_or(ParseColorError)?;
@@ -272,8 +269,8 @@ impl FromStr for RinkColor {
                             alpha: 1.0,
                         })
                     }
-                } else if color.starts_with("hsla(") {
-                    let color_values = color[5..].trim_end_matches(')');
+                } else if let Some(stripped) = color.strip_prefix("hsla(") {
+                    let color_values = stripped.trim_end_matches(')');
                     if color.matches(',').count() == 3 {
                         let (rgb_values, alpha) =
                             color_values.rsplit_once(',').ok_or(ParseColorError)?;
@@ -332,7 +329,6 @@ fn to_rgb(c: Color) -> [u8; 3] {
             _ => [0, 0, 0],
         },
         Color::Reset => [0, 0, 0],
-        _ => todo!("{c:?}"),
     }
 }
 
@@ -384,7 +380,7 @@ fn rgb_to_ansi() {
             if rgb[0] != rgb[1] || rgb[1] != rgb[2] {
                 assert_eq!(idxed, converted);
             } else {
-                assert!(i >= 232 && i <= 255);
+                assert!(i >= 232);
             }
         } else {
             panic!("color is not indexed")

+ 2 - 2
src/widget.rs

@@ -23,8 +23,8 @@ impl<'a> RinkBuffer<'a> {
     pub fn set(&mut self, x: u16, y: u16, new: &RinkCell) {
         let mut cell = self.buf.get_mut(x, y);
         cell.bg = convert(self.cfg.rendering_mode, new.bg.blend(cell.bg));
-        if &new.symbol == "" {
-            if &cell.symbol != "" {
+        if new.symbol.is_empty() {
+            if !cell.symbol.is_empty() {
                 // allows text to "shine through" transparent backgrounds
                 cell.fg = convert(self.cfg.rendering_mode, new.bg.blend(cell.fg));
             }