diff --git a/src/alignment/center.rs b/src/alignment/center.rs index 13d9d83e..becf4681 100644 --- a/src/alignment/center.rs +++ b/src/alignment/center.rs @@ -1,7 +1,7 @@ //! Horizontal and vertical center aligned text. use crate::{ alignment::{HorizontalTextAlignment, VerticalTextAlignment}, - parser::Token, + parser::{Parser, Token}, rendering::{ cursor::Cursor, line::{StyledLineIterator, UniformSpaceConfig}, @@ -30,13 +30,13 @@ where F: Font + Copy, { /// Starts processing a line. - NextLine(Option>, Cursor), + NextLine(Option>, Cursor, Parser<'a>), /// Renders the processed line. DrawLine(StyledLineIterator<'a, C, F, UniformSpaceConfig, CenterAligned>), } -impl<'a, C, F, V> StateFactory for StyledTextBox<'a, C, F, CenterAligned, V> +impl<'a, C, F, V> StateFactory<'a, F> for StyledTextBox<'a, C, F, CenterAligned, V> where C: PixelColor, F: Font + Copy, @@ -46,8 +46,8 @@ where #[inline] #[must_use] - fn create_state(&self, cursor: Cursor) -> Self::PixelIteratorState { - State::NextLine(None, cursor) + fn create_state(&self, cursor: Cursor, parser: Parser<'a>) -> Self::PixelIteratorState { + State::NextLine(None, cursor, parser) } } @@ -63,26 +63,25 @@ where fn next(&mut self) -> Option { loop { match self.state { - State::NextLine(ref carried_token, ref mut cursor) => { + State::NextLine(ref carried_token, mut cursor, ref mut parser) => { if !cursor.in_display_area() { break None; } - if carried_token.is_none() && self.parser.is_empty() { + if carried_token.is_none() && parser.is_empty() { break None; } + let parser_clone = parser.clone(); let max_line_width = cursor.line_width(); - let (width, _, _) = self.style.measure_line( - &mut self.parser.clone(), - carried_token.clone(), - max_line_width, - ); + let (width, _, _) = + self.style + .measure_line(parser, carried_token.clone(), max_line_width); cursor.advance_unchecked((max_line_width - width + 1) / 2); self.state = State::DrawLine(StyledLineIterator::new( - self.parser.clone(), - *cursor, + parser_clone, + cursor, UniformSpaceConfig { space_width: F::total_char_width(' '), }, @@ -96,10 +95,13 @@ where break pixel; } - self.parser = line_iterator.parser.clone(); let carried_token = line_iterator.remaining_token(); - self.state = State::NextLine(carried_token, line_iterator.cursor); + self.state = State::NextLine( + carried_token, + line_iterator.cursor, + line_iterator.parser.clone(), + ); } } } diff --git a/src/alignment/justified.rs b/src/alignment/justified.rs index 02eee02b..f9d5466d 100644 --- a/src/alignment/justified.rs +++ b/src/alignment/justified.rs @@ -1,7 +1,7 @@ //! Fully justified text. use crate::{ alignment::{HorizontalTextAlignment, VerticalTextAlignment}, - parser::Token, + parser::{Parser, Token}, rendering::{ cursor::Cursor, line::{SpaceConfig, StyledLineIterator}, @@ -76,13 +76,13 @@ where F: Font + Copy, { /// Starts processing a line. - NextLine(Option>, Cursor), + NextLine(Option>, Cursor, Parser<'a>), /// Renders the processed line. DrawLine(StyledLineIterator<'a, C, F, JustifiedSpaceConfig, Justified>), } -impl<'a, C, F, V> StateFactory for StyledTextBox<'a, C, F, Justified, V> +impl<'a, C, F, V> StateFactory<'a, F> for StyledTextBox<'a, C, F, Justified, V> where C: PixelColor, F: Font + Copy, @@ -92,8 +92,8 @@ where #[inline] #[must_use] - fn create_state(&self, cursor: Cursor) -> Self::PixelIteratorState { - State::NextLine(None, cursor) + fn create_state(&self, cursor: Cursor, parser: Parser<'a>) -> Self::PixelIteratorState { + State::NextLine(None, cursor, parser) } } @@ -109,21 +109,20 @@ where fn next(&mut self) -> Option { loop { match self.state { - State::NextLine(ref carried_token, ref cursor) => { + State::NextLine(ref carried_token, cursor, ref mut parser) => { if !cursor.in_display_area() { break None; } - if carried_token.is_none() && self.parser.is_empty() { + if carried_token.is_none() && parser.is_empty() { break None; } + let parser_clone = parser.clone(); let max_line_width = cursor.line_width(); - let (width, total_whitespace_count, t) = self.style.measure_line( - &mut self.parser.clone(), - carried_token.clone(), - max_line_width, - ); + let (width, total_whitespace_count, t) = + self.style + .measure_line(parser, carried_token.clone(), max_line_width); let space = max_line_width - (width - total_whitespace_count * F::total_char_width(' ')); @@ -138,8 +137,8 @@ where }; self.state = State::DrawLine(StyledLineIterator::new( - self.parser.clone(), - *cursor, + parser_clone, + cursor, space_info, self.style.text_style, carried_token.clone(), @@ -151,10 +150,11 @@ where break pixel; } - self.parser = line_iterator.parser.clone(); - let carried_token = line_iterator.remaining_token(); - - self.state = State::NextLine(carried_token, line_iterator.cursor); + self.state = State::NextLine( + line_iterator.remaining_token(), + line_iterator.cursor, + line_iterator.parser.clone(), + ); } } } diff --git a/src/alignment/left.rs b/src/alignment/left.rs index 203bc5d9..6f366d5c 100644 --- a/src/alignment/left.rs +++ b/src/alignment/left.rs @@ -1,7 +1,7 @@ //! Left aligned text. use crate::{ alignment::{HorizontalTextAlignment, VerticalTextAlignment}, - parser::Token, + parser::{Parser, Token}, rendering::{ cursor::Cursor, line::{StyledLineIterator, UniformSpaceConfig}, @@ -28,13 +28,13 @@ where F: Font + Copy, { /// Starts processing a line. - NextLine(Option>, Cursor), + NextLine(Option>, Cursor, Parser<'a>), /// Renders the processed line. DrawLine(StyledLineIterator<'a, C, F, UniformSpaceConfig, LeftAligned>), } -impl<'a, C, F, V> StateFactory for StyledTextBox<'a, C, F, LeftAligned, V> +impl<'a, C, F, V> StateFactory<'a, F> for StyledTextBox<'a, C, F, LeftAligned, V> where C: PixelColor, F: Font + Copy, @@ -44,8 +44,8 @@ where #[inline] #[must_use] - fn create_state(&self, cursor: Cursor) -> Self::PixelIteratorState { - State::NextLine(None, cursor) + fn create_state(&self, cursor: Cursor, parser: Parser<'a>) -> Self::PixelIteratorState { + State::NextLine(None, cursor, parser) } } @@ -61,18 +61,18 @@ where fn next(&mut self) -> Option { loop { match self.state { - State::NextLine(ref carried_token, ref cursor) => { + State::NextLine(ref carried_token, cursor, ref parser) => { if !cursor.in_display_area() { break None; } - if carried_token.is_none() && self.parser.is_empty() { + if carried_token.is_none() && parser.is_empty() { break None; } self.state = State::DrawLine(StyledLineIterator::new( - self.parser.clone(), - *cursor, + parser.clone(), + cursor, UniformSpaceConfig { space_width: F::total_char_width(' '), }, @@ -86,10 +86,11 @@ where break pixel; } - self.parser = line_iterator.parser.clone(); - let carried_token = line_iterator.remaining_token(); - - self.state = State::NextLine(carried_token, line_iterator.cursor); + self.state = State::NextLine( + line_iterator.remaining_token(), + line_iterator.cursor, + line_iterator.parser.clone(), + ); } }; } diff --git a/src/alignment/right.rs b/src/alignment/right.rs index b58be655..4ddae363 100644 --- a/src/alignment/right.rs +++ b/src/alignment/right.rs @@ -1,7 +1,7 @@ //! Right aligned text. use crate::{ alignment::{HorizontalTextAlignment, VerticalTextAlignment}, - parser::Token, + parser::{Parser, Token}, rendering::{ cursor::Cursor, line::{StyledLineIterator, UniformSpaceConfig}, @@ -28,13 +28,13 @@ where F: Font + Copy, { /// Starts processing a line. - NextLine(Option>, Cursor), + NextLine(Option>, Cursor, Parser<'a>), /// Renders the processed line. DrawLine(StyledLineIterator<'a, C, F, UniformSpaceConfig, RightAligned>), } -impl<'a, C, F, V> StateFactory for StyledTextBox<'a, C, F, RightAligned, V> +impl<'a, C, F, V> StateFactory<'a, F> for StyledTextBox<'a, C, F, RightAligned, V> where C: PixelColor, F: Font + Copy, @@ -44,8 +44,8 @@ where #[inline] #[must_use] - fn create_state(&self, cursor: Cursor) -> Self::PixelIteratorState { - State::NextLine(None, cursor) + fn create_state(&self, cursor: Cursor, parser: Parser<'a>) -> Self::PixelIteratorState { + State::NextLine(None, cursor, parser) } } @@ -61,26 +61,25 @@ where fn next(&mut self) -> Option { loop { match self.state { - State::NextLine(ref carried_token, ref mut cursor) => { + State::NextLine(ref carried_token, mut cursor, ref mut parser) => { if !cursor.in_display_area() { break None; } - if carried_token.is_none() && self.parser.is_empty() { + if carried_token.is_none() && parser.is_empty() { break None; } + let parser_clone = parser.clone(); let max_line_width = cursor.line_width(); - let (width, _, _) = self.style.measure_line( - &mut self.parser.clone(), - carried_token.clone(), - max_line_width, - ); + let (width, _, _) = + self.style + .measure_line(parser, carried_token.clone(), max_line_width); cursor.advance_unchecked(max_line_width - width); self.state = State::DrawLine(StyledLineIterator::new( - self.parser.clone(), - *cursor, + parser_clone, + cursor, UniformSpaceConfig { space_width: F::total_char_width(' '), }, @@ -94,10 +93,11 @@ where break pixel; } - self.parser = line_iterator.parser.clone(); - let carried_token = line_iterator.remaining_token(); - - self.state = State::NextLine(carried_token, line_iterator.cursor); + self.state = State::NextLine( + line_iterator.remaining_token(), + line_iterator.cursor, + line_iterator.parser.clone(), + ); } } } diff --git a/src/rendering/cursor.rs b/src/rendering/cursor.rs index 540bc957..f047cb25 100644 --- a/src/rendering/cursor.rs +++ b/src/rendering/cursor.rs @@ -7,15 +7,11 @@ use embedded_graphics::{fonts::Font, geometry::Point, primitives::Rectangle}; /// [`TextBox`]: ../../struct.TextBox.html #[derive(Copy, Clone, Debug)] pub struct Cursor { - _marker: PhantomData, - /// Current cursor position pub position: Point, - left: i32, - right: i32, - bottom: i32, - top: i32, + _marker: PhantomData, + bounds: Rectangle, } impl Cursor { @@ -26,10 +22,10 @@ impl Cursor { Self { _marker: PhantomData, position: bounds.top_left, - bottom: bounds.bottom_right.y + 1, - top: bounds.top_left.y, - left: bounds.top_left.x, - right: bounds.bottom_right.x + 1, + bounds: Rectangle::new( + bounds.top_left, + bounds.bottom_right + Point::new(1, 1 - F::CHARACTER_SIZE.height as i32), + ), } } @@ -37,7 +33,7 @@ impl Cursor { #[inline] #[must_use] pub fn line_width(&self) -> u32 { - (self.right - self.left) as u32 + (self.bounds.bottom_right.x - self.bounds.top_left.x) as u32 } /// Starts a new line. @@ -49,17 +45,19 @@ impl Cursor { /// Moves the cursor back to the start of the line. #[inline] pub fn carriage_return(&mut self) { - self.position.x = self.left; + self.position.x = self.bounds.top_left.x; } - /// Returns whether the cursor is in the bounding box. + /// Returns whether the cursor is completely in the bounding box. + /// + /// Completely means, that the line that is marked by the cursor can be drawn without any + /// vertical clipping or drawing outside the bounds. /// /// *Note:* Only vertical overrun is checked. #[inline] #[must_use] pub fn in_display_area(&self) -> bool { - self.position.y >= self.top - && (self.position.y + F::CHARACTER_SIZE.height as i32) <= self.bottom + self.bounds.top_left.y <= self.position.y && self.position.y <= self.bounds.bottom_right.y } /// Returns whether the current line has enough space to also include an object of given width. @@ -73,7 +71,7 @@ impl Cursor { #[inline] #[must_use] pub fn space(&self) -> u32 { - (self.right - self.position.x) as u32 + (self.bounds.bottom_right.x - self.position.x) as u32 } /// Advances the cursor by a given amount. diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs index 73777433..7f0c4118 100644 --- a/src/rendering/mod.rs +++ b/src/rendering/mod.rs @@ -16,12 +16,12 @@ use embedded_graphics::prelude::*; /// This trait is used to associate a state type to a horizontal alignment option. /// /// Implementing this trait is only necessary when creating new alignment algorithms. -pub trait StateFactory { +pub trait StateFactory<'a, F: Font> { /// The type of the state variable used for rendering. type PixelIteratorState; /// Creates a new state variable. - fn create_state(&self, cursor: Cursor) -> Self::PixelIteratorState; + fn create_state(&self, cursor: Cursor, parser: Parser<'a>) -> Self::PixelIteratorState; } /// Pixel iterator for styled text. @@ -31,16 +31,13 @@ where F: Font + Copy, A: HorizontalTextAlignment, V: VerticalTextAlignment, - StyledTextBox<'a, C, F, A, V>: StateFactory, + StyledTextBox<'a, C, F, A, V>: StateFactory<'a, F>, { - /// Parser to process the text during rendering. - pub parser: Parser<'a>, - /// Style used for rendering. pub style: TextBoxStyle, /// State information used by the rendering algorithms. - pub state: as StateFactory>::PixelIteratorState, + pub state: as StateFactory<'a, F>>::PixelIteratorState, } impl<'a, C, F, A, V> StyledTextBoxIterator<'a, C, F, A, V> @@ -49,7 +46,7 @@ where F: Font + Copy, A: HorizontalTextAlignment, V: VerticalTextAlignment, - StyledTextBox<'a, C, F, A, V>: StateFactory, + StyledTextBox<'a, C, F, A, V>: StateFactory<'a, F>, { /// Creates a new pixel iterator to render the styled [`TextBox`]. /// @@ -62,9 +59,8 @@ where V::apply_vertical_alignment(&mut cursor, &styled); Self { - parser: Parser::parse(styled.text_box.text), style: styled.style, - state: styled.create_state(cursor), + state: styled.create_state(cursor, Parser::parse(styled.text_box.text)), } } } diff --git a/src/style/mod.rs b/src/style/mod.rs index 1421439c..2e75e63f 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -321,7 +321,7 @@ where A: HorizontalTextAlignment, V: VerticalTextAlignment, StyledTextBoxIterator<'a, C, F, A, V>: Iterator>, - StyledTextBox<'a, C, F, A, V>: StateFactory, + StyledTextBox<'a, C, F, A, V>: StateFactory<'a, F>, { #[inline] fn draw>(self, display: &mut D) -> Result<(), D::Error> {