1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Pixel iterators used for text rendering.
#[cfg(feature = "ansi")]
pub mod ansi;
pub mod character;
pub mod cursor;
pub mod line;
pub mod line_iter;
pub mod modified_whitespace;
pub mod space_config;
pub mod whitespace;

use crate::{
    alignment::{HorizontalTextAlignment, VerticalTextAlignment},
    parser::{Parser, Token},
    rendering::{cursor::Cursor, line::StyledLinePixelIterator, space_config::SpaceConfig},
    style::{color::Rgb, height_mode::HeightMode, TextBoxStyle},
    StyledTextBox,
};
use embedded_graphics::prelude::*;

/// State variable used by the right aligned text renderer.
#[derive(Debug)]
pub enum State<'a, C, F, SP, A, V, H>
where
    C: PixelColor,
    F: Font + Copy,
{
    /// Starts processing a line.
    NextLine(Option<Token<'a>>, Cursor<F>, Parser<'a>),

    /// Renders the processed line.
    DrawLine(StyledLinePixelIterator<'a, C, F, SP, A, V, H>),
}

/// This trait is used to associate a renderer type to a horizontal alignment option.
///
/// Implementing this trait is only necessary when creating new alignment algorithms.
pub trait RendererFactory<'a, C: PixelColor> {
    /// The type of the pixel iterator.
    type Renderer: Iterator<Item = Pixel<C>>;

    /// Creates a new renderer object.
    fn create_renderer(&self) -> Self::Renderer;
}

type LineIteratorSource<'a, C, F, A, V, H, SP> =
    fn(
        TextBoxStyle<C, F, A, V, H>,
        Option<Token<'a>>,
        Cursor<F>,
        Parser<'a>,
    ) -> StyledLinePixelIterator<'a, C, F, SP, A, V, H>;

/// Pixel iterator for styled text.
pub struct StyledTextBoxIterator<'a, C, F, A, V, H, SP>
where
    C: PixelColor,
    F: Font + Copy,
{
    style: TextBoxStyle<C, F, A, V, H>,
    state: State<'a, C, F, SP, A, V, H>,
    next_line_fn: LineIteratorSource<'a, C, F, A, V, H, SP>,
}

impl<'a, C, F, A, V, H, SP> StyledTextBoxIterator<'a, C, F, A, V, H, SP>
where
    C: PixelColor,
    F: Font + Copy,
    A: HorizontalTextAlignment,
    V: VerticalTextAlignment,
    H: HeightMode,
    SP: SpaceConfig<Font = F>,
{
    /// Creates a new pixel iterator to render the styled [`TextBox`].
    ///
    /// [`TextBox`]: ../struct.TextBox.html
    #[inline]
    #[must_use]
    pub fn new(
        styled: &StyledTextBox<'a, C, F, A, V, H>,
        f: LineIteratorSource<'a, C, F, A, V, H, SP>,
    ) -> Self {
        let mut cursor = Cursor::new(styled.text_box.bounds, styled.style.line_spacing);

        V::apply_vertical_alignment(&mut cursor, &styled);

        Self {
            style: styled.style,
            state: State::NextLine(None, cursor, Parser::parse(styled.text_box.text)),
            next_line_fn: f,
        }
    }
}

impl<'a, C, F, A, V, H, SP> Iterator for StyledTextBoxIterator<'a, C, F, A, V, H, SP>
where
    C: PixelColor + From<Rgb>,
    F: Font + Copy,
    A: HorizontalTextAlignment,
    V: VerticalTextAlignment,
    H: HeightMode,
    SP: SpaceConfig<Font = F>,
{
    type Item = Pixel<C>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.state {
                State::NextLine(ref carried_token, ref cursor, ref parser) => {
                    if carried_token.is_none() && parser.is_empty() {
                        break None;
                    }

                    let f = self.next_line_fn;
                    self.state = State::DrawLine(f(
                        self.style,
                        carried_token.clone(),
                        *cursor,
                        parser.clone(),
                    ));
                }

                State::DrawLine(ref mut line_iterator) => {
                    if let pixel @ Some(_) = line_iterator.next() {
                        break pixel;
                    }

                    self.style = line_iterator.style;
                    self.state = State::NextLine(
                        line_iterator.remaining_token(),
                        line_iterator.cursor(),
                        line_iterator.parser(),
                    );
                }
            }
        }
    }
}