1use crate::layout::{self, geometry::Rect, SizedLayout};
2
3#[derive(Debug)]
4pub enum DrawCommand {
5 Text(Rect, String),
6 FillRect(Rect, String),
7 StrokeRect(Rect, usize, String),
8}
9
10impl<Ctx: Clone> SizedLayout<Ctx> {
11 pub fn resolve_draw_commands(&self, bounds: &Rect, context: &mut Ctx) -> Vec<DrawCommand> {
12 use layout::SizedNode::*;
13 let layout = self.clone();
14
15 match *layout.node {
16 Text(content) => {
17 vec![DrawCommand::Text(bounds.clone(), content)]
18 }
19 Width(_, node) | Height(_, node) => {
20 let frame = node.sizing.fit_into(bounds);
21
22 node.resolve_draw_commands(&frame, context)
23 }
24 VCenter(n) => {
25 let mut content_rect = n.sizing.fit_into(bounds);
26 let center_pos = bounds.y as usize + bounds.height / 2;
27 let center_start = center_pos - content_rect.height / 2;
28 content_rect.y = center_start as i64;
29
30 let content_bounds = n.sizing.fit_into(&content_rect);
31
32 n.resolve_draw_commands(&content_bounds, context)
33 }
34 HCenter(n) => {
35 let mut content_rect = n.sizing.fit_into(bounds);
36 let center_pos = bounds.x as usize + bounds.width / 2;
37 let center_start = center_pos - content_rect.width / 2;
38 content_rect.x = center_start as i64;
39
40 let content_bounds = n.sizing.fit_into(&content_rect);
41
42 n.resolve_draw_commands(&content_bounds, context)
43 }
44 VBottomAlign(n) => {
45 let mut content_rect = n.sizing.fit_into(bounds);
46 let bottom_most = bounds.y as usize + bounds.height;
47 let top_start = bottom_most - content_rect.height;
48 content_rect.y = top_start as i64;
49
50 n.resolve_draw_commands(&content_rect, context)
51 }
52 HRightAlign(n) => {
53 let mut content_rect = n.sizing.fit_into(bounds);
54 let right_most = bounds.x as usize + bounds.width;
55 let left_start = right_most - content_rect.width;
56 content_rect.x = left_start as i64;
57
58 let content_bounds = n.sizing.fit_into(&content_rect);
59
60 n.resolve_draw_commands(&content_bounds, context)
61 }
62 VTopAlign(n) | HLeftAlign(n) => {
63 let content_rect = n.sizing.fit_into(bounds);
64
65 n.resolve_draw_commands(&content_rect, context)
66 }
67 TopPadding(n, node) => {
68 let mut bounds = bounds.clone();
69 bounds.height = bounds.height.saturating_sub(n);
70 let mut frame = node.sizing.fit_into(&bounds);
71 frame.x = bounds.x;
72 frame.y = bounds.y + n as i64;
73
74 node.resolve_draw_commands(&frame, context)
75 }
76 BottomPadding(n, node) => {
77 let mut bounds = bounds.clone();
78 bounds.height = bounds.height.saturating_sub(n);
79
80 let mut frame = node.sizing.fit_into(&bounds);
81 frame.x = bounds.x;
82 frame.y = bounds.y;
83
84 node.resolve_draw_commands(&frame, context)
85 }
86 RightPadding(n, node) => {
87 let mut frame = node.sizing.fit_into(bounds);
88 frame.x = bounds.x;
89 frame.y = bounds.y;
90
91 let free_width = bounds.width.saturating_sub(n);
92 let adjustment = frame.width.saturating_sub(free_width);
93
94 frame.width = frame.width.saturating_sub(adjustment);
95
96 node.resolve_draw_commands(&frame, context)
97 }
98 LeftPadding(n, node) => {
99 let mut bounds = bounds.clone();
100 bounds.width = bounds.width.saturating_sub(n);
101 let mut frame = node.sizing.fit_into(&bounds);
102 frame.x = bounds.x + n as i64;
103 frame.y = bounds.y;
104
105 node.resolve_draw_commands(&frame, context)
106 }
107 Background(c, node) => {
108 let mut frame = node.sizing.fit_into(bounds);
109 frame.x = bounds.x;
110 frame.y = bounds.y;
111
112 let mut commands = vec![DrawCommand::FillRect(bounds.clone(), c.to_string())];
114
115 let content_commands = node.resolve_draw_commands(&frame, context);
116
117 commands.extend(content_commands);
118
119 commands
120 }
121 Border(n, c, edges, node) => {
122 let outer_bounds = bounds;
123 let mut inner_bounds = bounds.clone();
124 for edge in &edges {
125 match edge {
126 layout::alignment::Edge::Top => {
127 inner_bounds.height = inner_bounds.height.saturating_sub(n);
128 inner_bounds.y = inner_bounds.y.checked_add(n as i64).unwrap_or(0);
129 }
130 layout::alignment::Edge::Right => {
131 inner_bounds.width = inner_bounds.width.saturating_sub(n);
132 }
133 layout::alignment::Edge::Bottom => {
134 inner_bounds.height = inner_bounds.height.saturating_sub(n);
135 }
136 layout::alignment::Edge::Left => {
137 inner_bounds.width = inner_bounds.width.saturating_sub(n);
138 inner_bounds.x = inner_bounds.x.checked_add(n as i64).unwrap_or(0);
139 }
140 }
141 }
142
143 let mut frame = node.sizing.fit_into(&inner_bounds);
144 frame.x = inner_bounds.x;
145 frame.y = inner_bounds.y;
146
147 let mut commands = node.resolve_draw_commands(&frame, context);
148
149 if edges == layout::alignment::Edge::all() {
150 commands.push(DrawCommand::StrokeRect(outer_bounds.clone(), n, c.to_string()));
151 } else {
152 for edge in &edges {
153 let command = match edge {
154 layout::alignment::Edge::Top => {
155 let line_bounds = Rect::new(outer_bounds.x, outer_bounds.y, outer_bounds.width, n);
156
157 DrawCommand::FillRect(line_bounds, c.to_string())
158 }
159 layout::alignment::Edge::Right => {
160 let line_bounds = Rect::new(outer_bounds.max_x() - n as i64, outer_bounds.y, n, outer_bounds.height);
161
162 DrawCommand::FillRect(line_bounds, c.to_string())
163 }
164 layout::alignment::Edge::Bottom => {
165 let line_bounds = Rect::new(outer_bounds.x, outer_bounds.max_y() - n as i64, outer_bounds.width, n);
166
167 DrawCommand::FillRect(line_bounds, c.to_string())
168 }
169 layout::alignment::Edge::Left => {
170 let line_bounds = Rect::new(outer_bounds.x, outer_bounds.y, n, outer_bounds.height);
171
172 DrawCommand::FillRect(line_bounds, c.to_string())
173 }
174 };
175
176 commands.push(command);
177 }
178 }
179
180 commands
181 }
182 VerticalStack(alignment, spacing, nodes) => {
183 let mut max_width = 0usize;
184
185 let spacing_sizing = spacing * (nodes.len().saturating_sub(1));
186
187 let mut last_bounds = Rect::zero();
188
189 let mut greedy_count = 0;
190 let mut static_height = spacing_sizing;
191
192 for node in &nodes {
193 if let layout::sizing::Sizing::Static(n) = node.sizing.vertical {
194 static_height += n;
195 } else {
196 greedy_count += 1;
197 }
198 }
199
200 let mut greedy_space = bounds.height - static_height;
201 let greedy_size = if greedy_count != 0 { greedy_space / greedy_count } else { 0 };
202
203 let mut new_nodes = vec![];
204
205 for node in &nodes {
206 let mut n = (*node).clone();
207 n.sizing.vertical = match n.sizing.vertical {
208 layout::sizing::Sizing::Static(sz) => layout::sizing::Sizing::Static(sz),
209 layout::sizing::Sizing::Greedy(tight) => {
210 greedy_space -= greedy_size;
211 let mut node_height = greedy_size;
212 if greedy_space < greedy_size {
213 node_height += greedy_space;
214 greedy_space = 0;
215 }
216
217 layout::sizing::Sizing::Static(node_height.max(tight))
218 }
219 };
220
221 new_nodes.push(n);
222 }
223
224 let nodes = new_nodes;
225
226 let mut raw_bounds = vec![];
227 for node in &nodes {
228 let size = node.sizing.fit_into(bounds);
229
230 let spacing_offset = if raw_bounds.is_empty() {
231 0
232 } else {
233 spacing as i64
234 };
235
236 let node_bounds = Rect::new(0, last_bounds.max_y() + spacing_offset, size.width, size.height);
237 last_bounds = node_bounds.clone();
238
239 if node_bounds.width > max_width {
240 max_width = node_bounds.width;
241 }
242
243 raw_bounds.push(node_bounds);
244 }
245
246 let final_bounds: Vec<_> = raw_bounds.into_iter().map(|mut bound| {
247 match &alignment {
248 layout::alignment::HorizontalAlignment::Left => { }
249 layout::alignment::HorizontalAlignment::Center => {
250 let center = max_width / 2;
251 let start = center - bound.width/2;
252 bound.x = start as i64;
253 }
254 layout::alignment::HorizontalAlignment::Right => {
255 let right = max_width;
256 let start = right - bound.width;
257 bound.x = start as i64;
258 }
259 }
260
261 bound.x += bounds.x;
263 bound.y += bounds.y;
264
265 bound
266 }).collect();
267
268
269
270 nodes.into_iter().enumerate().flat_map(|(i, node)| {
271 let size = &final_bounds[i];
272
273 node.resolve_draw_commands(size, context)
274 }).collect::<Vec<_>>()
275 }
276 HorizontalStack(alignment, spacing, nodes) => {
277 let mut max_height = 0usize;
278
279 let spacing_sizing = spacing * (nodes.len().saturating_sub(1));
280
281 let mut last_bounds = Rect::zero();
282
283 let mut greedy_count = 0;
284 let mut static_width = spacing_sizing;
285
286 for node in &nodes {
287 if let layout::sizing::Sizing::Static(n) = node.sizing.horizontal {
288 static_width += n;
289 } else {
290 greedy_count += 1;
291 }
292 }
293
294 let mut greedy_space = bounds.width.saturating_sub(static_width);
295 let greedy_size = if greedy_count != 0 { greedy_space / greedy_count } else { 0 };
296
297 let mut new_nodes = vec![];
298
299 for node in &nodes {
300 let mut n = node.clone();
301 n.sizing.horizontal = match n.sizing.horizontal {
302 layout::sizing::Sizing::Static(sz) => layout::sizing::Sizing::Static(sz),
303 layout::sizing::Sizing::Greedy(tight) => {
304 greedy_space -= greedy_size;
305 let mut node_width = greedy_size;
306 if greedy_space < greedy_size {
307 node_width += greedy_space;
308 greedy_space = 0;
309 }
310
311 layout::sizing::Sizing::Static(node_width.max(tight))
312 }
313 };
314
315 new_nodes.push(n);
316 }
317
318 let nodes = new_nodes;
319
320 let mut raw_bounds = vec![];
321 for node in &nodes {
322 let size = node.sizing.fit_into(bounds);
323
324 let spacing_offset = if raw_bounds.is_empty() {
325 0
326 } else {
327 spacing as i64
328 };
329
330 let node_bounds = Rect::new(last_bounds.max_x() + spacing_offset, 0, size.width, size.height);
331 last_bounds = node_bounds.clone();
332
333 if node_bounds.height > max_height {
334 max_height = node_bounds.height;
335 }
336
337 raw_bounds.push(node_bounds);
338 }
339
340 let final_bounds: Vec<_> = raw_bounds.into_iter().map(|mut bound| {
341 match &alignment {
342 layout::alignment::VerticalAlignment::Top => { }
343 layout::alignment::VerticalAlignment::Center => {
344 let center = max_height / 2;
345 let start = center - bound.height/2;
346 bound.y = start as i64;
347 }
348 layout::alignment::VerticalAlignment::Bottom => {
349 let bottom = max_height;
350 let start = bottom - bound.height;
351 bound.y = start as i64;
352 }
353 }
354
355 bound.x += bounds.x;
357 bound.y += bounds.y;
358
359 bound
360 }).collect();
361
362
363
364 nodes.into_iter().enumerate().flat_map(|(i, node)| {
365 let size = &final_bounds[i];
366
367 node.resolve_draw_commands(size, context)
368 }).collect::<Vec<_>>()
369 }
370 DrawCanvas(action) => {
371 let result = action(context, bounds);
372
373 vec![DrawCommand::Text(bounds.clone(), result.to_string())]
374 }
375 }
376 }
377}