use crate::{
Component,
RenderError,
Rendered,
layout::Direction,
theme::{
Palette,
Style,
Theme,
},
};
pub struct Divider {
direction: Direction,
style: Style,
label: Option<String>,
}
impl Divider {
pub fn horizontal() -> Self {
Self {
direction: Direction::Horizontal,
style: Style::new(),
label: None,
}
}
pub fn vertical() -> Self {
Self {
direction: Direction::Vertical,
style: Style::new(),
label: None,
}
}
pub fn styled(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn labeled(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
}
impl Component for Divider {
fn render(&self, width: u16) -> Result<Rendered, RenderError> {
let theme = Theme::current();
let style = if self.style == Style::new() {
Style::new().fg(theme.border_default())
} else {
self.style.clone()
};
let line = match self.direction {
| Direction::Horizontal => {
if let Some(ref label) = self.label {
let label = format!(" {} ", label);
let label_vw = crate::utils::visible_width(&label);
if label_vw + 4 > width as usize {
let line = "─".repeat(width as usize);
crate::theme::stylize(&line, &style)
} else {
let side = (width as usize - label_vw) / 2;
let left = "─".repeat(side);
let right = "─".repeat(width as usize - side - label_vw);
let text = crate::theme::stylize(&label, &style);
let left = crate::theme::stylize(&left, &style);
let right = crate::theme::stylize(&right, &style);
format!("{}{}{}", left, text, right)
}
} else {
let line = "─".repeat(width as usize);
crate::theme::stylize(&line, &style)
}
},
| Direction::Vertical => {
let line = "│".repeat(width as usize);
crate::theme::stylize(&line, &style)
},
};
Ok(Rendered {
lines: vec![line],
cursor: None,
images: Vec::new(),
})
}
fn render_rect(&self, rect: crate::layout::Rect) -> Result<Rendered, RenderError> {
if self.direction == Direction::Vertical && rect.height > 1 {
let theme = Theme::current();
let style = if self.style == Style::new() {
Style::new().fg(theme.border_default())
} else {
self.style.clone()
};
let ch = crate::theme::stylize("│", &style);
let mut lines = Vec::new();
for _ in 0..rect.height {
lines.push(ch.clone());
}
Ok(Rendered {
lines,
cursor: None,
images: Vec::new(),
})
} else {
self.render(rect.width)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::theme::Theme;
#[test]
fn divider_horizontal_renders_line() {
Theme::with(Theme::Light, || {
let div = Divider::horizontal();
let rendered = div.render(10).unwrap();
assert_eq!(rendered.lines.len(), 1);
assert!(rendered.lines[0].contains("─"));
});
}
#[test]
fn divider_vertical_renders_pipe() {
Theme::with(Theme::Light, || {
let div = Divider::vertical();
let rendered = div.render(1).unwrap();
assert!(rendered.lines[0].contains("│"));
});
}
#[test]
fn divider_labeled_renders_label() {
Theme::with(Theme::Light, || {
let div = Divider::horizontal().labeled("Section");
let rendered = div.render(30).unwrap();
assert!(rendered.lines[0].contains("Section"));
});
}
#[test]
fn divider_rect_vertical_multi_line() {
Theme::with(Theme::Light, || {
let div = Divider::vertical();
let rendered = div
.render_rect(crate::layout::Rect::new(0, 0, 1, 3))
.unwrap();
assert_eq!(rendered.lines.len(), 3);
for line in &rendered.lines {
assert!(line.contains("│"));
}
});
}
}