From 985cd05573b71cf0caed0537714fb005ad219894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Tchilinguirian?= <60852187+theotchlx@users.noreply.github.com> Date: Mon, 20 Jan 2025 09:36:19 +0100 Subject: [PATCH] feat(symbols): add dashed borders (#1573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds several new border sets: - ratatui::symbols::border::LIGHT_DOUBLE_DASHED - ratatui::symbols::border::HEAVY_DOUBLE_DASHED - ratatui::symbols::border::LIGHT_TRIPLE_DASHED - ratatui::symbols::border::HEAVY_TRIPLE_DASHED - ratatui::symbols::border::LIGHT_QUADRUPLE_DASHED - ratatui::symbols::border::HEAVY_QUADRUPLE_DASHED And corresponding variants to the ratatui::widgets::BorderType enum Fixes: https://github.com/ratatui/ratatui/issues/1355 Signed-off-by: Théo Tchilinguirian --- ratatui-core/src/symbols/border.rs | 197 +++++++++++++++++++++++++++++ ratatui-core/src/symbols/line.rs | 48 +++++++ ratatui-widgets/src/block.rs | 138 ++++++++++++++++++++ ratatui-widgets/src/borders.rs | 54 ++++++++ 4 files changed, 437 insertions(+) diff --git a/ratatui-core/src/symbols/border.rs b/ratatui-core/src/symbols/border.rs index 5accbbb2..092ae89c 100644 --- a/ratatui-core/src/symbols/border.rs +++ b/ratatui-core/src/symbols/border.rs @@ -18,6 +18,20 @@ impl Default for Set { } } +// Helper function to convert a line set to a border set +const fn from_line_set(line_set: line::Set) -> Set { + Set { + top_left: line_set.top_left, + top_right: line_set.top_right, + bottom_left: line_set.bottom_left, + bottom_right: line_set.bottom_right, + vertical_left: line_set.vertical, + vertical_right: line_set.vertical, + horizontal_top: line_set.horizontal, + horizontal_bottom: line_set.horizontal, + } +} + /// Border Set with a single line width /// /// ```text @@ -94,6 +108,66 @@ pub const THICK: Set = Set { horizontal_bottom: line::THICK.horizontal, }; +/// Border Set with light double-dashed border lines +/// +/// ```text +/// ┌╌╌╌╌╌┐ +/// ╎xxxxx╎ +/// ╎xxxxx╎ +/// └╌╌╌╌╌┘ +/// ``` +pub const LIGHT_DOUBLE_DASHED: Set = from_line_set(line::LIGHT_DOUBLE_DASHED); + +/// Border Set with thick double-dashed border lines +/// +/// ```text +/// ┏╍╍╍╍╍┓ +/// ╏xxxxx╏ +/// ╏xxxxx╏ +/// ┗╍╍╍╍╍┛ +/// ``` +pub const HEAVY_DOUBLE_DASHED: Set = from_line_set(line::HEAVY_DOUBLE_DASHED); + +/// Border Set with light triple-dashed border lines +/// +/// ```text +/// ┌┄┄┄┄┄┐ +/// ┆xxxxx┆ +/// ┆xxxxx┆ +/// └┄┄┄┄┄┘ +/// ``` +pub const LIGHT_TRIPLE_DASHED: Set = from_line_set(line::LIGHT_TRIPLE_DASHED); + +/// Border Set with thick triple-dashed border lines +/// +/// ```text +/// ┏┅┅┅┅┅┓ +/// ┇xxxxx┇ +/// ┇xxxxx┇ +/// ┗┅┅┅┅┅┛ +/// ``` +pub const HEAVY_TRIPLE_DASHED: Set = from_line_set(line::HEAVY_TRIPLE_DASHED); + +/// Border Set with light quadruple-dashed border lines +/// +/// ```text +/// ┌┈┈┈┈┈┐ +/// ┊xxxxx┊ +/// ┊xxxxx┊ +/// └┈┈┈┈┈┘ +/// ``` +pub const LIGHT_QUADRUPLE_DASHED: Set = from_line_set(line::LIGHT_QUADRUPLE_DASHED); + +/// Border Set with thick quadruple-dashed border lines +/// +/// ```text +/// ┏┉┉┉┉┉┓ +/// ┋xxxxx┋ +/// ┋xxxxx┋ +/// ┗┉┉┉┉┉┛ +/// ``` +pub const HEAVY_QUADRUPLE_DASHED: Set = from_line_set(line::HEAVY_QUADRUPLE_DASHED); + pub const QUADRANT_TOP_LEFT: &str = "▘"; pub const QUADRANT_TOP_RIGHT: &str = "▝"; pub const QUADRANT_BOTTOM_LEFT: &str = "▖"; @@ -327,6 +401,39 @@ mod tests { ) } + #[test] + fn border_set_from_line_set() { + let custom_line_set = line::Set { + top_left: "a", + top_right: "b", + bottom_left: "c", + bottom_right: "d", + vertical: "e", + horizontal: "f", + vertical_left: "g", + vertical_right: "h", + horizontal_down: "i", + horizontal_up: "j", + cross: "k", + }; + + let border_set = from_line_set(custom_line_set); + + assert_eq!( + border_set, + Set { + top_left: "a", + top_right: "b", + bottom_left: "c", + bottom_right: "d", + vertical_left: "e", + vertical_right: "e", + horizontal_bottom: "f", + horizontal_top: "f", + } + ); + } + #[test] fn plain() { assert_eq!( @@ -387,6 +494,96 @@ mod tests { ); } + #[test] + fn light_double_dashed() { + assert_eq!( + render(LIGHT_DOUBLE_DASHED), + indoc!( + "░░░░░░ + ░┌╌╌┐░ + ░╎░░╎░ + ░╎░░╎░ + ░└╌╌┘░ + ░░░░░░" + ) + ); + } + + #[test] + fn heavy_double_dashed() { + assert_eq!( + render(HEAVY_DOUBLE_DASHED), + indoc!( + "░░░░░░ + ░┏╍╍┓░ + ░╏░░╏░ + ░╏░░╏░ + ░┗╍╍┛░ + ░░░░░░" + ) + ); + } + + #[test] + fn light_triple_dashed() { + assert_eq!( + render(LIGHT_TRIPLE_DASHED), + indoc!( + "░░░░░░ + ░┌┄┄┐░ + ░┆░░┆░ + ░┆░░┆░ + ░└┄┄┘░ + ░░░░░░" + ) + ); + } + + #[test] + fn heavy_triple_dashed() { + assert_eq!( + render(HEAVY_TRIPLE_DASHED), + indoc!( + "░░░░░░ + ░┏┅┅┓░ + ░┇░░┇░ + ░┇░░┇░ + ░┗┅┅┛░ + ░░░░░░" + ) + ); + } + + #[test] + fn light_quadruple_dashed() { + assert_eq!( + render(LIGHT_QUADRUPLE_DASHED), + indoc!( + "░░░░░░ + ░┌┈┈┐░ + ░┊░░┊░ + ░┊░░┊░ + ░└┈┈┘░ + ░░░░░░" + ) + ); + } + + #[test] + fn heavy_quadruple_dashed() { + assert_eq!( + render(HEAVY_QUADRUPLE_DASHED), + indoc!( + "░░░░░░ + ░┏┉┉┓░ + ░┋░░┋░ + ░┋░░┋░ + ░┗┉┉┛░ + ░░░░░░" + ) + ); + } + #[test] fn quadrant_outside() { assert_eq!( diff --git a/ratatui-core/src/symbols/line.rs b/ratatui-core/src/symbols/line.rs index 1416460f..54e4c0a9 100644 --- a/ratatui-core/src/symbols/line.rs +++ b/ratatui-core/src/symbols/line.rs @@ -1,10 +1,22 @@ pub const VERTICAL: &str = "│"; pub const DOUBLE_VERTICAL: &str = "║"; pub const THICK_VERTICAL: &str = "┃"; +pub const LIGHT_DOUBLE_DASH_VERTICAL: &str = "╎"; +pub const HEAVY_DOUBLE_DASH_VERTICAL: &str = "╏"; +pub const LIGHT_TRIPLE_DASH_VERTICAL: &str = "┆"; +pub const HEAVY_TRIPLE_DASH_VERTICAL: &str = "┇"; +pub const LIGHT_QUADRUPLE_DASH_VERTICAL: &str = "┊"; +pub const HEAVY_QUADRUPLE_DASH_VERTICAL: &str = "┋"; pub const HORIZONTAL: &str = "─"; pub const DOUBLE_HORIZONTAL: &str = "═"; pub const THICK_HORIZONTAL: &str = "━"; +pub const LIGHT_DOUBLE_DASH_HORIZONTAL: &str = "╌"; +pub const HEAVY_DOUBLE_DASH_HORIZONTAL: &str = "╍"; +pub const LIGHT_TRIPLE_DASH_HORIZONTAL: &str = "┄"; +pub const HEAVY_TRIPLE_DASH_HORIZONTAL: &str = "┅"; +pub const LIGHT_QUADRUPLE_DASH_HORIZONTAL: &str = "┈"; +pub const HEAVY_QUADRUPLE_DASH_HORIZONTAL: &str = "┉"; pub const TOP_RIGHT: &str = "┐"; pub const ROUNDED_TOP_RIGHT: &str = "╮"; @@ -117,6 +129,42 @@ pub const THICK: Set = Set { cross: THICK_CROSS, }; +pub const LIGHT_DOUBLE_DASHED: Set = Set { + vertical: LIGHT_DOUBLE_DASH_VERTICAL, + horizontal: LIGHT_DOUBLE_DASH_HORIZONTAL, + ..NORMAL +}; + +pub const HEAVY_DOUBLE_DASHED: Set = Set { + vertical: HEAVY_DOUBLE_DASH_VERTICAL, + horizontal: HEAVY_DOUBLE_DASH_HORIZONTAL, + ..THICK +}; + +pub const LIGHT_TRIPLE_DASHED: Set = Set { + vertical: LIGHT_TRIPLE_DASH_VERTICAL, + horizontal: LIGHT_TRIPLE_DASH_HORIZONTAL, + ..NORMAL +}; + +pub const HEAVY_TRIPLE_DASHED: Set = Set { + vertical: HEAVY_TRIPLE_DASH_VERTICAL, + horizontal: HEAVY_TRIPLE_DASH_HORIZONTAL, + ..THICK +}; + +pub const LIGHT_QUADRUPLE_DASHED: Set = Set { + vertical: LIGHT_QUADRUPLE_DASH_VERTICAL, + horizontal: LIGHT_QUADRUPLE_DASH_HORIZONTAL, + ..NORMAL +}; + +pub const HEAVY_QUADRUPLE_DASHED: Set = Set { + vertical: HEAVY_QUADRUPLE_DASH_VERTICAL, + horizontal: HEAVY_QUADRUPLE_DASH_HORIZONTAL, + ..THICK +}; + #[cfg(test)] mod tests { use indoc::{formatdoc, indoc}; diff --git a/ratatui-widgets/src/block.rs b/ratatui-widgets/src/block.rs index 28f9fa9b..47ae057e 100644 --- a/ratatui-widgets/src/block.rs +++ b/ratatui-widgets/src/block.rs @@ -1389,6 +1389,30 @@ mod tests { assert_eq!(format!("{}", BorderType::Rounded), "Rounded"); assert_eq!(format!("{}", BorderType::Double), "Double"); assert_eq!(format!("{}", BorderType::Thick), "Thick"); + assert_eq!( + format!("{}", BorderType::LightDoubleDashed), + "LightDoubleDashed" + ); + assert_eq!( + format!("{}", BorderType::HeavyDoubleDashed), + "HeavyDoubleDashed" + ); + assert_eq!( + format!("{}", BorderType::LightTripleDashed), + "LightTripleDashed" + ); + assert_eq!( + format!("{}", BorderType::HeavyTripleDashed), + "HeavyTripleDashed" + ); + assert_eq!( + format!("{}", BorderType::LightQuadrupleDashed), + "LightQuadrupleDashed" + ); + assert_eq!( + format!("{}", BorderType::HeavyQuadrupleDashed), + "HeavyQuadrupleDashed" + ); } #[test] @@ -1397,6 +1421,30 @@ mod tests { assert_eq!("Rounded".parse(), Ok(BorderType::Rounded)); assert_eq!("Double".parse(), Ok(BorderType::Double)); assert_eq!("Thick".parse(), Ok(BorderType::Thick)); + assert_eq!( + "LightDoubleDashed".parse(), + Ok(BorderType::LightDoubleDashed) + ); + assert_eq!( + "HeavyDoubleDashed".parse(), + Ok(BorderType::HeavyDoubleDashed) + ); + assert_eq!( + "LightTripleDashed".parse(), + Ok(BorderType::LightTripleDashed) + ); + assert_eq!( + "HeavyTripleDashed".parse(), + Ok(BorderType::HeavyTripleDashed) + ); + assert_eq!( + "LightQuadrupleDashed".parse(), + Ok(BorderType::LightQuadrupleDashed) + ); + assert_eq!( + "HeavyQuadrupleDashed".parse(), + Ok(BorderType::HeavyQuadrupleDashed) + ); assert_eq!("".parse::(), Err(ParseError::VariantNotFound)); } @@ -1490,6 +1538,96 @@ mod tests { assert_eq!(buffer, expected); } + #[test] + fn render_light_double_dashed_border() { + let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); + Block::bordered() + .border_type(BorderType::LightDoubleDashed) + .render(buffer.area, &mut buffer); + #[rustfmt::skip] + let expected = Buffer::with_lines([ + "┌╌╌╌╌╌╌╌╌┐", + "╎ ╎", + "└╌╌╌╌╌╌╌╌┘", + ]); + assert_eq!(buffer, expected); + } + + #[test] + fn render_heavy_double_dashed_border() { + let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); + Block::bordered() + .border_type(BorderType::HeavyDoubleDashed) + .render(buffer.area, &mut buffer); + #[rustfmt::skip] + let expected = Buffer::with_lines([ + "┏╍╍╍╍╍╍╍╍┓", + "╏ ╏", + "┗╍╍╍╍╍╍╍╍┛", + ]); + assert_eq!(buffer, expected); + } + + #[test] + fn render_light_triple_dashed_border() { + let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); + Block::bordered() + .border_type(BorderType::LightTripleDashed) + .render(buffer.area, &mut buffer); + #[rustfmt::skip] + let expected = Buffer::with_lines([ + "┌┄┄┄┄┄┄┄┄┐", + "┆ ┆", + "└┄┄┄┄┄┄┄┄┘", + ]); + assert_eq!(buffer, expected); + } + + #[test] + fn render_heavy_triple_dashed_border() { + let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); + Block::bordered() + .border_type(BorderType::HeavyTripleDashed) + .render(buffer.area, &mut buffer); + #[rustfmt::skip] + let expected = Buffer::with_lines([ + "┏┅┅┅┅┅┅┅┅┓", + "┇ ┇", + "┗┅┅┅┅┅┅┅┅┛", + ]); + assert_eq!(buffer, expected); + } + + #[test] + fn render_light_quadruple_dashed_border() { + let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); + Block::bordered() + .border_type(BorderType::LightQuadrupleDashed) + .render(buffer.area, &mut buffer); + #[rustfmt::skip] + let expected = Buffer::with_lines([ + "┌┈┈┈┈┈┈┈┈┐", + "┊ ┊", + "└┈┈┈┈┈┈┈┈┘", + ]); + assert_eq!(buffer, expected); + } + + #[test] + fn render_heavy_quadruple_dashed_border() { + let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); + Block::bordered() + .border_type(BorderType::HeavyQuadrupleDashed) + .render(buffer.area, &mut buffer); + #[rustfmt::skip] + let expected = Buffer::with_lines([ + "┏┉┉┉┉┉┉┉┉┓", + "┋ ┋", + "┗┉┉┉┉┉┉┉┉┛", + ]); + assert_eq!(buffer, expected); + } + #[test] fn render_custom_border_set() { let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 3)); diff --git a/ratatui-widgets/src/borders.rs b/ratatui-widgets/src/borders.rs index da359019..ae29920c 100644 --- a/ratatui-widgets/src/borders.rs +++ b/ratatui-widgets/src/borders.rs @@ -74,6 +74,54 @@ pub enum BorderType { /// ┗━━━━━━━┛ /// ``` Thick, + /// A light double-dashed border. + /// + /// ```plain + /// ┌╌╌╌╌╌╌╌┐ + /// ╎ ╎ + /// └╌╌╌╌╌╌╌┘ + /// ``` + LightDoubleDashed, + /// A heavy double-dashed border. + /// + /// ```plain + /// ┏╍╍╍╍╍╍╍┓ + /// ╏ ╏ + /// ┗╍╍╍╍╍╍╍┛ + /// ``` + HeavyDoubleDashed, + /// A light triple-dashed border. + /// + /// ```plain + /// ┌┄┄┄┄┄┄┄┐ + /// ┆ ┆ + /// └┄┄┄┄┄┄┄┘ + /// ``` + LightTripleDashed, + /// A heavy triple-dashed border. + /// + /// ```plain + /// ┏┅┅┅┅┅┅┅┓ + /// ┇ ┇ + /// ┗┅┅┅┅┅┅┅┛ + /// ``` + HeavyTripleDashed, + /// A light quadruple-dashed border. + /// + /// ```plain + /// ┌┈┈┈┈┈┈┈┐ + /// ┊ ┊ + /// └┈┈┈┈┈┈┈┘ + /// ``` + LightQuadrupleDashed, + /// A heavy quadruple-dashed border. + /// + /// ```plain + /// ┏┉┉┉┉┉┉┉┓ + /// ┋ ┋ + /// ┗┉┉┉┉┉┉┉┛ + /// ``` + HeavyQuadrupleDashed, /// A border with a single line on the inside of a half block. /// /// # Example @@ -105,6 +153,12 @@ impl BorderType { Self::Rounded => border::ROUNDED, Self::Double => border::DOUBLE, Self::Thick => border::THICK, + Self::LightDoubleDashed => border::LIGHT_DOUBLE_DASHED, + Self::HeavyDoubleDashed => border::HEAVY_DOUBLE_DASHED, + Self::LightTripleDashed => border::LIGHT_TRIPLE_DASHED, + Self::HeavyTripleDashed => border::HEAVY_TRIPLE_DASHED, + Self::LightQuadrupleDashed => border::LIGHT_QUADRUPLE_DASHED, + Self::HeavyQuadrupleDashed => border::HEAVY_QUADRUPLE_DASHED, Self::QuadrantInside => border::QUADRANT_INSIDE, Self::QuadrantOutside => border::QUADRANT_OUTSIDE, }