Skip to main content

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    /// Like [`block`](Self::block) with border colors from a shared theme.
87    #[cfg(feature = "theme")]
88    #[must_use]
89    pub fn themed_block(self, theme: &tui_nuances::Theme, focused: bool) -> Block<'static> {
90        self.block().border_style(theme.border_style(focused))
91    }
92
93    /// Creates a new `Block` with the specified border style.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use tui_piechart::border_style::BorderStyle;
99    ///
100    /// let block = BorderStyle::Rounded.block().title("My Chart");
101    /// ```
102    #[must_use]
103    pub fn block(self) -> Block<'static> {
104        match self {
105            Self::Standard => Block::bordered(),
106            Self::Rounded => Block::bordered().border_set(border::ROUNDED),
107            Self::Dashed => Block::bordered().border_set(BORDER_DASHED),
108            Self::RoundedDashed => Block::bordered().border_set(BORDER_ROUNDED_DASHED),
109            Self::CornerGapped => Block::bordered().border_set(BORDER_CORNER_GAPPED),
110            Self::RoundedCornerGapped => Block::bordered().border_set(BORDER_ROUNDED_CORNER_GAPPED),
111            Self::DoubleLineStandard => Block::bordered().border_set(border::DOUBLE),
112            Self::DoubleLineRounded => Block::bordered().border_set(BORDER_DOUBLE_ROUNDED),
113            Self::Thick => Block::bordered().border_set(border::THICK),
114            Self::ThickRounded => Block::bordered().border_set(BORDER_THICK_ROUNDED),
115            Self::ThickDashed => Block::bordered().border_set(BORDER_THICK_DASHED),
116            Self::ThickCornerGapped => Block::bordered().border_set(BORDER_THICK_CORNER_GAPPED),
117        }
118    }
119}
120
121// ============================================================================
122// CUSTOM BORDER SETS
123// ============================================================================
124
125/// Border set with dashed lines throughout
126///
127/// Uses Unicode dashed line characters (┄ and ┊) for a subtle, non-intrusive
128/// border appearance.
129pub const BORDER_DASHED: border::Set = border::Set {
130    top_left: "┌",
131    top_right: "┐",
132    bottom_left: "└",
133    bottom_right: "┘",
134    vertical_left: "┊",
135    vertical_right: "┊",
136    horizontal_top: "┄",
137    horizontal_bottom: "┄",
138};
139
140/// Border set with rounded corners and dashed lines
141///
142/// Combines rounded corners with dashed lines for a modern, subtle look.
143pub const BORDER_ROUNDED_DASHED: border::Set = border::Set {
144    top_left: "╭",
145    top_right: "╮",
146    bottom_left: "╰",
147    bottom_right: "╯",
148    vertical_left: "┊",
149    vertical_right: "┊",
150    horizontal_top: "┄",
151    horizontal_bottom: "┄",
152};
153
154/// Border set with gaps only at corners (standard lines)
155///
156/// Provides a minimalist look with continuous lines and gaps only at corners.
157pub const BORDER_CORNER_GAPPED: border::Set = border::Set {
158    top_left: " ",
159    top_right: " ",
160    bottom_left: " ",
161    bottom_right: " ",
162    vertical_left: "│",
163    vertical_right: "│",
164    horizontal_top: "─",
165    horizontal_bottom: "─",
166};
167
168/// Border set with rounded corners and gaps only at corners
169///
170/// Provides a minimalist look with continuous lines, rounded appearance, and
171/// gaps only at corners.
172pub const BORDER_ROUNDED_CORNER_GAPPED: border::Set = border::Set {
173    top_left: " ",
174    top_right: " ",
175    bottom_left: " ",
176    bottom_right: " ",
177    vertical_left: "│",
178    vertical_right: "│",
179    horizontal_top: "─",
180    horizontal_bottom: "─",
181};
182
183/// Border set with double lines and rounded corners (mixed style)
184///
185/// # Unicode Limitation
186///
187/// Unicode doesn't have true rounded double-line corners, so this uses
188/// rounded single-line corners with double-line edges for a softer appearance.
189///
190/// Visual: `╭═══╮` instead of the non-existent `╔═══╗` with rounded corners.
191pub const BORDER_DOUBLE_ROUNDED: border::Set = border::Set {
192    top_left: "╭",
193    top_right: "╮",
194    bottom_left: "╰",
195    bottom_right: "╯",
196    vertical_left: "║",
197    vertical_right: "║",
198    horizontal_top: "═",
199    horizontal_bottom: "═",
200};
201
202/// Border set with thick lines and rounded corners (mixed style)
203///
204/// # Unicode Limitation
205///
206/// Unicode doesn't have true rounded thick-line corners, so this uses
207/// rounded single-line corners with thick-line edges for a softer appearance.
208///
209/// Visual: `╭━━━╮` instead of the non-existent `┏━━━┓` with rounded corners.
210pub const BORDER_THICK_ROUNDED: border::Set = border::Set {
211    top_left: "╭",
212    top_right: "╮",
213    bottom_left: "╰",
214    bottom_right: "╯",
215    vertical_left: "┃",
216    vertical_right: "┃",
217    horizontal_top: "━",
218    horizontal_bottom: "━",
219};
220
221/// Border set with thick dashed lines
222///
223/// Uses Unicode heavy dashed line characters (┅ and ┇) for bold, non-intrusive
224/// emphasis.
225pub const BORDER_THICK_DASHED: border::Set = border::Set {
226    top_left: "┏",
227    top_right: "┓",
228    bottom_left: "┗",
229    bottom_right: "┛",
230    vertical_left: "┇",
231    vertical_right: "┇",
232    horizontal_top: "┅",
233    horizontal_bottom: "┅",
234};
235
236/// Border set with thick lines and gaps only at corners
237///
238/// Provides a bold minimalist look with thick continuous lines and gaps only
239/// at corners.
240pub const BORDER_THICK_CORNER_GAPPED: border::Set = border::Set {
241    top_left: " ",
242    top_right: " ",
243    bottom_left: " ",
244    bottom_right: " ",
245    vertical_left: "┃",
246    vertical_right: "┃",
247    horizontal_top: "━",
248    horizontal_bottom: "━",
249};
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254
255    #[test]
256    fn border_style_default() {
257        assert_eq!(BorderStyle::default(), BorderStyle::Standard);
258    }
259
260    #[test]
261    fn border_style_block_creation() {
262        // Test that all variants can create blocks without panicking
263        let styles = [
264            BorderStyle::Standard,
265            BorderStyle::Rounded,
266            BorderStyle::Dashed,
267            BorderStyle::RoundedDashed,
268            BorderStyle::CornerGapped,
269            BorderStyle::RoundedCornerGapped,
270            BorderStyle::DoubleLineStandard,
271            BorderStyle::DoubleLineRounded,
272            BorderStyle::Thick,
273            BorderStyle::ThickRounded,
274            BorderStyle::ThickDashed,
275            BorderStyle::ThickCornerGapped,
276        ];
277
278        for style in &styles {
279            let _block = style.block();
280        }
281    }
282
283    #[test]
284    fn border_style_equality() {
285        assert_eq!(BorderStyle::Standard, BorderStyle::Standard);
286        assert_ne!(BorderStyle::Standard, BorderStyle::Rounded);
287    }
288
289    #[test]
290    fn border_sets_have_valid_characters() {
291        // Ensure all border sets have non-empty strings
292        let sets = [
293            BORDER_DASHED,
294            BORDER_ROUNDED_DASHED,
295            BORDER_CORNER_GAPPED,
296            BORDER_ROUNDED_CORNER_GAPPED,
297            BORDER_DOUBLE_ROUNDED,
298            BORDER_THICK_ROUNDED,
299            BORDER_THICK_DASHED,
300            BORDER_THICK_CORNER_GAPPED,
301        ];
302
303        for set in &sets {
304            assert_ne!(set.top_left, "");
305            assert_ne!(set.top_right, "");
306            assert_ne!(set.bottom_left, "");
307            assert_ne!(set.bottom_right, "");
308            assert_ne!(set.vertical_left, "");
309            assert_ne!(set.vertical_right, "");
310            assert_ne!(set.horizontal_top, "");
311            assert_ne!(set.horizontal_bottom, "");
312        }
313    }
314
315    // Note: Title alignment and position tests are in the `title` module
316}