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}