1use core::marker::PhantomData;
2
3use embedded_graphics::{
4 mono_font::MonoTextStyle,
5 pixelcolor::BinaryColor,
6 prelude::{DrawTarget, Point, Size},
7 primitives::Rectangle,
8};
9use embedded_layout::{object_chain::ChainElement, prelude::*, view_group::ViewGroup};
10
11use crate::items::{Marker, MenuListItem};
12
13pub trait MenuItemCollection<R> {
15 fn bounds_of(&self, nth: usize) -> Rectangle;
16 fn value_of(&self, nth: usize) -> R;
17 fn interact_with(&mut self, nth: usize) -> R;
18 fn selectable(&self, nth: usize) -> bool;
20 fn count(&self) -> usize;
21 fn draw_styled<D>(
22 &self,
23 text_style: &MonoTextStyle<'static, BinaryColor>,
24 display: &mut D,
25 ) -> Result<(), D::Error>
26 where
27 D: DrawTarget<Color = BinaryColor>;
28}
29
30impl<I, R> MenuItemCollection<R> for I
32where
33 I: MenuListItem<R> + Marker,
34{
35 fn bounds_of(&self, nth: usize) -> Rectangle {
36 debug_assert!(nth == 0);
37 self.bounds()
38 }
39
40 fn value_of(&self, nth: usize) -> R {
41 debug_assert!(nth == 0);
42 self.value_of()
43 }
44
45 fn interact_with(&mut self, nth: usize) -> R {
46 debug_assert!(nth == 0);
47 self.interact()
48 }
49
50 fn selectable(&self, nth: usize) -> bool {
51 debug_assert!(nth == 0);
52 self.selectable()
53 }
54
55 fn count(&self) -> usize {
56 1
57 }
58
59 fn draw_styled<D>(
60 &self,
61 text_style: &MonoTextStyle<'static, BinaryColor>,
62 display: &mut D,
63 ) -> Result<(), D::Error>
64 where
65 D: DrawTarget<Color = BinaryColor>,
66 {
67 MenuListItem::draw_styled(self, text_style, display)
68 }
69}
70
71pub struct MenuItems<C, I, R>
72where
73 C: AsRef<[I]> + AsMut<[I]>,
74 I: MenuListItem<R>,
75{
76 items: C,
77 position: Point,
79 _marker: PhantomData<(I, R)>,
80}
81
82impl<C, I, R> MenuItems<C, I, R>
83where
84 C: AsRef<[I]> + AsMut<[I]>,
85 I: MenuListItem<R>,
86{
87 pub fn new(mut items: C) -> Self {
88 let mut offset = 0;
89
90 for item in items.as_mut().iter_mut() {
91 item.translate_mut(Point::new(0, offset));
92 offset += item.bounds().size.height as i32;
93 }
94
95 Self {
96 items,
97 position: Point::zero(),
98 _marker: PhantomData,
99 }
100 }
101}
102
103impl<C, I, R> MenuItemCollection<R> for MenuItems<C, I, R>
104where
105 C: AsRef<[I]> + AsMut<[I]>,
106 I: MenuListItem<R>,
107{
108 fn bounds_of(&self, nth: usize) -> Rectangle {
109 self.items.as_ref()[nth].bounds()
110 }
111
112 fn value_of(&self, nth: usize) -> R {
113 self.items.as_ref()[nth].value_of()
114 }
115
116 fn interact_with(&mut self, nth: usize) -> R {
117 self.items.as_mut()[nth].interact()
118 }
119
120 fn selectable(&self, nth: usize) -> bool {
121 self.items.as_ref()[nth].selectable()
122 }
123
124 fn count(&self) -> usize {
125 self.items.as_ref().len()
126 }
127
128 fn draw_styled<D>(
129 &self,
130 text_style: &MonoTextStyle<'static, BinaryColor>,
131 display: &mut D,
132 ) -> Result<(), D::Error>
133 where
134 D: DrawTarget<Color = BinaryColor>,
135 {
136 for item in self.items.as_ref() {
137 item.draw_styled(text_style, display)?;
138 }
139
140 Ok(())
141 }
142}
143
144impl<C, I, R> View for MenuItems<C, I, R>
145where
146 C: AsRef<[I]> + AsMut<[I]>,
147 I: MenuListItem<R>,
148{
149 fn translate_impl(&mut self, by: Point) {
150 self.position += by;
151 for view in self.items.as_mut().iter_mut() {
152 view.translate_impl(by);
153 }
154 }
155
156 fn bounds(&self) -> Rectangle {
157 let mut size = Size::zero();
158
159 for view in self.items.as_ref().iter() {
160 let view_size = view.bounds().size;
161 size = Size::new(
162 size.width.max(view_size.width),
163 size.height + view_size.height,
164 );
165 }
166
167 Rectangle::new(self.position, size)
168 }
169}
170
171impl<C, I, R> ViewGroup for MenuItems<C, I, R>
172where
173 C: AsRef<[I]> + AsMut<[I]>,
174 I: MenuListItem<R>,
175{
176 fn len(&self) -> usize {
177 self.count()
178 }
179
180 fn at(&self, idx: usize) -> &dyn View {
181 &self.items.as_ref()[idx]
182 }
183
184 fn at_mut(&mut self, idx: usize) -> &mut dyn View {
185 &mut self.items.as_mut()[idx]
186 }
187}
188
189impl<I, R> MenuItemCollection<R> for Chain<I>
190where
191 I: MenuItemCollection<R>,
192{
193 fn bounds_of(&self, nth: usize) -> Rectangle {
194 self.object.bounds_of(nth)
195 }
196
197 fn value_of(&self, nth: usize) -> R {
198 self.object.value_of(nth)
199 }
200
201 fn interact_with(&mut self, nth: usize) -> R {
202 self.object.interact_with(nth)
203 }
204
205 fn selectable(&self, nth: usize) -> bool {
206 self.object.selectable(nth)
207 }
208
209 fn count(&self) -> usize {
210 self.object.count()
211 }
212
213 fn draw_styled<D>(
214 &self,
215 text_style: &MonoTextStyle<'static, BinaryColor>,
216 display: &mut D,
217 ) -> Result<(), D::Error>
218 where
219 D: DrawTarget<Color = BinaryColor>,
220 {
221 self.object.draw_styled(text_style, display)
222 }
223}
224
225impl<I, LE, R> MenuItemCollection<R> for Link<I, LE>
226where
227 I: MenuItemCollection<R>,
228 LE: MenuItemCollection<R> + ChainElement,
229{
230 fn bounds_of(&self, nth: usize) -> Rectangle {
231 let count = self.parent.count();
232 if nth < count {
233 self.parent.bounds_of(nth)
234 } else {
235 self.object.bounds_of(nth - count)
236 }
237 }
238
239 fn value_of(&self, nth: usize) -> R {
240 let count = self.parent.count();
241 if nth < count {
242 self.parent.value_of(nth)
243 } else {
244 self.object.value_of(nth - count)
245 }
246 }
247
248 fn interact_with(&mut self, nth: usize) -> R {
249 let count = self.parent.count();
250 if nth < count {
251 self.parent.interact_with(nth)
252 } else {
253 self.object.interact_with(nth - count)
254 }
255 }
256
257 fn selectable(&self, nth: usize) -> bool {
258 let count = self.parent.count();
259 if nth < count {
260 self.parent.selectable(nth)
261 } else {
262 self.object.selectable(nth - count)
263 }
264 }
265
266 fn count(&self) -> usize {
267 self.object.count() + self.parent.count()
268 }
269
270 fn draw_styled<D>(
271 &self,
272 text_style: &MonoTextStyle<'static, BinaryColor>,
273 display: &mut D,
274 ) -> Result<(), D::Error>
275 where
276 D: DrawTarget<Color = BinaryColor>,
277 {
278 self.parent.draw_styled(text_style, display)?;
279 self.object.draw_styled(text_style, display)?;
280
281 Ok(())
282 }
283}