use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::style::Style;
use ratatui_core::text::{Line, Span, Text as RText};
use crate::element::Element;
pub struct Text {
spans: Vec<(String, Style)>,
}
pub fn text(content: impl Into<String>) -> Text {
Text {
spans: vec![(content.into(), Style::default())],
}
}
impl Text {
pub fn style(mut self, style: Style) -> Self {
if let Some(last) = self.spans.last_mut() {
last.1 = style;
}
self
}
pub fn span(mut self, content: impl Into<String>, style: Style) -> Self {
self.spans.push((content.into(), style));
self
}
fn to_ratatui(&self) -> RText<'_> {
let mut lines: Vec<Line<'_>> = vec![Line::default()];
for (content, style) in &self.spans {
for (i, part) in content.split('\n').enumerate() {
if i > 0 {
lines.push(Line::default());
}
if !part.is_empty() {
let line = lines.last_mut().expect("lines starts non-empty");
line.spans.push(Span::styled(part, *style));
}
}
}
RText::from(lines)
}
}
impl Element for Text {
fn height(&self, width: u16) -> u16 {
eye_declare_engine::wrap::wrapped_line_count(&self.to_ratatui(), width)
}
fn render(&self, area: Rect, buf: &mut Buffer) {
eye_declare_engine::wrap::render_wrapped(
self.to_ratatui(),
ratatui_core::layout::Alignment::Left,
0,
area,
buf,
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use ratatui_core::style::Color;
fn rendered(el: &impl Element, width: u16) -> Vec<String> {
let height = el.height(width);
let area = Rect::new(0, 0, width, height);
let mut buf = Buffer::empty(area);
el.render(area, &mut buf);
(0..height)
.map(|y| {
let mut line: String = (0..width).map(|x| buf[(x, y)].symbol()).collect();
while line.ends_with(' ') {
line.pop();
}
line
})
.collect()
}
#[test]
fn single_line() {
assert_eq!(rendered(&text("hello"), 10), vec!["hello"]);
}
#[test]
fn wraps_words_at_width() {
assert_eq!(rendered(&text("hello world"), 6), vec!["hello", "world"]);
}
#[test]
fn newlines_split_lines() {
assert_eq!(rendered(&text("a\nb"), 10), vec!["a", "b"]);
}
#[test]
fn spans_concatenate_and_carry_style() {
let el = text("ab").span("cd", Style::default().fg(Color::Red));
assert_eq!(rendered(&el, 10), vec!["abcd"]);
let area = Rect::new(0, 0, 10, 1);
let mut buf = Buffer::empty(area);
el.render(area, &mut buf);
assert_eq!(buf[(2, 0)].style().fg, Some(Color::Red));
assert_ne!(buf[(0, 0)].style().fg, Some(Color::Red));
}
#[test]
fn newline_inside_later_span() {
let el = text("a").span("b\nc", Style::default());
assert_eq!(rendered(&el, 10), vec!["ab", "c"]);
}
#[test]
fn empty_text_is_one_blank_row() {
assert_eq!(text("").height(10), 1);
}
}