embedded_menu/selection_indicator/style/
triangle.rs1use embedded_graphics::{
2 prelude::{DrawTarget, PixelColor, Point, Size},
3 primitives::{ContainsPoint, Primitive, PrimitiveStyle, Rectangle, Triangle as TriangleShape},
4 transform::Transform,
5 Drawable,
6};
7use embedded_layout::prelude::{horizontal::LeftToRight, vertical::Center, Align};
8
9use crate::{
10 interaction::InputState,
11 selection_indicator::{
12 style::{interpolate, IndicatorStyle},
13 Insets,
14 },
15 theme::Theme,
16};
17
18#[derive(Clone, Copy)]
19pub struct Triangle;
20
21impl IndicatorStyle for Triangle {
22 type Shape = Arrow;
23 type State = ();
24
25 fn padding(&self, _state: &Self::State, height: i32) -> Insets {
26 Insets {
27 left: height / 2 + 1,
28 top: 0,
29 right: 0,
30 bottom: 0,
31 }
32 }
33
34 fn shape(&self, _state: &Self::State, bounds: Rectangle, fill_width: u32) -> Self::Shape {
35 Arrow::new(bounds, fill_width)
36 }
37
38 fn draw<T, D>(
39 &self,
40 state: &Self::State,
41 input_state: InputState,
42 theme: &T,
43 display: &mut D,
44 ) -> Result<Self::Shape, D::Error>
45 where
46 T: Theme,
47 D: DrawTarget<Color = T::Color>,
48 {
49 let display_area = display.bounding_box();
50
51 let fill_width = if let InputState::InProgress(progress) = input_state {
52 interpolate(progress as u32, 0, 255, 0, display_area.size.width)
53 } else {
54 0
55 };
56
57 let shape = self.shape(state, display_area, fill_width);
58
59 shape.draw(theme.selection_color(), display)?;
60
61 Ok(shape)
62 }
63}
64
65#[derive(Clone, Copy)]
66pub struct Arrow {
67 body: Rectangle,
68 tip: TriangleShape,
69}
70
71const SHRINK: i32 = 1;
72
73impl Arrow {
74 pub fn new(bounds: Rectangle, fill_width: u32) -> Self {
75 let body = Rectangle::new(bounds.top_left, Size::new(fill_width, bounds.size.height));
76
77 let tip = TriangleShape::new(
78 Point::new(0, SHRINK),
79 Point::new(0, Self::tip_width(bounds)),
80 Point::new(
81 bounds.size.height as i32 / 2 - SHRINK,
82 bounds.size.height as i32 / 2,
83 ),
84 )
85 .align_to(&body, LeftToRight, Center)
86 .translate(Point::new(if body.is_zero_sized() { -1 } else { 0 }, 0));
88
89 Self { body, tip }
90 }
91
92 pub fn tip_width(bounds: Rectangle) -> i32 {
93 bounds.size.height as i32 - 1 - SHRINK
94 }
95
96 pub fn draw<D, C>(&self, color: C, target: &mut D) -> Result<(), D::Error>
97 where
98 C: PixelColor,
99 D: DrawTarget<Color = C>,
100 {
101 let style = PrimitiveStyle::with_fill(color);
102
103 self.body.into_styled(style).draw(target)?;
104 self.tip.into_styled(style).draw(target)?;
105
106 Ok(())
107 }
108}
109
110impl ContainsPoint for Arrow {
111 fn contains(&self, point: Point) -> bool {
112 self.body.contains(point) || self.tip.contains(point)
113 }
114}
115
116impl Transform for Arrow {
117 fn translate(&self, by: Point) -> Self {
118 Self {
119 body: self.body.translate(by),
120 tip: self.tip.translate(by),
121 }
122 }
123
124 fn translate_mut(&mut self, by: Point) -> &mut Self {
125 self.body.translate_mut(by);
126 self.tip.translate_mut(by);
127 self
128 }
129}