1use super::widget::Assignment;
5use crate::{Element, Renderer};
6use iced_core::layout::{Limits, Node};
7use iced_core::widget::Tree;
8use iced_core::{Alignment, Length, Padding, Point, Size};
9
10use taffy::geometry::{Line, Rect};
11use taffy::style::{AlignItems, Dimension, Display, GridPlacement, Style};
12use taffy::style_helpers::{auto, length};
13use taffy::{AlignContent, TaffyTree};
14
15#[allow(clippy::too_many_arguments)]
16#[allow(clippy::too_many_lines)]
17pub fn resolve<Message>(
18 renderer: &Renderer,
19 limits: &Limits,
20 items: &[Element<'_, Message>],
21 assignments: &[Assignment],
22 width: Length,
23 height: Length,
24 padding: Padding,
25 column_alignment: Alignment,
26 row_alignment: Alignment,
27 justify_content: Option<AlignContent>,
28 column_spacing: f32,
29 row_spacing: f32,
30 tree: &mut [Tree],
31) -> Node {
32 let max_size = limits.max();
33
34 let mut leafs = Vec::with_capacity(items.len());
35 let mut nodes = Vec::with_capacity(items.len());
36
37 let mut taffy = TaffyTree::<()>::with_capacity(items.len() + 1);
38
39 for ((child, assignment), tree) in items.iter().zip(assignments.iter()).zip(tree.iter_mut()) {
41 let child_widget = child.as_widget();
43 let child_node = child_widget.layout(tree, renderer, limits);
44 let size = child_node.size();
45
46 nodes.push(child_node);
47
48 let c_size = child_widget.size();
49 let (width, flex_grow, justify_self) = match c_size.width {
50 Length::Fill | Length::FillPortion(_) => {
51 (Dimension::Auto, 1.0, Some(AlignItems::Stretch))
52 }
53 _ => (length(size.width), 0.0, None),
54 };
55
56 let leaf = taffy.new_leaf(Style {
58 flex_grow,
59
60 grid_column: Line {
61 start: GridPlacement::Line((assignment.column as i16).into()),
62 end: GridPlacement::Line(
63 (assignment.column as i16 + assignment.width as i16).into(),
64 ),
65 },
66
67 grid_row: Line {
68 start: GridPlacement::Line((assignment.row as i16).into()),
69 end: GridPlacement::Line((assignment.row as i16 + assignment.height as i16).into()),
70 },
71
72 size: taffy::geometry::Size {
73 width,
74 height: match c_size.height {
75 Length::Fill | Length::FillPortion(_) => Dimension::Auto,
76 _ => length(size.height),
77 },
78 },
79
80 justify_self,
81
82 ..Style::default()
83 });
84
85 match leaf {
86 Ok(leaf) => leafs.push(leaf),
87 Err(why) => {
88 tracing::error!(?why, "cannot add leaf node to grid");
89 continue;
90 }
91 }
92 }
93
94 let root = taffy.new_with_children(
95 Style {
96 align_items: Some(match width {
97 Length::Fill | Length::FillPortion(_) => AlignItems::Stretch,
98 _ => match row_alignment {
99 Alignment::Start => AlignItems::Start,
100 Alignment::Center => AlignItems::Center,
101 Alignment::End => AlignItems::End,
102 },
103 }),
104
105 display: Display::Grid,
106
107 gap: taffy::geometry::Size {
108 width: length(column_spacing),
109 height: length(row_spacing),
110 },
111
112 justify_items: Some(match height {
113 Length::Fill | Length::FillPortion(_) => AlignItems::Stretch,
114 _ => match column_alignment {
115 Alignment::Start => AlignItems::Start,
116 Alignment::Center => AlignItems::Center,
117 Alignment::End => AlignItems::End,
118 },
119 }),
120
121 justify_content,
122
123 padding: Rect {
124 left: length(padding.left),
125 right: length(padding.right),
126 top: length(padding.top),
127 bottom: length(padding.bottom),
128 },
129
130 size: taffy::geometry::Size {
131 width: match width {
132 Length::Fixed(fixed) => length(fixed),
133 _ => auto(),
134 },
135 height: match height {
136 Length::Fixed(fixed) => length(fixed),
137 _ => auto(),
138 },
139 },
140
141 ..Style::default()
142 },
143 &leafs,
144 );
145
146 let root = match root {
147 Ok(root) => root,
148 Err(why) => {
149 tracing::error!(?why, "grid root style invalid");
150 return Node::new(Size::ZERO);
151 }
152 };
153
154 if let Err(why) = taffy.compute_layout(
155 root,
156 taffy::geometry::Size {
157 width: length(max_size.width),
158 height: length(max_size.height),
159 },
160 ) {
161 tracing::error!(?why, "grid layout did not compute");
162 return Node::new(Size::ZERO);
163 }
164
165 let grid_layout = match taffy.layout(root) {
166 Ok(layout) => layout,
167 Err(why) => {
168 tracing::error!(?why, "cannot get layout of grid");
169 return Node::new(Size::ZERO);
170 }
171 };
172
173 for (((leaf, child), node), tree) in leafs
174 .into_iter()
175 .zip(items.iter())
176 .zip(nodes.iter_mut())
177 .zip(tree)
178 {
179 if let Ok(leaf_layout) = taffy.layout(leaf) {
180 let child_widget = child.as_widget();
181 let c_size = child_widget.size();
182 match c_size.width {
183 Length::Fill | Length::FillPortion(_) => {
184 *node =
185 child_widget.layout(tree, renderer, &limits.width(leaf_layout.size.width));
186 }
187 _ => (),
188 }
189
190 *node = node.clone().move_to(Point {
191 x: leaf_layout.location.x,
192 y: leaf_layout.location.y,
193 })
194 }
195 }
196
197 let grid_size = Size {
198 width: grid_layout.content_size.width,
199 height: grid_layout.content_size.height,
200 };
201
202 Node::with_children(grid_size, nodes)
203}