use retroglyph_core::layout::TextLayout;
use retroglyph_core::text::{Line, Span};
use retroglyph_core::{Rect, Style};
use super::{Measure, Widget};
use crate::Surface;
#[derive(Clone, Copy, Debug)]
pub struct Paragraph<'a> {
text: &'a str,
style: Style,
}
impl<'a> Paragraph<'a> {
#[must_use]
pub fn new(text: &'a str) -> Self {
Self {
text,
style: Style::new(),
}
}
#[must_use]
pub const fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
fn line(&self) -> Line {
Line::from(Span::styled(self.text, self.style))
}
}
impl Measure for Paragraph<'_> {
fn height_for(&self, width: u16) -> u16 {
let line = self.line();
TextLayout::new(&line)
.rect(Rect::new(0, 0, width, u16::MAX))
.measure()
.height
}
}
impl Widget for Paragraph<'_> {
fn render(&self, area: Rect, surface: &mut Surface<'_>) {
let line = self.line();
let layer = surface.layer();
TextLayout::new(&line)
.rect(area)
.render_to_grid(surface.grid_mut(), layer);
}
}
#[cfg(test)]
mod tests {
use retroglyph_core::{Grid, Pos};
use super::*;
#[test]
fn height_for_matches_wrapped_line_count() {
let p = Paragraph::new("the quick brown fox jumps");
assert_eq!(p.height_for(10), 3); assert_eq!(p.height_for(100), 1);
}
#[test]
fn height_for_respects_hard_newlines() {
let p = Paragraph::new("first\nsecond\nthird");
assert_eq!(p.height_for(100), 3);
}
#[test]
fn render_draws_one_line_per_wrapped_row() {
let area = Rect::new(0, 0, 10, 5);
let mut grid = Grid::new(10, 5);
Paragraph::new("the quick brown fox jumps")
.render(area, &mut Surface::new(&mut grid, area, 0));
let row0: String = (0..10).map(|x| grid[Pos::new(x, 0)].glyph()).collect();
let row1: String = (0..10).map(|x| grid[Pos::new(x, 1)].glyph()).collect();
let row2: String = (0..10).map(|x| grid[Pos::new(x, 2)].glyph()).collect();
assert!(row0.starts_with("the quick"));
assert!(row1.starts_with("brown fox"));
assert!(row2.starts_with("jumps"));
}
#[test]
fn render_stops_at_the_area_bottom() {
let area = Rect::new(0, 0, 10, 1);
let mut grid = Grid::new(10, 2);
Paragraph::new("the quick brown fox jumps")
.render(area, &mut Surface::new(&mut grid, area, 0));
let row1: String = (0..10).map(|x| grid[Pos::new(x, 1)].glyph()).collect();
assert_eq!(row1.trim(), "");
}
}