retroglyph_widgets/widget/
print_line.rs1use retroglyph_core::text::Line;
3use retroglyph_core::{Backend, Rect, Terminal};
4use unicode_width::UnicodeWidthStr;
5
6use super::Widget;
7use crate::Align;
8use crate::text::truncate as truncate_to_cols;
9
10#[derive(Clone, Copy, Debug)]
17pub struct PrintLine<'a> {
18 line: &'a Line,
19 align: Align,
20}
21
22impl<'a> PrintLine<'a> {
23 #[must_use]
26 pub const fn new(line: &'a Line) -> Self {
27 Self {
28 line,
29 align: Align::Left,
30 }
31 }
32
33 #[must_use]
36 pub const fn align(mut self, align: Align) -> Self {
37 self.align = align;
38 self
39 }
40}
41
42impl<B: Backend> Widget<B> for PrintLine<'_> {
43 fn render(self, area: Rect, term: &mut Terminal<B>) {
44 let max_width = area.width();
45 let right = area.left() + max_width;
46 let line_width = self
49 .line
50 .spans
51 .iter()
52 .fold(0u16, |acc, s| acc.saturating_add(s.content.width() as u16))
53 .min(max_width);
54 let mut x = area.left() + self.align.offset(max_width, line_width);
55 for span in &self.line.spans {
56 if x >= right {
57 break;
58 }
59 let remaining = (right - x) as usize;
60 let text = truncate_to_cols(&span.content, remaining);
61 term.reset_style()
62 .fg(span.style.foreground())
63 .bg(span.style.background());
64 term.print(x, area.top(), &text);
65 x += text.width() as u16;
66 }
67 term.reset_style();
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use retroglyph_core::{Headless, text::Span};
74
75 use super::*;
76
77 #[test]
78 fn prints_every_span() {
79 let line = Line::from(vec![Span::raw("hi "), Span::raw("there")]);
80 let area = Rect::new(0, 0, 20, 1);
81 let mut term = Terminal::new(Headless::new(20, 1));
82 PrintLine::new(&line).render(area, &mut term);
83
84 let row: String = (0..20).map(|x| term.grid().get(x, 0).glyph()).collect();
85 assert!(row.starts_with("hi there"));
86 }
87
88 #[test]
89 fn right_align_places_the_whole_line_against_the_right_edge() {
90 let line = Line::from(vec![Span::raw("hi "), Span::raw("there")]);
91 let area = Rect::new(0, 0, 20, 1);
92 let mut term = Terminal::new(Headless::new(20, 1));
93 PrintLine::new(&line)
94 .align(Align::Right)
95 .render(area, &mut term);
96
97 let row: String = (0..20).map(|x| term.grid().get(x, 0).glyph()).collect();
99 assert!(row.ends_with("hi there"), "row was {row:?}");
100 }
101
102 #[test]
103 fn clips_to_max_width() {
104 let line = Line::raw("a much longer message than fits");
105 let area = Rect::new(0, 0, 5, 1);
106 let mut term = Terminal::new(Headless::new(5, 1));
107 PrintLine::new(&line).render(area, &mut term);
108
109 assert_eq!(term.grid().get(4, 0).glyph(), 'c');
111 }
112}