1use cvkg_core::{Alignment, Distribution, LayoutCache, LayoutView, Rect, Size, SizeProposal};
2use std::collections::HashMap;
3use taffy::prelude::*;
4
5pub struct TaffyLayoutEngine {
8 pub tree: taffy::TaffyTree,
9 pub node_map: HashMap<u64, taffy::NodeId>,
10}
11
12impl Default for TaffyLayoutEngine {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl TaffyLayoutEngine {
19 pub fn new() -> Self {
21 Self {
22 tree: taffy::TaffyTree::new(),
23 node_map: HashMap::new(),
24 }
25 }
26
27 pub fn get_or_insert_engine(cache: &mut LayoutCache) -> &mut Self {
29 if cache.engine.is_none() {
30 cache.engine = Some(Box::new(TaffyLayoutEngine::new()));
31 }
32 cache
33 .engine
34 .as_mut()
35 .unwrap()
36 .downcast_mut::<TaffyLayoutEngine>()
37 .unwrap()
38 }
39}
40
41pub fn taffy_alignment(alignment: cvkg_core::Alignment) -> Option<taffy::AlignItems> {
42 match alignment {
43 cvkg_core::Alignment::Leading => Some(taffy::AlignItems::Start),
44 cvkg_core::Alignment::Center => Some(taffy::AlignItems::Center),
45 cvkg_core::Alignment::Trailing => Some(taffy::AlignItems::End),
46 cvkg_core::Alignment::Top => Some(taffy::AlignItems::Start),
47 cvkg_core::Alignment::Bottom => Some(taffy::AlignItems::End),
48 }
49}
50
51pub fn taffy_distribution(dist: cvkg_core::Distribution) -> Option<taffy::JustifyContent> {
52 match dist {
53 cvkg_core::Distribution::Leading => Some(taffy::JustifyContent::Start),
54 cvkg_core::Distribution::Center => Some(taffy::JustifyContent::Center),
55 cvkg_core::Distribution::Trailing => Some(taffy::JustifyContent::End),
56 cvkg_core::Distribution::SpaceBetween => Some(taffy::JustifyContent::SpaceBetween),
57 cvkg_core::Distribution::Fill => Some(taffy::JustifyContent::Stretch),
58 _ => None,
59 }
60}
61
62#[derive(Clone, Copy)]
64pub struct FlexParams {
65 pub dir: taffy::FlexDirection,
66 pub spacing: f32,
67 pub alignment: cvkg_core::Alignment,
68 pub distribution: cvkg_core::Distribution,
69 pub bounds: Rect,
70 pub container_hash: u64,
71}
72
73pub fn collect_child_sizes(
75 subviews: &[&dyn LayoutView],
76 bounds: Rect,
77 cache: &mut LayoutCache,
78) -> (Vec<u64>, Vec<f32>, Vec<Size>) {
79 let mut sizes = Vec::with_capacity(subviews.len());
80 let mut hashes = Vec::with_capacity(subviews.len());
81 let mut flex_weights = Vec::with_capacity(subviews.len());
82
83 for child in subviews {
84 let hash = child.view_hash();
85 hashes.push(hash);
86 flex_weights.push(child.flex_weight());
87
88 let proposal = SizeProposal::new(Some(bounds.width), Some(bounds.height));
89 let cached_size = if hash != 0 {
90 cache.get_size(hash, proposal)
91 } else {
92 None
93 };
94
95 let size = match cached_size {
96 Some(sz) => sz,
97 None => {
98 let sz = crate::with_layout_cycle_guard(hash, Size::ZERO, || {
99 child.size_that_fits(proposal, &[], cache)
100 });
101 if hash != 0 {
102 cache.set_size(hash, proposal, sz);
103 }
104 sz
105 }
106 };
107 if hash != 0 {
108 cache.register_parent(hash, 0);
109 }
110 sizes.push(size);
111 }
112
113 (hashes, flex_weights, sizes)
114}
115
116pub fn intrinsic_flex_size(dir: taffy::FlexDirection, spacing: f32, sizes: &[Size]) -> Size {
118 if sizes.is_empty() {
119 return Size::ZERO;
120 }
121 let n = sizes.len();
122 match dir {
123 taffy::FlexDirection::Row | taffy::FlexDirection::RowReverse => {
124 let total_width: f32 = sizes.iter().map(|s| s.width).sum();
125 let max_height: f32 = sizes.iter().map(|s| s.height).fold(0.0, f32::max);
126 Size {
127 width: total_width + spacing * (n.saturating_sub(1) as f32),
128 height: max_height,
129 }
130 }
131 taffy::FlexDirection::Column | taffy::FlexDirection::ColumnReverse => {
132 let max_width: f32 = sizes.iter().map(|s| s.width).fold(0.0, f32::max);
133 let total_height: f32 = sizes.iter().map(|s| s.height).sum();
134 Size {
135 width: max_width,
136 height: total_height + spacing * (n.saturating_sub(1) as f32),
137 }
138 }
139 }
140}
141
142pub fn compute_taffy_flex(
143 params: &FlexParams,
144 subviews: &[&dyn LayoutView],
145 cache: &mut LayoutCache,
146) -> Vec<Rect> {
147 if cache.is_over_budget() {
148 let mut rects = Vec::with_capacity(subviews.len());
149 for child in subviews {
150 let hash = child.view_hash();
151 let r = if hash != 0 {
152 cache.previous_rects.get(&hash).copied().unwrap_or(Rect::zero())
153 } else {
154 Rect::zero()
155 };
156 rects.push(r);
157 }
158 return rects;
159 }
160
161 let (hashes, flex_weights, sizes) = collect_child_sizes(subviews, params.bounds, cache);
162
163 for &hash in &hashes {
164 if hash != 0 && params.container_hash != 0 {
165 cache.register_parent(hash, params.container_hash);
166 }
167 }
168
169 let engine = TaffyLayoutEngine::get_or_insert_engine(cache);
170 let mut child_nodes = Vec::with_capacity(subviews.len());
171
172 for ((&hash, &flex_weight), &size) in hashes.iter().zip(&flex_weights).zip(&sizes) {
173 let style = if flex_weight > 0.0 {
174 taffy::Style {
175 size: taffy::Size {
176 width: if params.dir == taffy::FlexDirection::Row {
177 taffy::Dimension::Auto
178 } else {
179 taffy::Dimension::Length(size.width)
180 },
181 height: if params.dir == taffy::FlexDirection::Column {
182 taffy::Dimension::Auto
183 } else {
184 taffy::Dimension::Length(size.height)
185 },
186 },
187 flex_grow: flex_weight,
188 flex_basis: taffy::Dimension::Percent(0.0),
189 ..Default::default()
190 }
191 } else {
192 taffy::Style {
193 size: taffy::Size {
194 width: taffy::Dimension::Length(size.width),
195 height: taffy::Dimension::Length(size.height),
196 },
197 ..Default::default()
198 }
199 };
200
201 let node = if hash != 0 {
202 if let Some(&existing) = engine.node_map.get(&hash) {
203 let _ = engine.tree.set_style(existing, style);
204 existing
205 } else {
206 let new_node = engine.tree.new_leaf(style).unwrap();
207 engine.node_map.insert(hash, new_node);
208 new_node
209 }
210 } else {
211 engine.tree.new_leaf(style).unwrap()
212 };
213 child_nodes.push(node);
214 }
215
216 let gap_val = taffy::LengthPercentage::Length(params.spacing);
217 let container_style = taffy::Style {
218 display: taffy::Display::Flex,
219 flex_direction: params.dir,
220 gap: taffy::Size {
221 width: if params.dir == taffy::FlexDirection::Row {
222 gap_val
223 } else {
224 taffy::LengthPercentage::Length(0.0)
225 },
226 height: if params.dir == taffy::FlexDirection::Column {
227 gap_val
228 } else {
229 taffy::LengthPercentage::Length(0.0)
230 },
231 },
232 align_items: taffy_alignment(params.alignment),
233 justify_content: taffy_distribution(params.distribution),
234 size: taffy::Size {
235 width: taffy::Dimension::Length(params.bounds.width),
236 height: taffy::Dimension::Length(params.bounds.height),
237 },
238 ..Default::default()
239 };
240
241 let root_node = if params.container_hash != 0 {
242 if let Some(&existing) = engine.node_map.get(¶ms.container_hash) {
243 let _ = engine.tree.set_style(existing, container_style);
244 let _ = engine.tree.set_children(existing, &child_nodes);
245 existing
246 } else {
247 let new_node = engine
248 .tree
249 .new_with_children(container_style, &child_nodes)
250 .unwrap();
251 engine.node_map.insert(params.container_hash, new_node);
252 new_node
253 }
254 } else {
255 engine
256 .tree
257 .new_with_children(container_style, &child_nodes)
258 .unwrap()
259 };
260
261 engine
262 .tree
263 .compute_layout(root_node, taffy::Size::MAX_CONTENT)
264 .unwrap();
265
266 let mut rects = Vec::with_capacity(subviews.len());
267 for &node in &child_nodes {
268 let layout = engine.tree.layout(node).unwrap();
269 rects.push(Rect {
270 x: params.bounds.x + layout.location.x,
271 y: params.bounds.y + layout.location.y,
272 width: layout.size.width,
273 height: layout.size.height,
274 });
275 }
276
277 if params.container_hash == 0 {
278 let _ = engine.tree.remove(root_node);
279 }
280
281 rects
282}
283
284pub struct HStack {
286 spacing: f32,
287 alignment: Alignment,
288 distribution: Distribution,
289}
290
291impl HStack {
292 pub fn new(spacing: f32, alignment: Alignment, distribution: Distribution) -> Self {
294 Self {
295 spacing,
296 alignment,
297 distribution,
298 }
299 }
300
301 pub fn compute_layout(
303 spacing: f32,
304 alignment: Alignment,
305 distribution: Distribution,
306 bounds: Rect,
307 subviews: &[&dyn LayoutView],
308 cache: &mut LayoutCache,
309 ) -> Vec<Rect> {
310 Self::compute_layout_incremental(
311 spacing,
312 alignment,
313 distribution,
314 bounds,
315 0,
316 subviews,
317 cache,
318 )
319 }
320
321 pub fn compute_layout_incremental(
322 spacing: f32,
323 alignment: Alignment,
324 distribution: Distribution,
325 bounds: Rect,
326 container_hash: u64,
327 subviews: &[&dyn LayoutView],
328 cache: &mut LayoutCache,
329 ) -> Vec<Rect> {
330 compute_taffy_flex(
331 &FlexParams {
332 dir: taffy::FlexDirection::Row,
333 spacing,
334 alignment,
335 distribution,
336 bounds,
337 container_hash,
338 },
339 subviews,
340 cache,
341 )
342 }
343}
344
345impl LayoutView for HStack {
346 fn size_that_fits(
347 &self,
348 proposal: SizeProposal,
349 subviews: &[&dyn LayoutView],
350 cache: &mut LayoutCache,
351 ) -> Size {
352 let bounds = Rect {
353 x: 0.0,
354 y: 0.0,
355 width: proposal.width.unwrap_or(10000.0),
356 height: proposal.height.unwrap_or(10000.0),
357 };
358 let (_, _, sizes) = collect_child_sizes(subviews, bounds, cache);
359 intrinsic_flex_size(taffy::FlexDirection::Row, self.spacing, &sizes)
360 }
361
362 fn place_subviews(
363 &self,
364 bounds: Rect,
365 subviews: &mut [&mut dyn LayoutView],
366 cache: &mut LayoutCache,
367 ) {
368 let views: Vec<&dyn LayoutView> =
369 subviews.iter().map(|v| &**v as &dyn LayoutView).collect();
370 let rects = Self::compute_layout_incremental(
371 self.spacing,
372 self.alignment,
373 self.distribution,
374 bounds,
375 self.view_hash(),
376 &views,
377 cache,
378 );
379 crate::animation::apply_layout_animations(rects, subviews, cache);
380 }
381}
382
383pub struct VStack {
385 spacing: f32,
386 alignment: Alignment,
387 distribution: Distribution,
388}
389
390impl VStack {
391 pub fn new(spacing: f32, alignment: Alignment, distribution: Distribution) -> Self {
393 Self {
394 spacing,
395 alignment,
396 distribution,
397 }
398 }
399
400 pub fn compute_layout(
402 spacing: f32,
403 alignment: Alignment,
404 distribution: Distribution,
405 bounds: Rect,
406 subviews: &[&dyn LayoutView],
407 cache: &mut LayoutCache,
408 ) -> Vec<Rect> {
409 Self::compute_layout_incremental(
410 spacing,
411 alignment,
412 distribution,
413 bounds,
414 0,
415 subviews,
416 cache,
417 )
418 }
419
420 pub fn compute_layout_incremental(
421 spacing: f32,
422 alignment: Alignment,
423 distribution: Distribution,
424 bounds: Rect,
425 container_hash: u64,
426 subviews: &[&dyn LayoutView],
427 cache: &mut LayoutCache,
428 ) -> Vec<Rect> {
429 compute_taffy_flex(
430 &FlexParams {
431 dir: taffy::FlexDirection::Column,
432 spacing,
433 alignment,
434 distribution,
435 bounds,
436 container_hash,
437 },
438 subviews,
439 cache,
440 )
441 }
442}
443
444impl LayoutView for VStack {
445 fn size_that_fits(
446 &self,
447 proposal: SizeProposal,
448 subviews: &[&dyn LayoutView],
449 cache: &mut LayoutCache,
450 ) -> Size {
451 let bounds = Rect {
452 x: 0.0,
453 y: 0.0,
454 width: proposal.width.unwrap_or(10000.0),
455 height: proposal.height.unwrap_or(10000.0),
456 };
457 let (_, _, sizes) = collect_child_sizes(subviews, bounds, cache);
458 intrinsic_flex_size(taffy::FlexDirection::Column, self.spacing, &sizes)
459 }
460
461 fn place_subviews(
462 &self,
463 bounds: Rect,
464 subviews: &mut [&mut dyn LayoutView],
465 cache: &mut LayoutCache,
466 ) {
467 let views: Vec<&dyn LayoutView> =
468 subviews.iter().map(|v| &**v as &dyn LayoutView).collect();
469 let rects = Self::compute_layout_incremental(
470 self.spacing,
471 self.alignment,
472 self.distribution,
473 bounds,
474 self.view_hash(),
475 &views,
476 cache,
477 );
478 crate::animation::apply_layout_animations(rects, subviews, cache);
479 }
480}
481
482pub struct ZStack {}
484
485impl Default for ZStack {
486 fn default() -> Self {
487 Self::new()
488 }
489}
490
491impl ZStack {
492 pub fn new() -> Self {
494 Self {}
495 }
496}
497
498impl LayoutView for ZStack {
499 fn size_that_fits(
500 &self,
501 proposal: SizeProposal,
502 subviews: &[&dyn LayoutView],
503 cache: &mut LayoutCache,
504 ) -> Size {
505 let mut width = 0.0f32;
506 let mut height = 0.0f32;
507 let self_hash = self.view_hash();
508
509 for child in subviews.iter() {
510 let child_hash = child.view_hash();
511 if self_hash != 0 && child_hash != 0 {
512 cache.register_parent(child_hash, self_hash);
513 }
514 let child_size = crate::with_layout_cycle_guard(child_hash, Size::ZERO, || {
515 child.size_that_fits(proposal, &[], cache)
516 });
517 width = width.max(child_size.width);
518 height = height.max(child_size.height);
519 }
520
521 Size { width, height }
522 }
523
524 fn place_subviews(
525 &self,
526 bounds: Rect,
527 subviews: &mut [&mut dyn LayoutView],
528 cache: &mut LayoutCache,
529 ) {
530 let self_hash = self.view_hash();
531 for child in subviews.iter_mut() {
532 let child_hash = child.view_hash();
533 if self_hash != 0 && child_hash != 0 {
534 cache.register_parent(child_hash, self_hash);
535 }
536 let is_visible = if let Some(viewport) = cache.viewport {
537 bounds.intersects(&viewport)
538 } else {
539 true
540 };
541 if is_visible {
542 crate::with_layout_cycle_guard_void(child_hash, || {
543 child.place_subviews(bounds, &mut [], cache);
544 });
545 }
546 }
547 }
548}
549
550pub struct Spacer;
552
553impl LayoutView for Spacer {
554 fn size_that_fits(
555 &self,
556 proposal: SizeProposal,
557 _subviews: &[&dyn LayoutView],
558 _cache: &mut LayoutCache,
559 ) -> Size {
560 Size {
561 width: proposal.width.unwrap_or(0.0),
562 height: proposal.height.unwrap_or(0.0),
563 }
564 }
565
566 fn place_subviews(
567 &self,
568 _bounds: Rect,
569 _subviews: &mut [&mut dyn LayoutView],
570 _cache: &mut LayoutCache,
571 ) {
572 }
573}
574
575pub struct Flex {
577 pub orientation: cvkg_core::Orientation,
578 pub spacing: f32,
579}
580
581impl Flex {
582 pub fn new(orientation: cvkg_core::Orientation, spacing: f32) -> Self {
583 Self {
584 orientation,
585 spacing,
586 }
587 }
588}
589
590impl LayoutView for Flex {
591 fn size_that_fits(
592 &self,
593 proposal: SizeProposal,
594 _subviews: &[&dyn LayoutView],
595 _cache: &mut LayoutCache,
596 ) -> Size {
597 Size {
598 width: proposal.width.unwrap_or(100.0),
599 height: proposal.height.unwrap_or(100.0),
600 }
601 }
602
603 fn place_subviews(
604 &self,
605 bounds: Rect,
606 subviews: &mut [&mut dyn LayoutView],
607 cache: &mut LayoutCache,
608 ) {
609 if subviews.is_empty() {
610 return;
611 }
612
613 let self_hash = self.view_hash();
614 let n = subviews.len() as f32;
615 match self.orientation {
616 cvkg_core::Orientation::Horizontal => {
617 let total_spacing = self.spacing * (n - 1.0);
618 let item_width = (bounds.width - total_spacing) / n;
619 for (i, child) in subviews.iter_mut().enumerate() {
620 let child_rect = Rect {
621 x: bounds.x + i as f32 * (item_width + self.spacing),
622 y: bounds.y,
623 width: item_width,
624 height: bounds.height,
625 };
626 let child_hash = child.view_hash();
627 if self_hash != 0 && child_hash != 0 {
628 cache.register_parent(child_hash, self_hash);
629 }
630 let is_visible = if let Some(viewport) = cache.viewport {
631 child_rect.intersects(&viewport)
632 } else {
633 true
634 };
635 if is_visible {
636 crate::with_layout_cycle_guard_void(child_hash, || {
637 child.place_subviews(child_rect, &mut [], cache);
638 });
639 }
640 }
641 }
642 cvkg_core::Orientation::Vertical => {
643 let total_spacing = self.spacing * (n - 1.0);
644 let item_height = (bounds.height - total_spacing) / n;
645 for (i, child) in subviews.iter_mut().enumerate() {
646 let child_rect = Rect {
647 x: bounds.x,
648 y: bounds.y + i as f32 * (item_height + self.spacing),
649 width: bounds.width,
650 height: item_height,
651 };
652 let child_hash = child.view_hash();
653 if self_hash != 0 && child_hash != 0 {
654 cache.register_parent(child_hash, self_hash);
655 }
656 let is_visible = if let Some(viewport) = cache.viewport {
657 child_rect.intersects(&viewport)
658 } else {
659 true
660 };
661 if is_visible {
662 crate::with_layout_cycle_guard_void(child_hash, || {
663 child.place_subviews(child_rect, &mut [], cache);
664 });
665 }
666 }
667 }
668 }
669 }
670}
671
672#[derive(Debug, Clone, Copy, PartialEq)]
674pub enum GridTrack {
675 Fixed(f32),
677 Flex(f32),
679 Auto,
681 MinMax(f32, f32),
683}
684
685pub fn taffy_track(track: GridTrack) -> taffy::TrackSizingFunction {
686 match track {
687 GridTrack::Fixed(v) => taffy::prelude::length(v),
688 GridTrack::Flex(v) => taffy::prelude::fr(v),
689 GridTrack::Auto => taffy::prelude::auto(),
690 GridTrack::MinMax(min, max) => {
691 taffy::prelude::minmax(taffy::prelude::length(min), taffy::prelude::length(max))
692 }
693 }
694}
695
696pub struct Grid {
698 pub columns: Vec<GridTrack>,
699 pub rows: Vec<GridTrack>,
700 pub column_gap: f32,
701 pub row_gap: f32,
702}
703
704impl Grid {
705 pub fn new(
707 columns: Vec<GridTrack>,
708 rows: Vec<GridTrack>,
709 column_gap: f32,
710 row_gap: f32,
711 ) -> Self {
712 Self {
713 columns,
714 rows,
715 column_gap,
716 row_gap,
717 }
718 }
719
720 pub fn compute_layout_rects(
722 &self,
723 bounds: Rect,
724 subviews: &[&dyn LayoutView],
725 placements: &[Option<cvkg_core::GridPlacement>],
726 cache: &mut LayoutCache,
727 ) -> Vec<Rect> {
728 self.compute_layout_rects_incremental(bounds, 0, subviews, placements, cache)
729 }
730
731 pub fn compute_layout_rects_incremental(
732 &self,
733 bounds: Rect,
734 container_hash: u64,
735 subviews: &[&dyn LayoutView],
736 placements: &[Option<cvkg_core::GridPlacement>],
737 cache: &mut LayoutCache,
738 ) -> Vec<Rect> {
739 if cache.is_over_budget() {
740 let mut rects = Vec::with_capacity(subviews.len());
741 for child in subviews {
742 let hash = child.view_hash();
743 let r = if hash != 0 {
744 cache.previous_rects.get(&hash).copied().unwrap_or(Rect::zero())
745 } else {
746 Rect::zero()
747 };
748 rects.push(r);
749 }
750 return rects;
751 }
752
753 let mut hashes = Vec::with_capacity(subviews.len());
754 for child in subviews {
755 let hash = child.view_hash();
756 hashes.push(hash);
757 if container_hash != 0 && hash != 0 {
758 cache.register_parent(hash, container_hash);
759 }
760 }
761
762 let engine = TaffyLayoutEngine::get_or_insert_engine(cache);
763 let mut child_nodes = Vec::with_capacity(subviews.len());
764
765 for (hash, placement) in hashes.iter().zip(placements.iter()) {
766 let style = if let Some(p) = placement.as_ref() {
767 taffy::Style {
768 size: taffy::Size {
769 width: taffy::Dimension::Auto,
770 height: taffy::Dimension::Auto,
771 },
772 grid_column: taffy::Line {
773 start: taffy::prelude::line((p.column + 1) as i16),
774 end: taffy::prelude::span(p.column_span as u16),
775 },
776 grid_row: taffy::Line {
777 start: taffy::prelude::line((p.row + 1) as i16),
778 end: taffy::prelude::span(p.row_span as u16),
779 },
780 ..Default::default()
781 }
782 } else {
783 taffy::Style {
784 size: taffy::Size {
785 width: taffy::Dimension::Auto,
786 height: taffy::Dimension::Auto,
787 },
788 ..Default::default()
789 }
790 };
791
792 let node = if *hash != 0 {
793 if let Some(&existing) = engine.node_map.get(hash) {
794 let _ = engine.tree.set_style(existing, style);
795 existing
796 } else {
797 let new_node = engine.tree.new_leaf(style).unwrap();
798 engine.node_map.insert(*hash, new_node);
799 new_node
800 }
801 } else {
802 engine.tree.new_leaf(style).unwrap()
803 };
804 child_nodes.push(node);
805 }
806
807 let container_style = taffy::Style {
808 display: taffy::Display::Grid,
809 grid_template_columns: self.columns.iter().copied().map(taffy_track).collect(),
810 grid_template_rows: self.rows.iter().copied().map(taffy_track).collect(),
811 gap: taffy::Size {
812 width: taffy::LengthPercentage::Length(self.column_gap),
813 height: taffy::LengthPercentage::Length(self.row_gap),
814 },
815 size: taffy::Size {
816 width: taffy::Dimension::Length(bounds.width),
817 height: taffy::Dimension::Length(bounds.height),
818 },
819 ..Default::default()
820 };
821
822 let root_node = if container_hash != 0 {
823 if let Some(&existing) = engine.node_map.get(&container_hash) {
824 let _ = engine.tree.set_style(existing, container_style);
825 let _ = engine.tree.set_children(existing, &child_nodes);
826 existing
827 } else {
828 let new_node = engine
829 .tree
830 .new_with_children(container_style, &child_nodes)
831 .unwrap();
832 engine.node_map.insert(container_hash, new_node);
833 new_node
834 }
835 } else {
836 engine
837 .tree
838 .new_with_children(container_style, &child_nodes)
839 .unwrap()
840 };
841
842 engine
843 .tree
844 .compute_layout(root_node, taffy::Size::MAX_CONTENT)
845 .unwrap();
846
847 let mut rects = Vec::with_capacity(subviews.len());
848 for &node in &child_nodes {
849 let layout = engine.tree.layout(node).unwrap();
850 rects.push(Rect {
851 x: bounds.x + layout.location.x,
852 y: bounds.y + layout.location.y,
853 width: layout.size.width,
854 height: layout.size.height,
855 });
856 }
857
858 if container_hash == 0 {
859 let _ = engine.tree.remove(root_node);
860 }
861 rects
862 }
863}
864
865impl LayoutView for Grid {
866 fn size_that_fits(
867 &self,
868 proposal: SizeProposal,
869 _subviews: &[&dyn LayoutView],
870 _cache: &mut LayoutCache,
871 ) -> Size {
872 Size {
873 width: proposal.width.unwrap_or(200.0),
874 height: proposal.height.unwrap_or(200.0),
875 }
876 }
877
878 fn place_subviews(
879 &self,
880 bounds: Rect,
881 subviews: &mut [&mut dyn LayoutView],
882 cache: &mut LayoutCache,
883 ) {
884 let views: Vec<&dyn LayoutView> =
885 subviews.iter().map(|v| &**v as &dyn LayoutView).collect();
886 let placements = vec![None; subviews.len()];
887 let rects = self.compute_layout_rects_incremental(
888 bounds,
889 self.view_hash(),
890 &views,
891 &placements,
892 cache,
893 );
894 crate::animation::apply_layout_animations(rects, subviews, cache);
895 }
896}