1use crate::style::Color;
4use crate::widget::traits::{RenderContext, View, WidgetProps};
5use crate::{impl_props_builders, impl_styled_view};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum Orientation {
10 #[default]
12 Horizontal,
13 Vertical,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19pub enum DividerStyle {
20 #[default]
22 Solid,
23 Dashed,
25 Dotted,
27 Double,
29 Thick,
31}
32
33pub struct Divider {
46 orientation: Orientation,
48 style: DividerStyle,
50 color: Color,
52 label: Option<String>,
54 label_color: Option<Color>,
56 margin: u16,
58 length: u16,
60 props: WidgetProps,
62}
63
64impl Divider {
65 pub fn new() -> Self {
67 Self {
68 orientation: Orientation::Horizontal,
69 style: DividerStyle::Solid,
70 color: Color::rgb(80, 80, 80),
71 label: None,
72 label_color: None,
73 margin: 0,
74 length: 0,
75 props: WidgetProps::new(),
76 }
77 }
78
79 pub fn vertical() -> Self {
81 Self {
82 orientation: Orientation::Vertical,
83 ..Self::new()
84 }
85 }
86
87 pub fn orientation(mut self, orientation: Orientation) -> Self {
89 self.orientation = orientation;
90 self
91 }
92
93 pub fn style(mut self, style: DividerStyle) -> Self {
95 self.style = style;
96 self
97 }
98
99 pub fn color(mut self, color: Color) -> Self {
101 self.color = color;
102 self
103 }
104
105 pub fn label(mut self, label: impl Into<String>) -> Self {
107 self.label = Some(label.into());
108 self
109 }
110
111 pub fn label_color(mut self, color: Color) -> Self {
113 self.label_color = Some(color);
114 self
115 }
116
117 pub fn margin(mut self, margin: u16) -> Self {
119 self.margin = margin;
120 self
121 }
122
123 pub fn length(mut self, length: u16) -> Self {
125 self.length = length;
126 self
127 }
128
129 pub fn dashed(mut self) -> Self {
131 self.style = DividerStyle::Dashed;
132 self
133 }
134
135 pub fn dotted(mut self) -> Self {
137 self.style = DividerStyle::Dotted;
138 self
139 }
140
141 pub fn double(mut self) -> Self {
143 self.style = DividerStyle::Double;
144 self
145 }
146
147 pub fn thick(mut self) -> Self {
149 self.style = DividerStyle::Thick;
150 self
151 }
152
153 fn line_char(&self) -> char {
155 match (self.orientation, self.style) {
156 (Orientation::Horizontal, DividerStyle::Solid) => '─',
157 (Orientation::Horizontal, DividerStyle::Dashed) => '╌',
158 (Orientation::Horizontal, DividerStyle::Dotted) => '┄',
159 (Orientation::Horizontal, DividerStyle::Double) => '═',
160 (Orientation::Horizontal, DividerStyle::Thick) => '━',
161 (Orientation::Vertical, DividerStyle::Solid) => '│',
162 (Orientation::Vertical, DividerStyle::Dashed) => '╎',
163 (Orientation::Vertical, DividerStyle::Dotted) => '┆',
164 (Orientation::Vertical, DividerStyle::Double) => '║',
165 (Orientation::Vertical, DividerStyle::Thick) => '┃',
166 }
167 }
168}
169
170impl Default for Divider {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176impl View for Divider {
177 crate::impl_view_meta!("Divider");
178
179 fn render(&self, ctx: &mut RenderContext) {
180 let area = ctx.area;
181 let line_char = self.line_char();
182
183 match self.orientation {
184 Orientation::Horizontal => {
185 let y = area.y;
186 let start_x = area.x + self.margin;
187 let end_x = if self.length > 0 {
188 (start_x + self.length).min(area.x + area.width)
189 } else {
190 area.x + area.width.saturating_sub(self.margin)
191 };
192
193 if let Some(ref label) = self.label {
195 let label_len = crate::utils::unicode::display_width(label) as u16;
197 let total_width = end_x - start_x;
198
199 if label_len + 4 <= total_width {
200 let label_start = start_x + (total_width - label_len) / 2 - 1;
201 let label_end = label_start + label_len + 2;
202
203 ctx.draw_hline(start_x, y, label_start - start_x, line_char, self.color);
205
206 ctx.draw_char(label_start, y, ' ', self.color);
208
209 let label_color = self.label_color.unwrap_or(self.color);
211 ctx.draw_text(label_start + 1, y, label, label_color);
212
213 ctx.draw_char(label_end - 1, y, ' ', self.color);
215
216 ctx.draw_hline(label_end, y, end_x - label_end, line_char, self.color);
218 } else {
219 let label_color = self.label_color.unwrap_or(self.color);
221 ctx.draw_text_clipped(start_x, y, label, label_color, end_x - start_x);
222 }
223 } else {
224 ctx.draw_hline(start_x, y, end_x - start_x, line_char, self.color);
226 }
227 }
228 Orientation::Vertical => {
229 let x = area.x;
230 let start_y = area.y + self.margin;
231 let end_y = if self.length > 0 {
232 (start_y + self.length).min(area.y + area.height)
233 } else {
234 area.y + area.height.saturating_sub(self.margin)
235 };
236
237 ctx.draw_vline(x, start_y, end_y - start_y, line_char, self.color);
238 }
239 }
240 }
241}
242
243impl_styled_view!(Divider);
244impl_props_builders!(Divider);
245
246pub fn divider() -> Divider {
248 Divider::new()
249}
250
251pub fn vdivider() -> Divider {
253 Divider::vertical()
254}
255
256