tui_piechart/
border_style.rs

1//! Border styles for pie chart block wrappers.
2//!
3//! This module provides predefined border styles that can be used to customize
4//! the appearance of the block that wraps a pie chart.
5//!
6//! # Examples
7//!
8//! ```
9//! use tui_piechart::{PieChart, PieSlice, border_style::BorderStyle};
10//! use ratatui::style::Color;
11//!
12//! let slices = vec![PieSlice::new("Rust", 45.0, Color::Red)];
13//! let piechart = PieChart::new(slices)
14//!     .block(BorderStyle::Rounded.block().title("My Chart"));
15//! ```
16
17use ratatui::symbols::border;
18use ratatui::widgets::Block;
19
20// Re-export title positioning types for backward compatibility
21// The actual implementations are in the `title` module
22pub use crate::title::{BlockExt, TitleAlignment, TitlePosition};
23
24// ============================================================================
25// BORDER STYLE ENUM
26// ============================================================================
27
28/// Predefined border styles for the pie chart block wrapper.
29///
30/// These styles provide convenient ways to customize the appearance of the
31/// block that wraps the pie chart.
32///
33/// # Unicode Limitations
34///
35/// Note that `DoubleLineRounded` and `ThickRounded` use mixed styles because
36/// Unicode doesn't have true rounded double-line or thick-line box-drawing
37/// characters. These styles use single-line rounded corners with double/thick
38/// edges for a softer appearance.
39///
40/// # Examples
41///
42/// ```
43/// use tui_piechart::{PieChart, PieSlice, border_style::BorderStyle};
44/// use ratatui::style::Color;
45///
46/// let slices = vec![PieSlice::new("Rust", 45.0, Color::Red)];
47///
48/// // Dashed borders for a subtle look
49/// let chart = PieChart::new(slices.clone())
50///     .block(BorderStyle::Dashed.block().title("Dashed"));
51///
52/// // Thick rounded for bold emphasis
53/// let chart = PieChart::new(slices)
54///     .block(BorderStyle::ThickRounded.block().title("Bold"));
55/// ```
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
57pub enum BorderStyle {
58    /// Standard single-line borders (default)
59    #[default]
60    Standard,
61    /// Rounded corners with single-line borders
62    Rounded,
63    /// Dashed lines throughout (┄┄┄)
64    Dashed,
65    /// Rounded corners with dashed borders
66    RoundedDashed,
67    /// Standard borders with gaps only at corners
68    CornerGapped,
69    /// Rounded borders with gaps only at corners
70    RoundedCornerGapped,
71    /// Double-line borders for standard style
72    DoubleLineStandard,
73    /// Double-line borders with rounded corners (mixed: rounded corners + double edges)
74    DoubleLineRounded,
75    /// Thick borders (uses heavy line drawing characters)
76    Thick,
77    /// Thick borders with rounded corners (mixed: rounded corners + thick edges)
78    ThickRounded,
79    /// Thick borders with dashed lines
80    ThickDashed,
81    /// Thick borders with gaps only at corners
82    ThickCornerGapped,
83}
84
85impl BorderStyle {
86    /// Creates a new `Block` with the specified border style.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use tui_piechart::border_style::BorderStyle;
92    ///
93    /// let block = BorderStyle::Rounded.block().title("My Chart");
94    /// ```
95    #[must_use]
96    pub fn block(self) -> Block<'static> {
97        match self {
98            Self::Standard => Block::bordered(),
99            Self::Rounded => Block::bordered().border_set(border::ROUNDED),
100            Self::Dashed => Block::bordered().border_set(BORDER_DASHED),
101            Self::RoundedDashed => Block::bordered().border_set(BORDER_ROUNDED_DASHED),
102            Self::CornerGapped => Block::bordered().border_set(BORDER_CORNER_GAPPED),
103            Self::RoundedCornerGapped => Block::bordered().border_set(BORDER_ROUNDED_CORNER_GAPPED),
104            Self::DoubleLineStandard => Block::bordered().border_set(border::DOUBLE),
105            Self::DoubleLineRounded => Block::bordered().border_set(BORDER_DOUBLE_ROUNDED),
106            Self::Thick => Block::bordered().border_set(border::THICK),
107            Self::ThickRounded => Block::bordered().border_set(BORDER_THICK_ROUNDED),
108            Self::ThickDashed => Block::bordered().border_set(BORDER_THICK_DASHED),
109            Self::ThickCornerGapped => Block::bordered().border_set(BORDER_THICK_CORNER_GAPPED),
110        }
111    }
112}
113
114// ============================================================================
115// CUSTOM BORDER SETS
116// ============================================================================
117
118/// Border set with dashed lines throughout
119///
120/// Uses Unicode dashed line characters (┄ and ┊) for a subtle, non-intrusive
121/// border appearance.
122pub const BORDER_DASHED: border::Set = border::Set {
123    top_left: "┌",
124    top_right: "┐",
125    bottom_left: "└",
126    bottom_right: "┘",
127    vertical_left: "┊",
128    vertical_right: "┊",
129    horizontal_top: "┄",
130    horizontal_bottom: "┄",
131};
132
133/// Border set with rounded corners and dashed lines
134///
135/// Combines rounded corners with dashed lines for a modern, subtle look.
136pub const BORDER_ROUNDED_DASHED: border::Set = border::Set {
137    top_left: "╭",
138    top_right: "╮",
139    bottom_left: "╰",
140    bottom_right: "╯",
141    vertical_left: "┊",
142    vertical_right: "┊",
143    horizontal_top: "┄",
144    horizontal_bottom: "┄",
145};
146
147/// Border set with gaps only at corners (standard lines)
148///
149/// Provides a minimalist look with continuous lines and gaps only at corners.
150pub const BORDER_CORNER_GAPPED: border::Set = border::Set {
151    top_left: " ",
152    top_right: " ",
153    bottom_left: " ",
154    bottom_right: " ",
155    vertical_left: "│",
156    vertical_right: "│",
157    horizontal_top: "─",
158    horizontal_bottom: "─",
159};
160
161/// Border set with rounded corners and gaps only at corners
162///
163/// Provides a minimalist look with continuous lines, rounded appearance, and
164/// gaps only at corners.
165pub const BORDER_ROUNDED_CORNER_GAPPED: border::Set = border::Set {
166    top_left: " ",
167    top_right: " ",
168    bottom_left: " ",
169    bottom_right: " ",
170    vertical_left: "│",
171    vertical_right: "│",
172    horizontal_top: "─",
173    horizontal_bottom: "─",
174};
175
176/// Border set with double lines and rounded corners (mixed style)
177///
178/// # Unicode Limitation
179///
180/// Unicode doesn't have true rounded double-line corners, so this uses
181/// rounded single-line corners with double-line edges for a softer appearance.
182///
183/// Visual: `╭═══╮` instead of the non-existent `╔═══╗` with rounded corners.
184pub const BORDER_DOUBLE_ROUNDED: border::Set = border::Set {
185    top_left: "╭",
186    top_right: "╮",
187    bottom_left: "╰",
188    bottom_right: "╯",
189    vertical_left: "║",
190    vertical_right: "║",
191    horizontal_top: "═",
192    horizontal_bottom: "═",
193};
194
195/// Border set with thick lines and rounded corners (mixed style)
196///
197/// # Unicode Limitation
198///
199/// Unicode doesn't have true rounded thick-line corners, so this uses
200/// rounded single-line corners with thick-line edges for a softer appearance.
201///
202/// Visual: `╭━━━╮` instead of the non-existent `┏━━━┓` with rounded corners.
203pub const BORDER_THICK_ROUNDED: border::Set = border::Set {
204    top_left: "╭",
205    top_right: "╮",
206    bottom_left: "╰",
207    bottom_right: "╯",
208    vertical_left: "┃",
209    vertical_right: "┃",
210    horizontal_top: "━",
211    horizontal_bottom: "━",
212};
213
214/// Border set with thick dashed lines
215///
216/// Uses Unicode heavy dashed line characters (┅ and ┇) for bold, non-intrusive
217/// emphasis.
218pub const BORDER_THICK_DASHED: border::Set = border::Set {
219    top_left: "┏",
220    top_right: "┓",
221    bottom_left: "┗",
222    bottom_right: "┛",
223    vertical_left: "┇",
224    vertical_right: "┇",
225    horizontal_top: "┅",
226    horizontal_bottom: "┅",
227};
228
229/// Border set with thick lines and gaps only at corners
230///
231/// Provides a bold minimalist look with thick continuous lines and gaps only
232/// at corners.
233pub const BORDER_THICK_CORNER_GAPPED: border::Set = border::Set {
234    top_left: " ",
235    top_right: " ",
236    bottom_left: " ",
237    bottom_right: " ",
238    vertical_left: "┃",
239    vertical_right: "┃",
240    horizontal_top: "━",
241    horizontal_bottom: "━",
242};
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[test]
249    fn border_style_default() {
250        assert_eq!(BorderStyle::default(), BorderStyle::Standard);
251    }
252
253    #[test]
254    fn border_style_block_creation() {
255        // Test that all variants can create blocks without panicking
256        let styles = [
257            BorderStyle::Standard,
258            BorderStyle::Rounded,
259            BorderStyle::Dashed,
260            BorderStyle::RoundedDashed,
261            BorderStyle::CornerGapped,
262            BorderStyle::RoundedCornerGapped,
263            BorderStyle::DoubleLineStandard,
264            BorderStyle::DoubleLineRounded,
265            BorderStyle::Thick,
266            BorderStyle::ThickRounded,
267            BorderStyle::ThickDashed,
268            BorderStyle::ThickCornerGapped,
269        ];
270
271        for style in &styles {
272            let _block = style.block();
273        }
274    }
275
276    #[test]
277    fn border_style_equality() {
278        assert_eq!(BorderStyle::Standard, BorderStyle::Standard);
279        assert_ne!(BorderStyle::Standard, BorderStyle::Rounded);
280    }
281
282    #[test]
283    fn border_sets_have_valid_characters() {
284        // Ensure all border sets have non-empty strings
285        let sets = [
286            BORDER_DASHED,
287            BORDER_ROUNDED_DASHED,
288            BORDER_CORNER_GAPPED,
289            BORDER_ROUNDED_CORNER_GAPPED,
290            BORDER_DOUBLE_ROUNDED,
291            BORDER_THICK_ROUNDED,
292            BORDER_THICK_DASHED,
293            BORDER_THICK_CORNER_GAPPED,
294        ];
295
296        for set in &sets {
297            assert!(!set.top_left.is_empty());
298            assert!(!set.top_right.is_empty());
299            assert!(!set.bottom_left.is_empty());
300            assert!(!set.bottom_right.is_empty());
301            assert!(!set.vertical_left.is_empty());
302            assert!(!set.vertical_right.is_empty());
303            assert!(!set.horizontal_top.is_empty());
304            assert!(!set.horizontal_bottom.is_empty());
305        }
306    }
307
308    // Note: Title alignment and position tests are in the `title` module
309}