1use super::{Widget, element::Element};
18use alloc::vec::Vec;
19use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
20use zest_core::{
21 Constraints, Horizontal, Length, RenderError, Renderer, TouchPhase, UiAction, Vertical,
22 WidgetId,
23};
24use zest_theme::Theme;
25
26struct Layer<'a, C: PixelColor, M: Clone> {
29 child: Element<'a, C, M>,
30 align_x: Horizontal,
31 align_y: Vertical,
32}
33
34pub struct Stack<'a, C: PixelColor, M: Clone> {
37 rect: Rectangle,
38 layers: Vec<Layer<'a, C, M>>,
39 width: Length,
40 height: Length,
41}
42
43impl<'a, C: PixelColor + 'a, M: Clone + 'a> Stack<'a, C, M> {
44 pub fn new() -> Self {
48 Self {
49 rect: Rectangle::zero(),
50 layers: Vec::new(),
51 width: Length::Fill,
52 height: Length::Fill,
53 }
54 }
55
56 #[must_use]
58 pub fn width(mut self, width: impl Into<Length>) -> Self {
59 self.width = width.into();
60 self
61 }
62
63 #[must_use]
65 pub fn height(mut self, height: impl Into<Length>) -> Self {
66 self.height = height.into();
67 self
68 }
69
70 #[must_use]
73 pub fn push<W>(self, child: W) -> Self
74 where
75 W: Widget<C, M> + 'a,
76 {
77 self.push_aligned(child, Horizontal::Center, Vertical::Center)
78 }
79
80 #[must_use]
83 pub fn push_aligned<W>(mut self, child: W, align_x: Horizontal, align_y: Vertical) -> Self
84 where
85 W: Widget<C, M> + 'a,
86 {
87 self.layers.push(Layer {
88 child: Element::new(child),
89 align_x,
90 align_y,
91 });
92 self
93 }
94
95 fn relayout(&mut self) {
96 let outer = Constraints::loose(self.rect.size);
97 let origin = self.rect.top_left;
98 let avail = self.rect.size;
99 for layer in &mut self.layers {
100 let desired = layer.child.measure(outer);
101 let cell = align_rect(origin, avail, desired, layer.align_x, layer.align_y);
102 layer.child.arrange(cell);
103 }
104 }
105}
106
107fn align_rect(
110 origin: Point,
111 avail: Size,
112 desired: Size,
113 align_x: Horizontal,
114 align_y: Vertical,
115) -> Rectangle {
116 let w = desired.width.min(avail.width);
117 let h = desired.height.min(avail.height);
118 let free_x = avail.width.saturating_sub(w) as i32;
119 let free_y = avail.height.saturating_sub(h) as i32;
120 let dx = match align_x {
121 Horizontal::Left => 0,
122 Horizontal::Center => free_x / 2,
123 Horizontal::Right => free_x,
124 };
125 let dy = match align_y {
126 Vertical::Top => 0,
127 Vertical::Center => free_y / 2,
128 Vertical::Bottom => free_y,
129 };
130 Rectangle::new(origin + Point::new(dx, dy), Size::new(w, h))
131}
132
133impl<'a, C: PixelColor + 'a, M: Clone + 'a> Default for Stack<'a, C, M> {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl<'a, C: PixelColor + 'a, M: Clone + 'a> Widget<C, M> for Stack<'a, C, M> {
140 fn measure(&mut self, constraints: Constraints) -> Size {
141 let mut intrinsic = Size::zero();
143 let loose = Constraints::loose(constraints.max);
144 for layer in &mut self.layers {
145 let s = layer.child.measure(loose);
146 intrinsic = Size::new(intrinsic.width.max(s.width), intrinsic.height.max(s.height));
147 }
148 let w = self.width.resolve(intrinsic.width, constraints.max.width);
149 let h = self
150 .height
151 .resolve(intrinsic.height, constraints.max.height);
152 constraints.clamp(Size::new(w, h))
153 }
154
155 fn preferred_size(&self) -> (Length, Length) {
156 (self.width, self.height)
157 }
158
159 fn arrange(&mut self, rect: Rectangle) {
160 self.rect = rect;
161 self.relayout();
162 }
163
164 fn rect(&self) -> Rectangle {
165 self.rect
166 }
167
168 fn handle_touch(&mut self, point: Point, phase: TouchPhase) -> Option<M> {
169 for layer in self.layers.iter_mut().rev() {
171 if let Some(msg) = layer.child.handle_touch(point, phase) {
172 return Some(msg);
173 }
174 }
175 None
176 }
177
178 fn mark_pressed(&mut self, point: Point) {
179 for layer in &mut self.layers {
180 layer.child.mark_pressed(point);
181 }
182 }
183
184 fn collect_focusable(&self, out: &mut Vec<WidgetId>) {
185 for layer in &self.layers {
186 layer.child.collect_focusable(out);
187 }
188 }
189
190 fn sync_focus(&mut self, focused: Option<WidgetId>) {
191 for layer in &mut self.layers {
192 layer.child.sync_focus(focused);
193 }
194 }
195
196 fn route_action(&mut self, target: WidgetId, action: UiAction) -> Option<M> {
197 for layer in self.layers.iter_mut().rev() {
198 if let Some(msg) = layer.child.route_action(target, action) {
199 return Some(msg);
200 }
201 }
202 None
203 }
204
205 fn navigate_focus(&self, target: WidgetId, action: UiAction) -> Option<WidgetId> {
206 for layer in self.layers.iter().rev() {
207 if let Some(next) = layer.child.navigate_focus(target, action) {
208 return Some(next);
209 }
210 }
211 None
212 }
213
214 fn focus_rect(&self, target: WidgetId) -> Option<Rectangle> {
215 for layer in self.layers.iter().rev() {
216 if let Some(rect) = layer.child.focus_rect(target) {
217 return Some(rect);
218 }
219 }
220 None
221 }
222
223 fn focus_at(&self, point: Point) -> Option<WidgetId> {
224 for layer in self.layers.iter().rev() {
225 if let Some(id) = layer.child.focus_at(point) {
226 return Some(id);
227 }
228 }
229 None
230 }
231
232 fn draw<'t>(
233 &self,
234 renderer: &mut dyn Renderer<C>,
235 theme: &Theme<'t, C>,
236 ) -> Result<(), RenderError> {
237 for layer in &self.layers {
239 layer.child.draw(renderer, theme)?;
240 }
241 Ok(())
242 }
243}