use ctl_text::{Context, Line, Measurement, Overflow, Renderable, Text};
use crate::glyphs::Glyphs;
use crate::lines::{literal, rule_line};
const TITLE_MARGIN: u16 = 4;
#[derive(Clone, Debug, Default)]
pub struct Rule {
title: Option<Text>,
}
impl Rule {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn titled(title: Text) -> Self {
Self { title: Some(title) }
}
}
impl Renderable for Rule {
fn measure(&self, context: &Context) -> Measurement {
let Some(title) = &self.title else {
return Measurement::exact(1);
};
let measured = title.measure_as(context, Overflow::Ellipsis);
Measurement::new(
measured.minimum().saturating_add(TITLE_MARGIN),
measured.maximum().saturating_add(TITLE_MARGIN),
)
}
fn render(&self, context: &Context) -> Vec<Line> {
let rule_char = Glyphs::for_chars(context.box_chars()).rule;
let width = context.width();
let titled = self.title.as_ref().and_then(|title| {
let room = width.saturating_sub(TITLE_MARGIN);
if room == 0 {
return None;
}
title
.render_as(&context.at_width(room), Overflow::Ellipsis)
.into_iter()
.find(|line| !line.is_empty())
});
let Some(title) = titled else {
return vec![rule_line(rule_char, width)];
};
let mut line = Line::new();
line.push(literal(&format!("{rule_char}{rule_char} ")));
line.extend(title.spans().iter().cloned());
let fill = width.saturating_sub(title.width().saturating_add(TITLE_MARGIN));
if fill > 0 {
let tail: String = std::iter::once(' ')
.chain(std::iter::repeat_n(rule_char, usize::from(fill)))
.collect();
line.push(literal(&tail));
}
vec![line]
}
}