embedded_text/plugin/
tail.rs

1//! Display the last lines of the text.
2
3use az::SaturatingAs;
4use embedded_graphics::{
5    prelude::PixelColor,
6    text::renderer::{CharacterStyle, TextRenderer},
7};
8
9use crate::{
10    plugin::Plugin,
11    rendering::{cursor::Cursor, TextBoxProperties},
12};
13
14/// Text tail display plugin.
15///
16/// Aligns the last line of the text to be always visible. If the text fits inside the text box,
17/// it will be top aligned. If the text is longer, it will be bottom aligned.
18#[derive(Copy, Clone)]
19pub struct Tail;
20
21impl<'a, C: PixelColor> Plugin<'a, C> for Tail {
22    fn on_start_render<S: CharacterStyle + TextRenderer>(
23        &mut self,
24        cursor: &mut Cursor,
25        props: &TextBoxProperties<'_, S>,
26    ) {
27        let box_height = props.bounding_box.size.height.saturating_as();
28        if props.text_height > box_height {
29            let offset = box_height - props.text_height;
30
31            cursor.y += offset;
32        }
33    }
34}
35
36#[cfg(test)]
37mod test {
38    use embedded_graphics::{
39        mock_display::MockDisplay,
40        mono_font::{ascii::FONT_6X9, MonoTextStyleBuilder},
41        pixelcolor::BinaryColor,
42        prelude::{Point, Size},
43        primitives::Rectangle,
44        Drawable,
45    };
46
47    use crate::{plugin::tail::Tail, style::TextBoxStyle, utils::test::size_for, TextBox};
48
49    #[track_caller]
50    pub fn assert_rendered(text: &str, size: Size, pattern: &[&str]) {
51        let mut display = MockDisplay::new();
52
53        let character_style = MonoTextStyleBuilder::new()
54            .font(&FONT_6X9)
55            .text_color(BinaryColor::On)
56            .background_color(BinaryColor::Off)
57            .build();
58
59        let style = TextBoxStyle::default();
60
61        TextBox::with_textbox_style(
62            text,
63            Rectangle::new(Point::zero(), size),
64            character_style,
65            style,
66        )
67        .add_plugin(Tail)
68        .draw(&mut display)
69        .unwrap();
70
71        display.assert_pattern(pattern);
72    }
73
74    #[test]
75    fn scrolling_behaves_as_top_if_lines_dont_overflow() {
76        assert_rendered(
77            "word",
78            size_for(&FONT_6X9, 4, 2),
79            &[
80                "........................",
81                "......................#.",
82                "......................#.",
83                "#...#...##...#.#....###.",
84                "#.#.#..#..#..##.#..#..#.",
85                "#.#.#..#..#..#.....#..#.",
86                ".#.#....##...#......###.",
87                "........................",
88                "........................",
89                "                        ",
90                "                        ",
91                "                        ",
92                "                        ",
93                "                        ",
94                "                        ",
95                "                        ",
96                "                        ",
97                "                        ",
98            ],
99        );
100    }
101
102    #[test]
103    fn scrolling_behaves_as_bottom_if_lines_overflow() {
104        assert_rendered(
105            "word word2 word3 word4",
106            size_for(&FONT_6X9, 5, 2),
107            &[
108                "..............................",
109                "......................#..####.",
110                "......................#....#..",
111                "#...#...##...#.#....###...##..",
112                "#.#.#..#..#..##.#..#..#.....#.",
113                "#.#.#..#..#..#.....#..#.....#.",
114                ".#.#....##...#......###..###..",
115                "..............................",
116                "..............................",
117                "..............................",
118                "......................#....#..",
119                "......................#...##..",
120                "#...#...##...#.#....###..#.#..",
121                "#.#.#..#..#..##.#..#..#.#..#..",
122                "#.#.#..#..#..#.....#..#.#####.",
123                ".#.#....##...#......###....#..",
124                "..............................",
125                "..............................",
126            ],
127        );
128    }
129}