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
mod navigation_item;
pub mod select;

pub use navigation_item::NavigationItem;
pub use select::Select;

use crate::{
    interaction::InteractionController,
    margin::{Margin, MarginExt},
    selection_indicator::{style::IndicatorStyle, SelectionIndicatorController},
    MenuItem, MenuStyle,
};
use embedded_graphics::{
    draw_target::DrawTarget,
    pixelcolor::Rgb888,
    prelude::{PixelColor, Point, Size},
    primitives::{Rectangle, StyledDrawable},
    text::{renderer::TextRenderer, Baseline},
    Drawable,
};
use embedded_layout::prelude::*;
use embedded_text::{alignment::HorizontalAlignment, style::TextBoxStyleBuilder, TextBox};

pub struct MenuLine<I> {
    bounds: Margin<Rectangle>,
    value_width: u32,
    item: I,
}

impl<I> MenuLine<I>
where
    I: MenuItem,
{
    pub fn new<C, S, IT, P>(item: I, style: MenuStyle<C, S, IT, P>) -> MenuLine<I>
    where
        C: PixelColor,
        S: IndicatorStyle,
        IT: InteractionController,
        P: SelectionIndicatorController,
    {
        let style = style.text_style();

        let longest_value = item.longest_value_str();

        let value_width = style
            .measure_string(longest_value, Point::zero(), Baseline::Top)
            .bounding_box
            .size
            .width;

        MenuLine {
            item,
            value_width,
            bounds: Rectangle::new(
                Point::zero(),
                Size::new(1, style.font.character_size.height),
            )
            .with_margin(0, 0, -1, 0),
        }
    }

    pub fn as_item(&self) -> &I {
        &self.item
    }

    pub fn as_item_mut(&mut self) -> &mut I {
        &mut self.item
    }
}

impl<I> View for MenuLine<I> {
    fn translate_impl(&mut self, by: Point) {
        self.bounds.translate_mut(by);
    }

    fn bounds(&self) -> Rectangle {
        self.bounds.bounds()
    }
}

impl<C, S, I, IT, P> StyledDrawable<MenuStyle<C, S, IT, P>> for MenuLine<I>
where
    C: PixelColor + From<Rgb888>,
    I: MenuItem,
    S: IndicatorStyle,
    IT: InteractionController,
    P: SelectionIndicatorController,
{
    type Color = C;
    type Output = ();

    fn draw_styled<D>(
        &self,
        style: &MenuStyle<C, S, IT, P>,
        display: &mut D,
    ) -> Result<Self::Output, D::Error>
    where
        D: DrawTarget<Color = Self::Color>,
    {
        let text_bounds = self.bounds.bounds();
        let display_area = display.bounding_box();

        if text_bounds.intersection(&display_area).is_zero_sized() {
            return Ok(());
        }

        let text_style = style.text_style();
        let value_text = self.item.value();

        let mut inner_bounds = self.bounds.inner.bounds();
        inner_bounds.size.width = display_area.size.width - self.bounds.left as u32;
        TextBox::with_textbox_style(
            value_text,
            inner_bounds,
            text_style,
            TextBoxStyleBuilder::new()
                .alignment(HorizontalAlignment::Right)
                .build(),
        )
        .draw(display)?;

        inner_bounds.size.width -= self.value_width;
        TextBox::new(self.item.title(), inner_bounds, text_style).draw(display)?;

        Ok(())
    }
}