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: &mut [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
41 .iter_mut()
42 .zip(assignments.iter())
43 .zip(tree.iter_mut())
44 {
45 let child_widget = child.as_widget_mut();
47 let child_node = child_widget.layout(tree, renderer, limits);
48 let size = child_node.size();
49
50 nodes.push(child_node);
51
52 let c_size = child_widget.size();
53 let (width, flex_grow, justify_self) = match c_size.width {
54 Length::Fill | Length::FillPortion(_) => {
55 (Dimension::auto(), 1.0, Some(AlignItems::Stretch))
56 }
57 _ => (length(size.width), 0.0, None),
58 };
59
60 let leaf = taffy.new_leaf(Style {
62 flex_grow,
63
64 grid_column: Line {
65 start: GridPlacement::Line((assignment.column as i16).into()),
66 end: GridPlacement::Line(
67 (assignment.column as i16 + assignment.width as i16).into(),
68 ),
69 },
70
71 grid_row: Line {
72 start: GridPlacement::Line((assignment.row as i16).into()),
73 end: GridPlacement::Line((assignment.row as i16 + assignment.height as i16).into()),
74 },
75
76 size: taffy::geometry::Size {
77 width,
78 height: match c_size.height {
79 Length::Fill | Length::FillPortion(_) => Dimension::auto(),
80 _ => length(size.height),
81 },
82 },
83
84 justify_self,
85
86 ..Style::default()
87 });
88
89 match leaf {
90 Ok(leaf) => leafs.push(leaf),
91 Err(why) => {
92 tracing::error!(?why, "cannot add leaf node to grid");
93 continue;
94 }
95 }
96 }
97
98 let root = taffy.new_with_children(
99 Style {
100 align_items: Some(match width {
101 Length::Fill | Length::FillPortion(_) => AlignItems::Stretch,
102 _ => match row_alignment {
103 Alignment::Start => AlignItems::Start,
104 Alignment::Center => AlignItems::Center,
105 Alignment::End => AlignItems::End,
106 },
107 }),
108
109 display: Display::Grid,
110
111 gap: taffy::geometry::Size {
112 width: length(column_spacing),
113 height: length(row_spacing),
114 },
115
116 justify_items: Some(match height {
117 Length::Fill | Length::FillPortion(_) => AlignItems::Stretch,
118 _ => match column_alignment {
119 Alignment::Start => AlignItems::Start,
120 Alignment::Center => AlignItems::Center,
121 Alignment::End => AlignItems::End,
122 },
123 }),
124
125 justify_content,
126
127 padding: Rect {
128 left: length(padding.left),
129 right: length(padding.right),
130 top: length(padding.top),
131 bottom: length(padding.bottom),
132 },
133
134 size: taffy::geometry::Size {
135 width: match width {
136 Length::Fixed(fixed) => length(fixed),
137 _ => auto(),
138 },
139 height: match height {
140 Length::Fixed(fixed) => length(fixed),
141 _ => auto(),
142 },
143 },
144
145 ..Style::default()
146 },
147 &leafs,
148 );
149
150 let root = match root {
151 Ok(root) => root,
152 Err(why) => {
153 tracing::error!(?why, "grid root style invalid");
154 return Node::new(Size::ZERO);
155 }
156 };
157
158 if let Err(why) = taffy.compute_layout(
159 root,
160 taffy::geometry::Size {
161 width: length(max_size.width),
162 height: length(max_size.height),
163 },
164 ) {
165 tracing::error!(?why, "grid layout did not compute");
166 return Node::new(Size::ZERO);
167 }
168
169 let grid_layout = match taffy.layout(root) {
170 Ok(layout) => layout,
171 Err(why) => {
172 tracing::error!(?why, "cannot get layout of grid");
173 return Node::new(Size::ZERO);
174 }
175 };
176
177 for (((leaf, child), node), tree) in leafs
178 .into_iter()
179 .zip(items.iter_mut())
180 .zip(nodes.iter_mut())
181 .zip(tree)
182 {
183 if let Ok(leaf_layout) = taffy.layout(leaf) {
184 let child_widget = child.as_widget_mut();
185 let c_size = child_widget.size();
186 match c_size.width {
187 Length::Fill | Length::FillPortion(_) => {
188 *node =
189 child_widget.layout(tree, renderer, &limits.width(leaf_layout.size.width));
190 }
191 _ => (),
192 }
193
194 node.move_to_mut(Point {
195 x: leaf_layout.location.x,
196 y: leaf_layout.location.y,
197 })
198 }
199 }
200
201 let grid_size = Size {
202 width: grid_layout.content_size.width,
203 height: grid_layout.content_size.height,
204 };
205
206 Node::with_children(grid_size, nodes)
207}