Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-widgets/src/layout.rs
1//! Layout widgets for arranging child widgets
2 
3use crate::widget::{generate_id, Widget, WidgetId};
4use std::any::Any;
5use strato_core::taffy::{
6 prelude::*,
7 style::{AlignItems, Dimension, FlexDirection, JustifyContent},
8};
9use strato_core::{
10 event::{Event, EventResult},
11 layout::{
12 AlignItems as CoreAlignItems, Constraints, FlexContainer,
13 FlexDirection as CoreFlexDirection, FlexItem, JustifyContent as CoreJustifyContent, Layout,
14 Size,
15 },
16 taffy_layout::{TaffyLayoutError, TaffyLayoutResult, TaffyWidget},
17};
18use strato_renderer::batch::RenderBatch;
19 
20/// Main axis alignment for flex layouts
21#[derive(Debug, Clone, Copy, PartialEq)]
22pub enum MainAxisAlignment {
23 Start,
24 Center,
25 End,
26 SpaceBetween,
27 SpaceAround,
28 SpaceEvenly,
29}
30 
31impl MainAxisAlignment {
32 fn to_taffy(&self) -> JustifyContent {
33 match self {
34 MainAxisAlignment::Start => JustifyContent::FlexStart,
35 MainAxisAlignment::Center => JustifyContent::Center,
36 MainAxisAlignment::End => JustifyContent::FlexEnd,
37 MainAxisAlignment::SpaceBetween => JustifyContent::SpaceBetween,
38 MainAxisAlignment::SpaceAround => JustifyContent::SpaceAround,
39 MainAxisAlignment::SpaceEvenly => JustifyContent::SpaceEvenly,
40 }
41 }
42 
43 fn to_core(&self) -> CoreJustifyContent {
44 match self {
45 MainAxisAlignment::Start => CoreJustifyContent::FlexStart,
46 MainAxisAlignment::Center => CoreJustifyContent::Center,
47 MainAxisAlignment::End => CoreJustifyContent::FlexEnd,
48 MainAxisAlignment::SpaceBetween => CoreJustifyContent::SpaceBetween,
49 MainAxisAlignment::SpaceAround => CoreJustifyContent::SpaceAround,
50 MainAxisAlignment::SpaceEvenly => CoreJustifyContent::SpaceEvenly,
51 }
52 }
53}
54 
55/// Cross axis alignment for flex layouts
56#[derive(Debug, Clone, Copy, PartialEq)]
57pub enum CrossAxisAlignment {
58 Start,
59 Center,
60 End,
61 Stretch,
62 Baseline,
63}
64 
65impl CrossAxisAlignment {
66 fn to_taffy(&self) -> AlignItems {
67 match self {
68 CrossAxisAlignment::Start => AlignItems::FlexStart,
69 CrossAxisAlignment::Center => AlignItems::Center,
70 CrossAxisAlignment::End => AlignItems::FlexEnd,
71 CrossAxisAlignment::Stretch => AlignItems::Stretch,
72 CrossAxisAlignment::Baseline => AlignItems::Baseline,
73 }
74 }
75 
76 fn to_core(&self) -> CoreAlignItems {
77 match self {
78 CrossAxisAlignment::Start => CoreAlignItems::FlexStart,
79 CrossAxisAlignment::Center => CoreAlignItems::Center,
80 CrossAxisAlignment::End => CoreAlignItems::FlexEnd,
81 CrossAxisAlignment::Stretch => CoreAlignItems::Stretch,
82 CrossAxisAlignment::Baseline => CoreAlignItems::Baseline,
83 }
84 }
85}
86 
87/// Row widget for horizontal layout
88#[derive(Debug)]
89pub struct Row {
90 id: WidgetId,
91 children: Vec<Box<dyn Widget>>,
92 main_axis_alignment: MainAxisAlignment,
93 cross_axis_alignment: CrossAxisAlignment,
94 spacing: f32,
95 // Layout cache computed during layout()
96 cached_child_sizes: Vec<Size>,
97}
98 
99impl Row {
100 /// Create a new row
101 pub fn new() -> Self {
102 Self {
103 id: generate_id(),
104 children: Vec::new(),
105 main_axis_alignment: MainAxisAlignment::Start,
106 cross_axis_alignment: CrossAxisAlignment::Center,
107 spacing: 0.0,
108 cached_child_sizes: Vec::new(),
109 }
110 }
111 
112 /// Add children widgets
113 pub fn children(mut self, children: Vec<Box<dyn Widget>>) -> Self {
114 self.children = children;
115 self
116 }
117 
118 /// Add a single child
119 pub fn child(mut self, child: Box<dyn Widget>) -> Self {
120 self.children.push(child);
121 self
122 }
123 
124 /// Set main axis alignment
125 pub fn main_axis_alignment(mut self, alignment: MainAxisAlignment) -> Self {
126 self.main_axis_alignment = alignment;
127 self
128 }
129 
130 /// Set cross axis alignment
131 pub fn cross_axis_alignment(mut self, alignment: CrossAxisAlignment) -> Self {
132 self.cross_axis_alignment = alignment;
133 self
134 }
135 
136 /// Set spacing between children
137 pub fn spacing(mut self, spacing: f32) -> Self {
138 self.spacing = spacing;
139 self
140 }
141}
142 
143impl Widget for Row {
144 fn id(&self) -> WidgetId {
145 self.id
146 }
147 
148 fn layout(&mut self, constraints: Constraints) -> Size {
149 let engine = strato_core::layout::LayoutEngine::new();
150 
151 // Relax constraints for children measurement
152 let child_constraints = Constraints {
153 min_width: 0.0,
154 max_width: constraints.max_width,
155 min_height: 0.0,
156 max_height: constraints.max_height,
157 };
158 
159 // Calculate child sizes
160 let mut child_data = Vec::new();
161 let mut sizes = Vec::with_capacity(self.children.len());
162 for child in &mut self.children {
163 let child_size = child.layout(child_constraints);
164 sizes.push(child_size);
165 
166 let mut flex_item = FlexItem::default();
167 if let Some(flex) = child.as_any().downcast_ref::<Flex>() {
168 flex_item = FlexItem::grow(flex.flex);
169 }
170 child_data.push((flex_item, child_size));
171 }
172 // Cache sizes for use during render()
173 self.cached_child_sizes = sizes;
174 
175 // Calculate layout
176 let container = FlexContainer {
177 direction: CoreFlexDirection::Row,
178 justify_content: self.main_axis_alignment.to_core(),
179 align_items: self.cross_axis_alignment.to_core(),
180 ..Default::default()
181 };
182 let layouts = engine.calculate_flex_layout(&container, &child_data, constraints);
183 
184 // Calculate total size
185 let width = layouts
186 .iter()
187 .map(|l| l.position.x + l.size.width)
188 .max_by(|a, b| a.partial_cmp(b).unwrap())
189 .unwrap_or(0.0);
190 let height = layouts
191 .iter()
192 .map(|l| l.size.height)
193 .max_by(|a, b| a.partial_cmp(b).unwrap())
194 .unwrap_or(0.0);
195 
196 Size::new(width, height)
197 }
198 
199 fn render(&self, batch: &mut RenderBatch, layout: Layout) {
200 let engine = strato_core::layout::LayoutEngine::new();
201 
202 // Calculate child layouts using cached sizes measured in layout()
203 let mut child_data = Vec::new();
204 for (i, child) in self.children.iter().enumerate() {
205 let child_size = self
206 .cached_child_sizes
207 .get(i)
208 .copied()
209 .unwrap_or_else(|| Size::new(100.0, 50.0));
210 
211 let mut flex_item = FlexItem::default();
212 if let Some(flex) = child.as_any().downcast_ref::<Flex>() {
213 flex_item = FlexItem::grow(flex.flex);
214 }
215 child_data.push((flex_item, child_size));
216 }
217 
218 let container = FlexContainer {
219 direction: CoreFlexDirection::Row,
220 justify_content: self.main_axis_alignment.to_core(),
221 align_items: self.cross_axis_alignment.to_core(),
222 ..Default::default()
223 };
224 let layouts = engine.calculate_flex_layout(
225 &container,
226 &child_data,
227 Constraints::loose(layout.size.width, layout.size.height),
228 );
229 
230 // Render children
231 for (child, child_layout) in self.children.iter().zip(layouts.iter()) {
232 let absolute_layout =
233 Layout::new(layout.position + child_layout.position, child_layout.size);
234 child.render(batch, absolute_layout);
235 }
236 }
237 
238 fn handle_event(&mut self, event: &Event) -> EventResult {
239 for child in &mut self.children {
240 if child.handle_event(event) == EventResult::Handled {
241 return EventResult::Handled;
242 }
243 }
244 EventResult::Ignored
245 }
246 
247 fn children(&self) -> Vec<&(dyn Widget + '_)> {
248 self.children.iter().map(|c| c.as_ref()).collect()
249 }
250 
251 fn children_mut<'a>(&'a mut self) -> Vec<&'a mut (dyn Widget + 'a)> {
252 self.children
253 .iter_mut()
254 .map(|c| c.as_mut() as &'a mut (dyn Widget + 'a))
255 .collect()
256 }
257 
258 fn as_any(&self) -> &dyn Any {
259 self
260 }
261 
262 fn as_any_mut(&mut self) -> &mut dyn Any {
263 self
264 }
265 
266 fn clone_widget(&self) -> Box<dyn Widget> {
267 Box::new(Row {
268 id: generate_id(),
269 children: self.children.iter().map(|c| c.clone_widget()).collect(),
270 main_axis_alignment: self.main_axis_alignment,
271 cross_axis_alignment: self.cross_axis_alignment,
272 spacing: self.spacing,
273 cached_child_sizes: Vec::new(),
274 })
275 }
276 
277 fn as_taffy(&self) -> Option<&dyn TaffyWidget> {
278 Some(self)
279 }
280 
281 fn render_taffy(
282 &self,
283 batch: &mut RenderBatch,
284 tree: &TaffyTree<()>,
285 node: NodeId,
286 parent_offset: strato_core::types::Point,
287 ) {
288 if let Ok(layout) = tree.layout(node) {
289 let my_position = parent_offset
290 + strato_core::types::Point::new(layout.location.x, layout.location.y);
291 
292 // Render children
293 if let Ok(children_nodes) = tree.children(node) {
294 let mut child_node_idx = 0;
295 for child in &self.children {
296 if child.as_taffy().is_some() {
297 if child_node_idx < children_nodes.len() {
298 let child_node = children_nodes[child_node_idx];
299 child.render_taffy(batch, tree, child_node, my_position);
300 child_node_idx += 1;
301 }
302 }
303 }
304 }
305 }
306 }
307}
308 
309impl TaffyWidget for Row {
310 fn build_layout(&self, tree: &mut TaffyTree<()>) -> TaffyLayoutResult<NodeId> {
311 let mut children_nodes = Vec::with_capacity(self.children.len());
312 for child in &self.children {
313 if let Some(taffy_child) = child.as_taffy() {
314 let node = taffy_child.build_layout(tree)?;
315 children_nodes.push(node);
316 }
317 }
318 
319 let style = Style {
320 display: Display::Flex,
321 flex_direction: FlexDirection::Row,
322 justify_content: Some(self.main_axis_alignment.to_taffy()),
323 align_items: Some(self.cross_axis_alignment.to_taffy()),
324 gap: strato_core::taffy::prelude::Size {
325 width: length(self.spacing),
326 height: length(0.0),
327 },
328 ..Default::default()
329 };
330 
331 tree.new_with_children(style, &children_nodes)
332 .map_err(|e| TaffyLayoutError::from(e))
333 }
334}
335 
336/// Column widget for vertical layout
337#[derive(Debug)]
338pub struct Column {
339 id: WidgetId,
340 children: Vec<Box<dyn Widget>>,
341 main_axis_alignment: MainAxisAlignment,
342 cross_axis_alignment: CrossAxisAlignment,
343 spacing: f32,
344 // Layout cache computed during layout()
345 cached_child_sizes: Vec<Size>,
346}
347 
348impl Column {
349 /// Create a new column
350 pub fn new() -> Self {
351 Self {
352 id: generate_id(),
353 children: Vec::new(),
354 main_axis_alignment: MainAxisAlignment::Start,
355 cross_axis_alignment: CrossAxisAlignment::Center,
356 spacing: 0.0,
357 cached_child_sizes: Vec::new(),
358 }
359 }
360 
361 /// Add children widgets
362 pub fn children(mut self, children: Vec<Box<dyn Widget>>) -> Self {
363 self.children = children;
364 self
365 }
366 
367 /// Add a single child
368 pub fn child(mut self, child: Box<dyn Widget>) -> Self {
369 self.children.push(child);
370 self
371 }
372 
373 /// Set main axis alignment
374 pub fn main_axis_alignment(mut self, alignment: MainAxisAlignment) -> Self {
375 self.main_axis_alignment = alignment;
376 self
377 }
378 
379 /// Set cross axis alignment
380 pub fn cross_axis_alignment(mut self, alignment: CrossAxisAlignment) -> Self {
381 self.cross_axis_alignment = alignment;
382 self
383 }
384 
385 /// Set spacing between children
386 pub fn spacing(mut self, spacing: f32) -> Self {
387 self.spacing = spacing;
388 self
389 }
390}
391 
392impl Widget for Column {
393 fn id(&self) -> WidgetId {
394 self.id
395 }
396 
397 fn layout(&mut self, constraints: Constraints) -> Size {
398 let engine = strato_core::layout::LayoutEngine::new();
399 
400 // Relax constraints for children measurement
401 let child_constraints = Constraints {
402 min_width: 0.0,
403 max_width: constraints.max_width,
404 min_height: 0.0,
405 max_height: constraints.max_height,
406 };
407 
408 // Calculate child sizes
409 let mut child_data = Vec::new();
410 let mut sizes = Vec::with_capacity(self.children.len());
411 for child in &mut self.children {
412 let child_size = child.layout(child_constraints);
413 sizes.push(child_size);
414 
415 let mut flex_item = FlexItem::default();
416 if let Some(flex) = child.as_any().downcast_ref::<Flex>() {
417 flex_item = FlexItem::grow(flex.flex);
418 }
419 child_data.push((flex_item, child_size));
420 }
421 // Cache sizes for render()
422 self.cached_child_sizes = sizes;
423 
424 // Calculate layout
425 let container = FlexContainer {
426 direction: CoreFlexDirection::Column,
427 justify_content: self.main_axis_alignment.to_core(),
428 align_items: self.cross_axis_alignment.to_core(),
429 ..Default::default()
430 };
431 let layouts = engine.calculate_flex_layout(&container, &child_data, constraints);
432 
433 // Calculate total size
434 let width = layouts
435 .iter()
436 .map(|l| l.size.width)
437 .max_by(|a, b| a.partial_cmp(b).unwrap())
438 .unwrap_or(0.0);
439 let height = layouts
440 .iter()
441 .map(|l| l.position.y + l.size.height)
442 .max_by(|a, b| a.partial_cmp(b).unwrap())
443 .unwrap_or(0.0);
444 
445 Size::new(width, height)
446 }
447 
448 fn render(&self, batch: &mut RenderBatch, layout: Layout) {
449 let engine = strato_core::layout::LayoutEngine::new();
450 
451 // Calculate child layouts using cached sizes computed during layout()
452 let mut child_data = Vec::new();
453 for (i, child) in self.children.iter().enumerate() {
454 let child_size = self
455 .cached_child_sizes
456 .get(i)
457 .copied()
458 .unwrap_or_else(|| Size::new(100.0, 50.0));
459 
460 let mut flex_item = FlexItem::default();
461 if let Some(flex) = child.as_any().downcast_ref::<Flex>() {
462 flex_item = FlexItem::grow(flex.flex);
463 }
464 child_data.push((flex_item, child_size));
465 }
466 
467 let container = FlexContainer {
468 direction: CoreFlexDirection::Column,
469 justify_content: self.main_axis_alignment.to_core(),
470 align_items: self.cross_axis_alignment.to_core(),
471 ..Default::default()
472 };
473 let layouts = engine.calculate_flex_layout(
474 &container,
475 &child_data,
476 Constraints::loose(layout.size.width, layout.size.height),
477 );
478 
479 // Render children
480 for (child, child_layout) in self.children.iter().zip(layouts.iter()) {
481 let absolute_layout =
482 Layout::new(layout.position + child_layout.position, child_layout.size);
483 child.render(batch, absolute_layout);
484 }
485 }
486 
487 fn handle_event(&mut self, event: &Event) -> EventResult {
488 for child in &mut self.children {
489 if child.handle_event(event) == EventResult::Handled {
490 return EventResult::Handled;
491 }
492 }
493 EventResult::Ignored
494 }
495 
496 fn children(&self) -> Vec<&(dyn Widget + '_)> {
497 self.children.iter().map(|c| c.as_ref()).collect()
498 }
499 
500 fn children_mut<'a>(&'a mut self) -> Vec<&'a mut (dyn Widget + 'a)> {
501 self.children
502 .iter_mut()
503 .map(|c| c.as_mut() as &'a mut (dyn Widget + 'a))
504 .collect()
505 }
506 
507 fn as_any(&self) -> &dyn Any {
508 self
509 }
510 
511 fn as_any_mut(&mut self) -> &mut dyn Any {
512 self
513 }
514 
515 fn clone_widget(&self) -> Box<dyn Widget> {
516 Box::new(Column {
517 id: generate_id(),
518 children: self.children.iter().map(|c| c.clone_widget()).collect(),
519 main_axis_alignment: self.main_axis_alignment,
520 cross_axis_alignment: self.cross_axis_alignment,
521 spacing: self.spacing,
522 cached_child_sizes: Vec::new(),
523 })
524 }
525 
526 fn as_taffy(&self) -> Option<&dyn TaffyWidget> {
527 Some(self)
528 }
529 
530 fn render_taffy(
531 &self,
532 batch: &mut RenderBatch,
533 tree: &TaffyTree<()>,
534 node: NodeId,
535 parent_offset: strato_core::types::Point,
536 ) {
537 if let Ok(layout) = tree.layout(node) {
538 let my_position = parent_offset
539 + strato_core::types::Point::new(layout.location.x, layout.location.y);
540 
541 // Render children
542 if let Ok(children_nodes) = tree.children(node) {
543 let mut child_node_idx = 0;
544 for child in &self.children {
545 if child.as_taffy().is_some() {
546 if child_node_idx < children_nodes.len() {
547 let child_node = children_nodes[child_node_idx];
548 child.render_taffy(batch, tree, child_node, my_position);
549 child_node_idx += 1;
550 }
551 }
552 }
553 }
554 }
555 }
556}
557 
558impl TaffyWidget for Column {
559 fn build_layout(&self, tree: &mut TaffyTree<()>) -> TaffyLayoutResult<NodeId> {
560 let mut children_nodes = Vec::with_capacity(self.children.len());
561 for child in &self.children {
562 if let Some(taffy_child) = child.as_taffy() {
563 let node = taffy_child.build_layout(tree)?;
564 children_nodes.push(node);
565 }
566 }
567 
568 let style = Style {
569 display: Display::Flex,
570 flex_direction: FlexDirection::Column,
571 justify_content: Some(self.main_axis_alignment.to_taffy()),
572 align_items: Some(self.cross_axis_alignment.to_taffy()),
573 gap: strato_core::taffy::prelude::Size {
574 width: length(0.0),
575 height: length(self.spacing),
576 },
577 ..Default::default()
578 };
579 
580 tree.new_with_children(style, &children_nodes)
581 .map_err(|e| TaffyLayoutError::from(e))
582 }
583}
584 
585/// Stack widget for layered layout
586#[derive(Debug)]
587pub struct Stack {
588 id: WidgetId,
589 children: Vec<Box<dyn Widget>>,
590}
591 
592impl Stack {
593 /// Create a new stack
594 pub fn new() -> Self {
595 Self {
596 id: generate_id(),
597 children: Vec::new(),
598 }
599 }
600 
601 /// Add children widgets
602 pub fn children(mut self, children: Vec<Box<dyn Widget>>) -> Self {
603 self.children = children;
604 self
605 }
606 
607 /// Add a single child
608 pub fn child(mut self, child: Box<dyn Widget>) -> Self {
609 self.children.push(child);
610 self
611 }
612}
613 
614impl Widget for Stack {
615 fn id(&self) -> WidgetId {
616 self.id
617 }
618 
619 fn layout(&mut self, constraints: Constraints) -> Size {
620 let mut max_width: f32 = 0.0;
621 let mut max_height: f32 = 0.0;
622 
623 for child in &mut self.children {
624 let size = child.layout(constraints);
625 max_width = max_width.max(size.width);
626 max_height = max_height.max(size.height);
627 }
628 
629 Size::new(max_width, max_height)
630 }
631 
632 fn render(&self, batch: &mut RenderBatch, layout: Layout) {
633 // Render all children at the same position
634 for child in &self.children {
635 child.render(batch, layout);
636 }
637 }
638 
639 fn handle_event(&mut self, event: &Event) -> EventResult {
640 // Events are handled from top to bottom (reverse order)
641 for child in self.children.iter_mut().rev() {
642 if child.handle_event(event) == EventResult::Handled {
643 return EventResult::Handled;
644 }
645 }
646 EventResult::Ignored
647 }
648 
649 fn children(&self) -> Vec<&(dyn Widget + '_)> {
650 self.children.iter().map(|c| c.as_ref()).collect()
651 }
652 
653 fn children_mut<'a>(&'a mut self) -> Vec<&'a mut (dyn Widget + 'a)> {
654 self.children
655 .iter_mut()
656 .map(|c| c.as_mut() as &'a mut (dyn Widget + 'a))
657 .collect()
658 }
659 
660 fn as_any(&self) -> &dyn Any {
661 self
662 }
663 
664 fn as_any_mut(&mut self) -> &mut dyn Any {
665 self
666 }
667 
668 fn clone_widget(&self) -> Box<dyn Widget> {
669 Box::new(Stack {
670 id: generate_id(),
671 children: self.children.iter().map(|c| c.clone_widget()).collect(),
672 })
673 }
674 
675 fn as_taffy(&self) -> Option<&dyn TaffyWidget> {
676 Some(self)
677 }
678 
679 fn render_taffy(
680 &self,
681 batch: &mut RenderBatch,
682 tree: &TaffyTree<()>,
683 node: NodeId,
684 parent_offset: strato_core::types::Point,
685 ) {
686 if let Ok(layout) = tree.layout(node) {
687 let my_position = parent_offset
688 + strato_core::types::Point::new(layout.location.x, layout.location.y);
689 
690 // Render children
691 if let Ok(children_nodes) = tree.children(node) {
692 let mut child_node_idx = 0;
693 for child in &self.children {
694 if child.as_taffy().is_some() {
695 if child_node_idx < children_nodes.len() {
696 let child_node = children_nodes[child_node_idx];
697 child.render_taffy(batch, tree, child_node, my_position);
698 child_node_idx += 1;
699 }
700 }
701 }
702 }
703 }
704 }
705}
706 
707impl TaffyWidget for Stack {
708 fn build_layout(&self, tree: &mut TaffyTree<()>) -> TaffyLayoutResult<NodeId> {
709 let mut children_nodes = Vec::with_capacity(self.children.len());
710 for child in &self.children {
711 if let Some(taffy_child) = child.as_taffy() {
712 let node = taffy_child.build_layout(tree)?;
713 // Force absolute positioning for Stack children
714 let mut style = tree.style(node).cloned().unwrap_or_default();
715 style.position = Position::Absolute;
716 tree.set_style(node, style)
717 .map_err(|e| TaffyLayoutError::from(e))?;
718 children_nodes.push(node);
719 }
720 }
721 
722 let style = Style {
723 display: Display::Flex,
724 size: strato_core::taffy::prelude::Size {
725 width: percent(1.0),
726 height: percent(1.0),
727 },
728 ..Default::default()
729 };
730 
731 tree.new_with_children(style, &children_nodes)
732 .map_err(|e| TaffyLayoutError::from(e))
733 }
734}
735 
736/// Flexible widget for flex layout
737#[derive(Debug)]
738pub struct Flex {
739 id: WidgetId,
740 child: Box<dyn Widget>,
741 flex: f32,
742}
743 
744impl Flex {
745 /// Create a new flex widget
746 pub fn new(child: Box<dyn Widget>) -> Self {
747 Self {
748 id: generate_id(),
749 child,
750 flex: 1.0,
751 }
752 }
753 
754 /// Set flex factor
755 pub fn flex(mut self, flex: f32) -> Self {
756 self.flex = flex;
757 self
758 }
759}
760 
761impl Widget for Flex {
762 fn id(&self) -> WidgetId {
763 self.id
764 }
765 
766 fn layout(&mut self, constraints: Constraints) -> Size {
767 self.child.layout(constraints)
768 }
769 
770 fn render(&self, batch: &mut RenderBatch, layout: Layout) {
771 self.child.render(batch, layout);
772 }
773 
774 fn handle_event(&mut self, event: &Event) -> EventResult {
775 self.child.handle_event(event)
776 }
777 
778 fn children(&self) -> Vec<&(dyn Widget + '_)> {
779 vec![self.child.as_ref()]
780 }
781 
782 fn children_mut<'a>(&'a mut self) -> Vec<&'a mut (dyn Widget + 'a)> {
783 vec![self.child.as_mut() as &'a mut (dyn Widget + 'a)]
784 }
785 
786 fn as_any(&self) -> &dyn Any {
787 self
788 }
789 
790 fn as_any_mut(&mut self) -> &mut dyn Any {
791 self
792 }
793 
794 fn clone_widget(&self) -> Box<dyn Widget> {
795 Box::new(Flex {
796 id: generate_id(),
797 child: self.child.clone_widget(),
798 flex: self.flex,
799 })
800 }
801 
802 fn as_taffy(&self) -> Option<&dyn TaffyWidget> {
803 Some(self)
804 }
805 
806 fn render_taffy(
807 &self,
808 batch: &mut RenderBatch,
809 tree: &TaffyTree<()>,
810 node: NodeId,
811 parent_offset: strato_core::types::Point,
812 ) {
813 // Flex doesn't have its own node in Taffy (it configures the child's node).
814 // So we pass the same node to the child.
815 self.child.render_taffy(batch, tree, node, parent_offset);
816 }
817}
818 
819impl TaffyWidget for Flex {
820 fn build_layout(&self, tree: &mut TaffyTree<()>) -> TaffyLayoutResult<NodeId> {
821 if let Some(taffy_child) = self.child.as_taffy() {
822 let node = taffy_child.build_layout(tree)?;
823 let mut style = tree
824 .style(node)
825 .map_err(|e| TaffyLayoutError::from(e))?
826 .clone();
827 style.flex_grow = self.flex;
828 tree.set_style(node, style)
829 .map_err(|e| TaffyLayoutError::from(e))?;
830 Ok(node)
831 } else {
832 Err(TaffyLayoutError::NodeBuildFailed)
833 }
834 }
835}
836