retroglyph_widgets/widget/
text.rs1use retroglyph_core::{Rect, Style};
3use unicode_width::UnicodeWidthStr;
4
5use super::Widget;
6use crate::Align;
7use crate::Surface;
8use crate::text::truncate as truncate_to_cols;
9
10#[derive(Clone, Copy, Debug)]
35pub struct Text<'a> {
36 content: &'a str,
37 style: Style,
38 align: Align,
39}
40
41impl<'a> Text<'a> {
42 #[must_use]
44 pub fn new(content: &'a str) -> Self {
45 Self {
46 content,
47 style: Style::new(),
48 align: Align::Left,
49 }
50 }
51
52 #[must_use]
54 pub const fn style(mut self, style: Style) -> Self {
55 self.style = style;
56 self
57 }
58
59 #[must_use]
61 pub const fn align(mut self, align: Align) -> Self {
62 self.align = align;
63 self
64 }
65}
66
67impl Widget for Text<'_> {
68 fn render(&self, area: Rect, surface: &mut Surface<'_>) {
69 if area.width() == 0 {
70 return;
71 }
72 let text = truncate_to_cols(self.content, area.width_usize());
73 #[allow(clippy::cast_possible_truncation)]
76 let text_width = text.width() as u16;
77 let x = area.left() + self.align.offset(area.width(), text_width);
78 surface.print((x, area.top()), text, self.style);
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use retroglyph_core::{Color, Grid, Pos};
85
86 use super::*;
87
88 #[test]
89 fn prints_the_content_in_the_given_style() {
90 let area = Rect::new(0, 0, 10, 1);
91 let mut grid = Grid::new(10, 1);
92 Text::new("hi")
93 .style(Style::new().fg(Color::WHITE))
94 .render(area, &mut Surface::new(&mut grid, area, 0));
95
96 assert_eq!(grid[Pos::new(0, 0)].glyph(), 'h');
97 assert_eq!(grid[Pos::new(1, 0)].glyph(), 'i');
98 assert_eq!(grid[Pos::new(0, 0)].style().foreground(), Color::WHITE);
99 }
100
101 #[test]
102 fn clips_to_area_width() {
103 let area = Rect::new(0, 0, 5, 1);
104 let mut grid = Grid::new(5, 1);
105 Text::new("a much longer message than fits")
106 .render(area, &mut Surface::new(&mut grid, area, 0));
107
108 assert_eq!(grid[Pos::new(4, 0)].glyph(), 'c'); }
110
111 #[test]
112 fn right_align_places_text_against_the_right_edge() {
113 let area = Rect::new(0, 0, 10, 1);
114 let mut grid = Grid::new(10, 1);
115 Text::new("hi")
116 .align(Align::Right)
117 .render(area, &mut Surface::new(&mut grid, area, 0));
118
119 assert_eq!(grid[Pos::new(8, 0)].glyph(), 'h');
121 assert_eq!(grid[Pos::new(9, 0)].glyph(), 'i');
122 assert_eq!(grid[Pos::new(7, 0)].glyph(), ' ');
123 }
124
125 #[test]
126 fn center_align_centers_text() {
127 let area = Rect::new(0, 0, 10, 1);
128 let mut grid = Grid::new(10, 1);
129 Text::new("hi")
130 .align(Align::Center)
131 .render(area, &mut Surface::new(&mut grid, area, 0));
132
133 assert_eq!(grid[Pos::new(4, 0)].glyph(), 'h');
135 assert_eq!(grid[Pos::new(5, 0)].glyph(), 'i');
136 }
137
138 #[test]
139 fn zero_width_is_a_no_op() {
140 let area = Rect::new(0, 0, 0, 1);
141 let mut grid = Grid::new(1, 1);
142 Text::new("hi").render(area, &mut Surface::new(&mut grid, area, 0));
143 assert_eq!(grid[Pos::new(0, 0)].glyph(), ' ');
144 }
145}