zest_widget/widget/
led.rs1use super::Widget;
11use core::marker::PhantomData;
12use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
13use zest_core::{Constraints, Length, RenderError, Renderer, TouchPhase};
14use zest_theme::Theme;
15
16const DEFAULT_DIAMETER: u32 = 16;
18
19pub struct LED<C: PixelColor, M: Clone> {
21 rect: Rectangle,
22 on_color: C,
24 off_color: Option<C>,
26 on: bool,
28 width: Length,
29 height: Length,
30 _phantom: PhantomData<M>,
31}
32
33impl<C: PixelColor, M: Clone> LED<C, M> {
34 pub fn new(color: C) -> Self {
37 Self {
38 rect: Rectangle::zero(),
39 on_color: color,
40 off_color: None,
41 on: true,
42 width: Length::Fixed(DEFAULT_DIAMETER),
43 height: Length::Fixed(DEFAULT_DIAMETER),
44 _phantom: PhantomData,
45 }
46 }
47
48 pub fn from_state(on: bool, on_color: C) -> Self {
52 Self {
53 rect: Rectangle::zero(),
54 on_color,
55 off_color: None,
56 on,
57 width: Length::Fixed(DEFAULT_DIAMETER),
58 height: Length::Fixed(DEFAULT_DIAMETER),
59 _phantom: PhantomData,
60 }
61 }
62
63 #[must_use]
65 pub fn on(mut self, on: bool) -> Self {
66 self.on = on;
67 self
68 }
69
70 #[must_use]
72 pub fn on_color(mut self, color: C) -> Self {
73 self.on_color = color;
74 self
75 }
76
77 #[must_use]
79 pub fn off_color(mut self, color: C) -> Self {
80 self.off_color = Some(color);
81 self
82 }
83
84 #[must_use]
86 pub fn width(mut self, width: impl Into<Length>) -> Self {
87 self.width = width.into();
88 self
89 }
90
91 #[must_use]
93 pub fn height(mut self, height: impl Into<Length>) -> Self {
94 self.height = height.into();
95 self
96 }
97}
98
99impl<C: PixelColor, M: Clone> Widget<C, M> for LED<C, M> {
100 fn measure(&mut self, constraints: Constraints) -> Size {
101 let w = self.width.resolve(DEFAULT_DIAMETER, constraints.max.width);
102 let h = self
103 .height
104 .resolve(DEFAULT_DIAMETER, constraints.max.height);
105 constraints.clamp(Size::new(w, h))
106 }
107
108 fn preferred_size(&self) -> (Length, Length) {
109 (self.width, self.height)
110 }
111
112 fn arrange(&mut self, rect: Rectangle) {
113 self.rect = rect;
114 }
115
116 fn rect(&self) -> Rectangle {
117 self.rect
118 }
119
120 fn handle_touch(&mut self, _point: Point, _phase: TouchPhase) -> Option<M> {
121 None
122 }
123
124 fn draw<'t>(
125 &self,
126 renderer: &mut dyn Renderer<C>,
127 theme: &Theme<'t, C>,
128 ) -> Result<(), RenderError> {
129 let color = if self.on {
130 self.on_color
131 } else {
132 self.off_color.unwrap_or(theme.background.divider)
133 };
134 let radius = self.rect.size.width.min(self.rect.size.height) / 2;
136 if radius == 0 {
137 return Ok(());
138 }
139 let center = self.rect.top_left
140 + Point::new(
141 self.rect.size.width as i32 / 2,
142 self.rect.size.height as i32 / 2,
143 );
144 renderer.fill_circle(center, radius, color)
145 }
146}