Skip to main content

textyle/rendering/
mod.rs

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                // self.draw_rect(bounds, &c.to_string());
113                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 => { /* Already aligned to the 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                    // move from 0 based bounds to the actual frame of the container
262                    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 => { /* Already aligned to the 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                    // move from 0 based bounds to the actual frame of the container
356                    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}