embedded_menu/selection_indicator/style/
triangle.rs

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
use embedded_graphics::{
    prelude::{DrawTarget, PixelColor, Point, Size},
    primitives::{ContainsPoint, Primitive, PrimitiveStyle, Rectangle, Triangle as TriangleShape},
    transform::Transform,
    Drawable,
};
use embedded_layout::prelude::{horizontal::LeftToRight, vertical::Center, Align};

use crate::{
    interaction::InputState,
    selection_indicator::{
        style::{interpolate, IndicatorStyle},
        Insets,
    },
    theme::Theme,
};

#[derive(Clone, Copy)]
pub struct Triangle;

impl IndicatorStyle for Triangle {
    type Shape = Arrow;
    type State = ();

    fn padding(&self, _state: &Self::State, height: i32) -> Insets {
        Insets {
            left: height / 2 + 1,
            top: 0,
            right: 0,
            bottom: 0,
        }
    }

    fn shape(&self, _state: &Self::State, bounds: Rectangle, fill_width: u32) -> Self::Shape {
        Arrow::new(bounds, fill_width)
    }

    fn draw<T, D>(
        &self,
        state: &Self::State,
        input_state: InputState,
        theme: &T,
        display: &mut D,
    ) -> Result<Self::Shape, D::Error>
    where
        T: Theme,
        D: DrawTarget<Color = T::Color>,
    {
        let display_area = display.bounding_box();

        let fill_width = if let InputState::InProgress(progress) = input_state {
            interpolate(progress as u32, 0, 255, 0, display_area.size.width)
        } else {
            0
        };

        let shape = self.shape(state, display_area, fill_width);

        shape.draw(theme.selection_color(), display)?;

        Ok(shape)
    }
}

#[derive(Clone, Copy)]
pub struct Arrow {
    body: Rectangle,
    tip: TriangleShape,
}

const SHRINK: i32 = 1;

impl Arrow {
    pub fn new(bounds: Rectangle, fill_width: u32) -> Self {
        let body = Rectangle::new(bounds.top_left, Size::new(fill_width, bounds.size.height));

        let tip = TriangleShape::new(
            Point::new(0, SHRINK),
            Point::new(0, Self::tip_width(bounds)),
            Point::new(
                bounds.size.height as i32 / 2 - SHRINK,
                bounds.size.height as i32 / 2,
            ),
        )
        .align_to(&body, LeftToRight, Center)
        // e-layout doesn't align well to 0 area rectangles
        .translate(Point::new(if body.is_zero_sized() { -1 } else { 0 }, 0));

        Self { body, tip }
    }

    pub fn tip_width(bounds: Rectangle) -> i32 {
        bounds.size.height as i32 - 1 - SHRINK
    }

    pub fn draw<D, C>(&self, color: C, target: &mut D) -> Result<(), D::Error>
    where
        C: PixelColor,
        D: DrawTarget<Color = C>,
    {
        let style = PrimitiveStyle::with_fill(color);

        self.body.into_styled(style).draw(target)?;
        self.tip.into_styled(style).draw(target)?;

        Ok(())
    }
}

impl ContainsPoint for Arrow {
    fn contains(&self, point: Point) -> bool {
        self.body.contains(point) || self.tip.contains(point)
    }
}

impl Transform for Arrow {
    fn translate(&self, by: Point) -> Self {
        Self {
            body: self.body.translate(by),
            tip: self.tip.translate(by),
        }
    }

    fn translate_mut(&mut self, by: Point) -> &mut Self {
        self.body.translate_mut(by);
        self.tip.translate_mut(by);
        self
    }
}