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}