retroglyph_ui/widget/
print_line.rs1use retroglyph_core::text::{Line, width as measured_width};
3
4use super::Widget;
5use crate::Align;
6use crate::Surface;
7use crate::text::draw_clipped;
8
9#[derive(Clone, Copy, Debug)]
32pub struct PrintLine<'a> {
33 line: &'a Line,
34 align: Align,
35}
36
37impl<'a> PrintLine<'a> {
38 #[must_use]
41 pub const fn new(line: &'a Line) -> Self {
42 Self {
43 line,
44 align: Align::Left,
45 }
46 }
47
48 #[must_use]
51 pub const fn align(mut self, align: Align) -> Self {
52 self.align = align;
53 self
54 }
55}
56
57impl Widget for PrintLine<'_> {
58 fn render(&self, surface: &mut Surface<'_>) {
59 let max_width = surface.width();
60 let right = max_width;
61 let line_width = self
66 .line
67 .spans
68 .iter()
69 .fold(0u16, |acc, s| {
70 acc.saturating_add(measured_width(&s.content))
71 })
72 .min(max_width);
73 let mut x = self.align.offset(max_width, line_width);
74 for span in &self.line.spans {
75 if x >= right {
76 break;
77 }
78 let remaining = right - x;
79 let text_w = draw_clipped(
80 surface,
81 (x, 0),
82 remaining,
83 &span.content,
84 Align::Left,
85 span.style,
86 );
87 x += text_w;
88 }
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use alloc::string::String;
95 use alloc::vec;
96
97 use retroglyph_core::grid::{Grid, Pos, Rect};
98 use retroglyph_core::text::Span;
99
100 use super::*;
101
102 #[test]
103 fn prints_every_span() {
104 let line = Line::from(vec![Span::raw("hi "), Span::raw("there")]);
105 let area = Rect::new(0, 0, 20, 1);
106 let mut grid = Grid::new(20, 1);
107 PrintLine::new(&line).render(&mut Surface::new(&mut grid, area, 0));
108
109 let row: String = (0..20).map(|x| grid[Pos::new(x, 0)].glyph()).collect();
110 assert!(row.starts_with("hi there"));
111 }
112
113 #[test]
114 fn right_align_places_the_whole_line_against_the_right_edge() {
115 let line = Line::from(vec![Span::raw("hi "), Span::raw("there")]);
116 let area = Rect::new(0, 0, 20, 1);
117 let mut grid = Grid::new(20, 1);
118 PrintLine::new(&line)
119 .align(Align::Right)
120 .render(&mut Surface::new(&mut grid, area, 0));
121
122 let row: String = (0..20).map(|x| grid[Pos::new(x, 0)].glyph()).collect();
124 assert!(row.ends_with("hi there"), "row was {row:?}");
125 }
126
127 #[test]
128 fn clips_to_max_width() {
129 let line = Line::raw("a much longer message than fits");
130 let area = Rect::new(0, 0, 5, 1);
131 let mut grid = Grid::new(5, 1);
132 PrintLine::new(&line).render(&mut Surface::new(&mut grid, area, 0));
133
134 assert_eq!(grid[Pos::new(4, 0)].glyph(), 'c');
136 }
137}