retroglyph_widgets/widget/
paragraph.rs1use retroglyph_core::layout::TextLayout;
10use retroglyph_core::text::{Line, Span};
11use retroglyph_core::{Backend, Rect, Style, Terminal};
12
13use super::{Measure, Widget};
14
15#[derive(Clone, Copy, Debug)]
23pub struct Paragraph<'a> {
24 text: &'a str,
25 style: Style,
26}
27
28impl<'a> Paragraph<'a> {
29 #[must_use]
31 pub fn new(text: &'a str) -> Self {
32 Self {
33 text,
34 style: Style::new(),
35 }
36 }
37
38 #[must_use]
40 pub const fn style(mut self, style: Style) -> Self {
41 self.style = style;
42 self
43 }
44
45 fn line(&self) -> Line {
46 Line::from(Span::styled(self.text, self.style))
47 }
48}
49
50impl Measure for Paragraph<'_> {
51 fn height_for(&self, width: u16) -> u16 {
52 let line = self.line();
53 TextLayout::new(&line)
54 .rect(Rect::new(0, 0, width, u16::MAX))
55 .measure()
56 .height
57 }
58}
59
60impl<B: Backend> Widget<B> for Paragraph<'_> {
61 fn render(self, area: Rect, term: &mut Terminal<B>) {
62 let line = self.line();
63 TextLayout::new(&line).rect(area).render(term);
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use retroglyph_core::Headless;
70
71 use super::*;
72
73 #[test]
74 fn height_for_matches_wrapped_line_count() {
75 let p = Paragraph::new("the quick brown fox jumps");
76 assert_eq!(p.height_for(10), 3); assert_eq!(p.height_for(100), 1);
78 }
79
80 #[test]
81 fn height_for_respects_hard_newlines() {
82 let p = Paragraph::new("first\nsecond\nthird");
85 assert_eq!(p.height_for(100), 3);
86 }
87
88 #[test]
89 fn render_draws_one_line_per_wrapped_row() {
90 let area = Rect::new(0, 0, 10, 5);
91 let mut term = Terminal::new(Headless::new(10, 5));
92 Paragraph::new("the quick brown fox jumps").render(area, &mut term);
93
94 let row0: String = (0..10).map(|x| term.grid().get(x, 0).glyph()).collect();
95 let row1: String = (0..10).map(|x| term.grid().get(x, 1).glyph()).collect();
96 let row2: String = (0..10).map(|x| term.grid().get(x, 2).glyph()).collect();
97 assert!(row0.starts_with("the quick"));
98 assert!(row1.starts_with("brown fox"));
99 assert!(row2.starts_with("jumps"));
100 }
101
102 #[test]
103 fn render_stops_at_the_area_bottom() {
104 let area = Rect::new(0, 0, 10, 1);
106 let mut term = Terminal::new(Headless::new(10, 2));
107 Paragraph::new("the quick brown fox jumps").render(area, &mut term);
108
109 let row1: String = (0..10).map(|x| term.grid().get(x, 1).glyph()).collect();
110 assert_eq!(row1.trim(), "");
111 }
112}