use crate::style::Color;
use crate::widget::theme::DARK_GRAY;
use crate::widget::traits::{RenderContext, View, WidgetProps};
use crate::{impl_props_builders, impl_styled_view};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Orientation {
#[default]
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DividerStyle {
#[default]
Solid,
Dashed,
Dotted,
Double,
Thick,
}
pub struct Divider {
orientation: Orientation,
style: DividerStyle,
color: Color,
label: Option<String>,
label_color: Option<Color>,
margin: u16,
length: u16,
props: WidgetProps,
}
impl Divider {
pub fn new() -> Self {
Self {
orientation: Orientation::Horizontal,
style: DividerStyle::Solid,
color: DARK_GRAY,
label: None,
label_color: None,
margin: 0,
length: 0,
props: WidgetProps::new(),
}
}
pub fn vertical() -> Self {
Self {
orientation: Orientation::Vertical,
..Self::new()
}
}
pub fn orientation(mut self, orientation: Orientation) -> Self {
self.orientation = orientation;
self
}
pub fn style(mut self, style: DividerStyle) -> Self {
self.style = style;
self
}
pub fn color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn label_color(mut self, color: Color) -> Self {
self.label_color = Some(color);
self
}
pub fn margin(mut self, margin: u16) -> Self {
self.margin = margin;
self
}
pub fn length(mut self, length: u16) -> Self {
self.length = length;
self
}
pub fn dashed(mut self) -> Self {
self.style = DividerStyle::Dashed;
self
}
pub fn dotted(mut self) -> Self {
self.style = DividerStyle::Dotted;
self
}
pub fn double(mut self) -> Self {
self.style = DividerStyle::Double;
self
}
pub fn thick(mut self) -> Self {
self.style = DividerStyle::Thick;
self
}
fn line_char(&self) -> char {
match (self.orientation, self.style) {
(Orientation::Horizontal, DividerStyle::Solid) => '─',
(Orientation::Horizontal, DividerStyle::Dashed) => '╌',
(Orientation::Horizontal, DividerStyle::Dotted) => '┄',
(Orientation::Horizontal, DividerStyle::Double) => '═',
(Orientation::Horizontal, DividerStyle::Thick) => '━',
(Orientation::Vertical, DividerStyle::Solid) => '│',
(Orientation::Vertical, DividerStyle::Dashed) => '╎',
(Orientation::Vertical, DividerStyle::Dotted) => '┆',
(Orientation::Vertical, DividerStyle::Double) => '║',
(Orientation::Vertical, DividerStyle::Thick) => '┃',
}
}
}
impl Default for Divider {
fn default() -> Self {
Self::new()
}
}
impl View for Divider {
crate::impl_view_meta!("Divider");
fn render(&self, ctx: &mut RenderContext) {
let area = ctx.area;
let line_char = self.line_char();
match self.orientation {
Orientation::Horizontal => {
let start_x = self.margin;
let end_x = if self.length > 0 {
(start_x + self.length).min(area.width)
} else {
area.width.saturating_sub(self.margin)
};
if let Some(ref label) = self.label {
let label_len = crate::utils::unicode::display_width(label) as u16;
let total_width = end_x - start_x;
if label_len + 4 <= total_width {
let label_start = start_x + (total_width - label_len) / 2 - 1;
let label_end = label_start + label_len + 2;
ctx.draw_hline(start_x, 0, label_start - start_x, line_char, self.color);
ctx.draw_char(label_start, 0, ' ', self.color);
let label_color = self.label_color.unwrap_or(self.color);
ctx.draw_text(label_start + 1, 0, label, label_color);
ctx.draw_char(label_end - 1, 0, ' ', self.color);
ctx.draw_hline(label_end, 0, end_x - label_end, line_char, self.color);
} else {
let label_color = self.label_color.unwrap_or(self.color);
ctx.draw_text_clipped(start_x, 0, label, label_color, end_x - start_x);
}
} else {
ctx.draw_hline(start_x, 0, end_x - start_x, line_char, self.color);
}
}
Orientation::Vertical => {
let start_y = self.margin;
let end_y = if self.length > 0 {
(start_y + self.length).min(area.height)
} else {
area.height.saturating_sub(self.margin)
};
ctx.draw_vline(0, start_y, end_y - start_y, line_char, self.color);
}
}
}
}
impl_styled_view!(Divider);
impl_props_builders!(Divider);
pub fn divider() -> Divider {
Divider::new()
}
pub fn vdivider() -> Divider {
Divider::vertical()
}