embedded_text/style/height_mode.rs
1//! Height adjustment options.
2//!
3//! This module defines various options to set the height of a [`TextBox`]. Although it is
4//! necessary to specify the size of a [`TextBox`], sometimes we may want the text to stretch or
5//! shrink the text box. Height modes help us achieve this.
6use crate::{
7 plugin::PluginMarker as Plugin, rendering::cursor::Cursor, style::VerticalOverdraw, TextBox,
8};
9use core::ops::Range;
10use embedded_graphics::{geometry::Dimensions, text::renderer::TextRenderer};
11
12/// Specifies how the [`TextBox`]'s height should be adjusted.
13#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
14pub enum HeightMode {
15 /// Keep the original [`TextBox`] height.
16 ///
17 /// # Example: default mode is `Exact(FullRowsOnly)`
18 ///
19 /// ```rust
20 /// # use embedded_graphics::{
21 /// # mono_font::{ascii::FONT_6X9, MonoTextStyle},
22 /// # pixelcolor::BinaryColor,
23 /// # prelude::*,
24 /// # };
25 /// # let character_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
26 /// #
27 /// use embedded_graphics::primitives::Rectangle;
28 /// use embedded_text::TextBox;
29 ///
30 /// // The default option is Exact, which does not change the size of the TextBox.
31 /// let bounding_box = Rectangle::new(Point::zero(), Size::new(60, 60));
32 /// let text_box = TextBox::new(
33 /// "Two lines\nof text",
34 /// bounding_box,
35 /// character_style,
36 /// );
37 ///
38 /// assert_eq!(text_box.bounding_box().size, Size::new(60, 60));
39 /// ```
40 ///
41 /// # Example: display everything inside the bounding box.
42 ///
43 /// ```rust
44 /// # use embedded_graphics::{
45 /// # mono_font::{ascii::FONT_6X9, MonoTextStyle},
46 /// # pixelcolor::BinaryColor,
47 /// # prelude::*,
48 /// # };
49 /// # let character_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
50 /// #
51 /// use embedded_graphics::primitives::Rectangle;
52 /// use embedded_text::{TextBox, style::{HeightMode, VerticalOverdraw}};
53 ///
54 /// // `HeightMode::Exact(VerticalOverdraw::Hidden)` will display everything inside the text
55 /// // box, not just completely visible rows.
56 /// let bounding_box = Rectangle::new(Point::zero(), Size::new(60, 10));
57 /// let text_box = TextBox::with_height_mode(
58 /// "Two lines\nof text",
59 /// bounding_box,
60 /// character_style,
61 /// HeightMode::Exact(VerticalOverdraw::Hidden),
62 /// );
63 ///
64 /// assert_eq!(text_box.bounding_box().size, Size::new(60, 10));
65 /// ```
66 Exact(VerticalOverdraw),
67
68 /// Sets the height of the [`TextBox`] to exactly fit the text.
69 ///
70 /// Note: in this mode, vertical alignment is meaningless. Make sure to use [`Top`] alignment
71 /// for efficiency.
72 ///
73 /// # Example: `FitToText` shrinks the [`TextBox`].
74 ///
75 /// ```rust
76 /// # use embedded_graphics::{
77 /// # mono_font::{ascii::FONT_6X9, MonoTextStyle},
78 /// # pixelcolor::BinaryColor,
79 /// # prelude::*,
80 /// # };
81 /// # let character_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
82 /// use embedded_graphics::primitives::Rectangle;
83 /// use embedded_text::{TextBox, style::HeightMode};
84 ///
85 /// // FitToText shrinks the TextBox to the height of the text
86 /// let bounding_box = Rectangle::new(Point::zero(), Size::new(60, 60));
87 /// let text_box = TextBox::with_height_mode(
88 /// "Two lines\nof text",
89 /// bounding_box,
90 /// character_style,
91 /// HeightMode::FitToText,
92 /// );
93 ///
94 /// assert_eq!(text_box.bounding_box().size, Size::new(60, 18));
95 /// ```
96 ///
97 /// # Example: `FitToText` grows the [`TextBox`].
98 ///
99 /// ```rust
100 /// # use embedded_graphics::{
101 /// # mono_font::{ascii::FONT_6X9, MonoTextStyle},
102 /// # pixelcolor::BinaryColor,
103 /// # prelude::*,
104 /// # };
105 /// # let character_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
106 /// use embedded_graphics::primitives::Rectangle;
107 /// use embedded_text::{TextBox, style::HeightMode};
108 ///
109 /// // FitToText grows the TextBox to the height of the text
110 /// let bounding_box = Rectangle::new(Point::zero(), Size::new(60, 0));
111 /// let text_box = TextBox::with_height_mode(
112 /// "Two lines\nof text",
113 /// bounding_box,
114 /// character_style,
115 /// HeightMode::FitToText,
116 /// );
117 ///
118 /// assert_eq!(text_box.bounding_box().size, Size::new(60, 18));
119 /// ```
120 ///
121 /// [`Top`]: crate::alignment::VerticalAlignment::Top
122 FitToText,
123
124 /// If the text does not fill the bounding box, shrink the [`TextBox`] to be as tall as the
125 /// text.
126 ///
127 /// # Example: `ShrinkToText` does not grow the [`TextBox`].
128 ///
129 /// ```rust
130 /// # use embedded_graphics::{
131 /// # mono_font::{ascii::FONT_6X9, MonoTextStyle},
132 /// # pixelcolor::BinaryColor,
133 /// # prelude::*,
134 /// # };
135 /// # let character_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
136 /// #
137 /// use embedded_graphics::primitives::Rectangle;
138 /// use embedded_text::{TextBox, style::{HeightMode, VerticalOverdraw}};
139 ///
140 /// // This TextBox contains two lines of text, but is 0px high
141 /// let bounding_box = Rectangle::new(Point::zero(), Size::new(60, 0));
142 /// let text_box = TextBox::with_height_mode(
143 /// "Two lines\nof text",
144 /// bounding_box,
145 /// character_style,
146 /// HeightMode::ShrinkToText(VerticalOverdraw::FullRowsOnly),
147 /// );
148 ///
149 /// assert_eq!(text_box.bounding_box().size, Size::new(60, 0));
150 /// ```
151 ///
152 /// # Example: `ShrinkToText` shrinks the [`TextBox`].
153 ///
154 /// ```rust
155 /// # use embedded_graphics::{
156 /// # mono_font::{ascii::FONT_6X9, MonoTextStyle},
157 /// # pixelcolor::BinaryColor,
158 /// # prelude::*,
159 /// # };
160 /// # let character_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
161 /// #
162 /// use embedded_graphics::primitives::Rectangle;
163 /// use embedded_text::{TextBox, style::{HeightMode, VerticalOverdraw}};
164 ///
165 /// // This TextBox contains two lines of text, but is 60px high
166 /// let bounding_box = Rectangle::new(Point::zero(), Size::new(60, 60));
167 /// let text_box = TextBox::with_height_mode(
168 /// "Two lines\nof text",
169 /// bounding_box,
170 /// character_style,
171 /// HeightMode::ShrinkToText(VerticalOverdraw::Hidden),
172 /// );
173 ///
174 /// assert_eq!(text_box.bounding_box().size, Size::new(60, 18));
175 /// ```
176 ShrinkToText(VerticalOverdraw),
177}
178
179impl HeightMode {
180 /// Apply the height mode to the text box.
181 ///
182 /// *Note:* This function normally does not need to be called manually.
183 pub(crate) fn apply<'a, F, M>(self, text_box: &mut TextBox<'a, F, M>)
184 where
185 F: TextRenderer,
186 M: Plugin<'a, F::Color>,
187 {
188 match self {
189 HeightMode::Exact(_) => {}
190 HeightMode::FitToText => {
191 text_box.fit_height();
192 }
193 HeightMode::ShrinkToText(_) => {
194 text_box.fit_height_limited(text_box.bounding_box().size.height);
195 }
196 }
197 }
198
199 /// Calculate the range of rows of the current line that can be drawn.
200 ///
201 /// If a line does not fully fit in the bounding box, some `HeightMode` options allow drawing
202 /// partial lines. For a partial line, this function calculates, which rows of each character
203 /// should be displayed.
204 pub(crate) fn calculate_displayed_row_range(self, cursor: &Cursor) -> Range<u32> {
205 let overdraw = match self {
206 HeightMode::Exact(overdraw) | HeightMode::ShrinkToText(overdraw) => overdraw,
207 HeightMode::FitToText => VerticalOverdraw::Visible,
208 };
209
210 overdraw.calculate_displayed_row_range(cursor)
211 }
212}