1use super::{
16 Widget,
17 button::Button,
18 column::Column,
19 container::Container,
20 element::{Element, IntoElement},
21 row::Row,
22 text::Text,
23};
24use alloc::string::String;
25use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
26use zest_core::{Constraints, Horizontal, Length, RenderError, Renderer, TouchPhase, Vertical};
27use zest_theme::Theme;
28
29const TITLE_BAR_H: u32 = 28;
31
32pub struct Window<'a, C: PixelColor, M: Clone> {
34 rect: Rectangle,
35 title: String,
36 on_close: Option<M>,
37 child: Option<Element<'a, C, M>>,
38 padding: u32,
39 width: Length,
40 height: Length,
41 tree: Option<Element<'a, C, M>>,
43}
44
45impl<'a, C: PixelColor + 'a, M: Clone + 'a> Window<'a, C, M> {
46 pub fn new() -> Self {
50 Self {
51 rect: Rectangle::zero(),
52 title: String::new(),
53 on_close: None,
54 child: None,
55 padding: 6,
56 width: Length::Fill,
57 height: Length::Fill,
58 tree: None,
59 }
60 }
61
62 #[must_use]
64 pub fn title(mut self, title: impl Into<String>) -> Self {
65 self.title = title.into();
66 self
67 }
68
69 #[must_use]
72 pub fn on_close(mut self, msg: M) -> Self {
73 self.on_close = Some(msg);
74 self
75 }
76
77 #[must_use]
79 pub fn child<W>(mut self, child: W) -> Self
80 where
81 W: Widget<C, M> + 'a,
82 {
83 self.child = Some(Element::new(child));
84 self
85 }
86
87 #[must_use]
89 pub fn padding(mut self, padding: u32) -> Self {
90 self.padding = padding;
91 self
92 }
93
94 #[must_use]
96 pub fn width(mut self, width: impl Into<Length>) -> Self {
97 self.width = width.into();
98 self
99 }
100
101 #[must_use]
103 pub fn height(mut self, height: impl Into<Length>) -> Self {
104 self.height = height.into();
105 self
106 }
107
108 fn build_tree(&mut self) -> Element<'a, C, M> {
111 let title = Text::new(self.title.clone())
114 .align_x(Horizontal::Left)
115 .align_y(Vertical::Center)
116 .width(Length::Fill)
117 .height(Length::Fill);
118
119 let mut bar: Row<'a, C, M> = Row::new()
120 .spacing(4)
121 .height(Length::Fixed(TITLE_BAR_H))
122 .push(title);
123
124 if let Some(msg) = self.on_close.clone() {
125 bar = bar.push(
126 Button::new("x")
127 .on_press(msg)
128 .width(Length::Fixed(TITLE_BAR_H.saturating_sub(4)))
129 .height(Length::Fill),
130 );
131 }
132
133 let mut content: Container<'a, C, M> =
135 Container::new().padding(self.padding).height(Length::Fill);
136 if let Some(child) = self.child.take() {
137 content = content.child(child);
138 }
139
140 Column::new()
141 .spacing(0)
142 .width(self.width)
143 .height(self.height)
144 .push(bar)
145 .push(content)
146 .into_element()
147 }
148}
149
150impl<'a, C: PixelColor + 'a, M: Clone + 'a> Default for Window<'a, C, M> {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156impl<'a, C: PixelColor + 'a, M: Clone + 'a> Widget<C, M> for Window<'a, C, M> {
157 fn measure(&mut self, constraints: Constraints) -> Size {
158 if self.tree.is_none() {
159 self.tree = Some(self.build_tree());
160 }
161 let w = self
162 .width
163 .resolve(constraints.max.width, constraints.max.width);
164 let h = self
165 .height
166 .resolve(constraints.max.height, constraints.max.height);
167 let size = constraints.clamp(Size::new(w, h));
168 if let Some(tree) = self.tree.as_mut() {
169 tree.measure(Constraints::loose(size));
170 }
171 size
172 }
173
174 fn preferred_size(&self) -> (Length, Length) {
175 (self.width, self.height)
176 }
177
178 fn arrange(&mut self, rect: Rectangle) {
179 self.rect = rect;
180 if self.tree.is_none() {
181 self.tree = Some(self.build_tree());
182 }
183 if let Some(tree) = self.tree.as_mut() {
184 tree.arrange(rect);
185 }
186 }
187
188 fn rect(&self) -> Rectangle {
189 self.rect
190 }
191
192 fn handle_touch(&mut self, point: Point, phase: TouchPhase) -> Option<M> {
193 self.tree
194 .as_mut()
195 .and_then(|tree| tree.handle_touch(point, phase))
196 }
197
198 fn mark_pressed(&mut self, point: Point) {
199 if let Some(tree) = self.tree.as_mut() {
200 tree.mark_pressed(point);
201 }
202 }
203
204 fn draw<'t>(
205 &self,
206 renderer: &mut dyn Renderer<C>,
207 theme: &Theme<'t, C>,
208 ) -> Result<(), RenderError> {
209 renderer.fill_rect(self.rect, theme.primary.base)?;
212 let bar = Rectangle::new(
213 self.rect.top_left,
214 Size::new(self.rect.size.width, TITLE_BAR_H.min(self.rect.size.height)),
215 );
216 renderer.fill_rect(bar, theme.secondary.base)?;
217 renderer.stroke_rect(self.rect, theme.background.divider)?;
218
219 if let Some(tree) = &self.tree {
220 tree.draw(renderer, theme)?;
221 }
222 Ok(())
223 }
224}