1use embedded_graphics::geometry::Point;
2use embedded_graphics::iterator::raw::RawDataSlice;
3use embedded_graphics::pixelcolor::raw::{BigEndian, RawData, RawU1};
4
5use crate::el::El;
6use crate::icons::icons5::Icons5;
7use crate::icons::{IconData, IconKind, IconSet};
8use crate::layout::{LayoutNode, Limits, Viewport};
9use crate::log::logger::warning;
10use crate::size::Length;
11use crate::{color::UiColor, event::Event, render::Renderer, size::Size, widget::Widget};
12
13pub struct IconPicker;
14
15impl IconPicker {
16 pub fn by_size(&self, size: u32, kind: IconKind) -> Option<IconData> {
17 match size {
18 5.. => Icons5.pick(kind),
19 _ => None,
20 }
21 }
22
23 pub fn flex_size(&self, length: Length, limits: &Limits) -> u32 {
24 let fit_square = limits.resolve_square(length);
25 match fit_square {
26 5.. => 5,
27 _ => 0,
28 }
29 }
30
31 pub fn flex(&self, length: Length, limits: &Limits, kind: IconKind) -> Option<IconData> {
32 let size = self.flex_size(length, limits);
33 self.by_size(size, kind)
34 }
35}
36
37#[derive(Clone, Copy)]
38pub struct Icon<R>
39where
40 R: Renderer,
41{
42 size: Length,
43 kind: IconKind,
44 color: R::Color,
45 background: R::Color,
46}
47
48impl<R> Icon<R>
49where
50 R: Renderer,
51{
52 pub fn new(kind: IconKind) -> Self {
53 Self {
56 size: Length::Fill,
57 kind,
58 color: R::Color::default_foreground(),
59 background: R::Color::default_background(),
60 }
61 }
62
63 pub fn color(mut self, color: impl Into<R::Color>) -> Self {
64 self.color = color.into();
65 self
66 }
67
68 pub fn background(mut self, background: impl Into<R::Color>) -> Self {
69 self.background = background.into();
70 self
71 }
72
73 pub fn invert(mut self) -> Self {
74 self.color = self.background;
75 self.background = self.color;
76 self
77 }
78
79 pub fn size(mut self, size: impl Into<Length>) -> Self {
80 self.size = size.into();
81 self
82 }
83
84 pub fn do_invert(self, invert: bool) -> Self {
85 if invert {
86 self.invert()
87 } else {
88 self
89 }
90 }
91}
92
93impl<Message, R, E, S> Widget<Message, R, E, S> for Icon<R>
94where
95 R: Renderer,
96 E: Event,
97{
98 fn id(&self) -> Option<crate::el::ElId> {
99 None
100 }
101
102 fn tree_ids(&self) -> alloc::vec::Vec<crate::el::ElId> {
103 vec![]
104 }
105
106 fn size(&self) -> crate::size::Size<crate::size::Length> {
107 Size::new_equal(self.size)
108 }
109
110 fn layout(
111 &self,
112 _ctx: &mut crate::ui::UiCtx<Message>,
113 _state: &mut crate::state::StateNode,
114 _styler: &S,
115 limits: &crate::layout::Limits,
116 _viewport: &Viewport,
117 ) -> crate::layout::LayoutNode {
118 let size = Size::new_equal(IconPicker.flex_size(self.size, limits));
119
120 LayoutNode::new(limits.resolve_size(size.width, size.height, size))
121 }
122
123 fn draw(
124 &self,
125 _ctx: &mut crate::ui::UiCtx<Message>,
126 _state: &mut crate::state::StateNode,
127 renderer: &mut R,
128 _styler: &S,
129 layout: crate::layout::Layout,
130 ) {
131 let bounds = layout.bounds();
132 let bounds_size = bounds.size.max_square();
133 let icon = IconPicker.by_size(bounds_size, self.kind);
134
135 if let Some(icon) = icon {
138 let icon_size = icon.size;
139
140 let icon_position = bounds.position
143 + Point::new(bounds.size.width as i32, bounds.size.height as i32) / 2
144 - Point::new_equal(icon_size as i32) / 2;
145
146 let bits_iter = RawDataSlice::<RawU1, BigEndian>::new(&icon.data).into_iter();
147
148 let data_width = icon_size.max(8);
149
150 for (index, bit) in bits_iter.enumerate() {
151 if index % data_width as usize >= icon_size as usize {
152 continue;
153 }
154
155 let y = index / data_width as usize;
156 let x = index % data_width as usize;
157
158 let point = Point::new(x as i32, y as i32) + icon_position;
159
160 let color = match bit.into_inner() {
161 0 => self.background,
162 1 => self.color,
163 _ => unreachable!(),
164 };
165
166 renderer.pixel(point, color);
167 }
168 } else {
169 warning!(
170 "Icon cannot be rendered: icon size: {:?} does not fit into box of size {:?}",
171 bounds_size,
172 bounds.size
173 );
174 }
175 }
176}
177
178impl<'a, Message, R, E, S> From<Icon<R>> for El<'a, Message, R, E, S>
179where
180 Message: 'a,
181 R: Renderer + 'a,
182 E: Event + 'a,
183 S: 'a,
184{
185 fn from(value: Icon<R>) -> Self {
186 Self::new(value)
187 }
188}
189
190impl<'a, Message, R, E, S> From<IconKind> for El<'a, Message, R, E, S>
191where
192 Message: 'a,
193 R: Renderer + 'a,
194 E: Event + 'a,
195 S: 'a,
196{
197 fn from(value: IconKind) -> Self {
198 El::new(Icon::new(value))
199 }
200}
201
202impl<R: Renderer> Into<Icon<R>> for IconKind {
203 fn into(self) -> Icon<R> {
204 Icon::new(self)
205 }
206}