1use crate::Element;
20
21use crate::layout::{Limits, Node};
22use crate::length;
23use crate::widget;
24use crate::{Alignment, Length, Padding, Size};
25
26#[derive(Debug)]
28pub enum Axis {
29 Horizontal,
31
32 Vertical,
34}
35
36impl Axis {
37 fn main(&self, size: Size) -> f32 {
38 match self {
39 Axis::Horizontal => size.width,
40 Axis::Vertical => size.height,
41 }
42 }
43
44 fn cross(&self, size: Size) -> f32 {
45 match self {
46 Axis::Horizontal => size.height,
47 Axis::Vertical => size.width,
48 }
49 }
50
51 fn pack<T>(&self, main: T, cross: T) -> (T, T) {
52 match self {
53 Axis::Horizontal => (main, cross),
54 Axis::Vertical => (cross, main),
55 }
56 }
57}
58
59pub fn resolve<Message, Theme, Renderer>(
64 axis: Axis,
65 renderer: &Renderer,
66 limits: &Limits,
67 width: Length,
68 height: Length,
69 padding: Padding,
70 spacing: f32,
71 align_items: Alignment,
72 items: &mut [Element<'_, Message, Theme, Renderer>],
73 trees: &mut [widget::Tree],
74) -> Node
75where
76 Renderer: crate::Renderer,
77{
78 let limits = limits.width(width).height(height).shrink(padding);
79 let total_spacing = spacing * items.len().saturating_sub(1) as f32;
80 let max_cross = axis.cross(limits.max());
81
82 let (main_compress, cross_compress) = {
83 let compression = limits.compression();
84 axis.pack(compression.width, compression.height)
85 };
86
87 let compression = {
88 let (compress_x, compress_y) = axis.pack(main_compress, false);
89 Size::new(compress_x, compress_y)
90 };
91
92 let mut fill_main_sum = 0;
93 let mut some_fill_cross = false;
94 let mut some_fill_max = false;
95 let mut some_fill_min = false;
96 let mut min_total = 0.0;
97 let mut min_factors = 0;
98 let mut cross = 0.0;
99 let mut available = axis.main(limits.max()) - total_spacing;
100
101 let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
102 nodes.resize(items.len(), Node::default());
103
104 #[derive(Debug, Clone, Copy)]
105 struct Meta {
106 main: Length,
107 cross: Length,
108 category: Category,
109 resolved: bool,
110 }
111
112 #[derive(Debug, Clone, Copy)]
113 enum Category {
114 Static,
115 CrossFluid,
116 CrossFluidDeferred(f32),
117 MainFluid,
118 }
119
120 let mut metas = Vec::with_capacity(items.len());
121
122 for (i, child) in items.iter_mut().enumerate() {
127 let size = child.as_widget().size();
128 let (size_main, size_cross) = axis.pack(size.width, size.height);
129
130 let fill_main_factor = size_main.fill_factor();
131 let fill_cross_factor = size_cross.fill_factor();
132 let main_is_static = main_compress || fill_main_factor == 0;
133
134 let category = match (main_is_static, cross_compress, fill_cross_factor == 0) {
135 (true, false, _) | (true, _, true) => Category::Static,
136 (true, true, false) => {
137 if let Length::Fixed(main) = size_main {
138 available -= main;
139 Category::CrossFluidDeferred(main)
140 } else {
141 Category::CrossFluid
142 }
143 }
144 (false, _, _) => Category::MainFluid,
145 };
146
147 let meta = Meta {
148 main: size_main,
149 cross: size_cross,
150 resolved: false,
151 category,
152 };
153
154 metas.push(meta);
155
156 match meta.main {
157 Length::Bounded {
158 sizing: length::Sizing::Fill(_),
159 bounds: length::Bounds::Min(min),
160 }
161 | Length::Fluid(length::Constraint::Min(min)) => {
162 min_total += min;
163 min_factors += fill_main_factor;
164 some_fill_min = true;
165 }
166 Length::Bounded {
167 sizing: length::Sizing::Fill(_),
168 bounds: length::Bounds::Max(_),
169 }
170 | Length::Fluid(length::Constraint::Max) => {
171 some_fill_max = true;
172 }
173 Length::Bounded {
174 sizing: length::Sizing::Fill(_),
175 bounds: length::Bounds::Both { .. },
176 } => {
177 some_fill_max = true;
178 some_fill_min = true;
179 }
180 _ => {}
181 }
182
183 let Category::Static = meta.category else {
184 fill_main_sum += fill_main_factor;
185 some_fill_cross = some_fill_cross || fill_cross_factor != 0;
186 continue;
187 };
188
189 let (max_width, max_height) = axis.pack(
190 available,
191 if !cross_compress || fill_cross_factor == 0 {
192 max_cross
193 } else {
194 cross
195 },
196 );
197
198 let child_limits =
199 Limits::with_compression(Size::ZERO, Size::new(max_width, max_height), compression);
200
201 let layout = child
202 .as_widget_mut()
203 .layout(&mut trees[i], renderer, &child_limits);
204
205 let size = layout.size();
206
207 available -= axis.main(size);
208 cross = cross.max(axis.cross(size));
209 nodes[i] = layout;
210 }
211
212 if cross_compress && some_fill_cross {
223 for (i, child) in items.iter_mut().enumerate() {
224 let meta = metas[i];
225
226 let Category::CrossFluid = meta.category else {
227 continue;
228 };
229
230 let (max_width, max_height) =
231 axis.pack(available, if cross_compress { cross } else { max_cross });
232
233 let child_limits =
234 Limits::with_compression(Size::ZERO, Size::new(max_width, max_height), compression);
235
236 let layout = child
237 .as_widget_mut()
238 .layout(&mut trees[i], renderer, &child_limits);
239
240 let size = layout.size();
241
242 available -= axis.main(size);
243 cross = cross.max(axis.cross(size));
244 nodes[i] = layout;
245 }
246 }
247
248 let mut remaining = available.max(0.0);
249
250 #[derive(Debug, Clone, Copy)]
259 enum Stage {
260 Max,
261 Min,
262 }
263
264 let mut step = if main_compress {
265 None
266 } else if some_fill_max {
267 Some(Stage::Max)
268 } else if some_fill_min {
269 Some(Stage::Min)
270 } else {
271 None
272 };
273
274 while let Some(stage) = step {
275 let current = remaining;
276 let (reserved_space, reserved_factors) = match stage {
277 Stage::Max => (min_total, min_factors),
278 Stage::Min => (0.0, 0),
279 };
280
281 for (i, child) in items.iter_mut().enumerate() {
282 let meta = &mut metas[i];
283
284 if meta.resolved {
285 continue;
286 }
287
288 let fill_main_factor = meta.main.fill_factor();
289
290 if fill_main_factor == 0 {
291 continue;
292 }
293
294 let bounds = match stage {
295 Stage::Max => match meta.main {
296 Length::Bounded {
297 bounds: bounds @ (length::Bounds::Max(_) | length::Bounds::Both { .. }),
298 ..
299 } => bounds,
300 Length::Fluid(length::Constraint::Max) => length::Bounds::Min(0.0),
301 _ => continue,
302 },
303 Stage::Min => match meta.main {
304 Length::Bounded {
305 bounds: bounds @ (length::Bounds::Min(_) | length::Bounds::Both { .. }),
306 ..
307 } => bounds,
308 Length::Fluid(length::Constraint::Min(min)) => length::Bounds::Min(min),
309 _ => continue,
310 },
311 };
312
313 let max_available = (remaining - reserved_space) * fill_main_factor as f32
314 / (fill_main_sum - reserved_factors) as f32;
315
316 let max_available = if max_available.is_nan() {
317 f32::INFINITY
318 } else {
319 max_available
320 };
321
322 let (min, max) = match bounds {
323 length::Bounds::Max(max) => (0.0, max),
324 length::Bounds::Both { min, max } => (min, max),
325 length::Bounds::Min(min) => (min, max_available),
326 };
327
328 match stage {
329 Stage::Max if max > max_available => continue,
330 Stage::Min if min < max_available => continue,
331 _ => {}
332 }
333
334 let min = min.min(remaining);
335 let max = max.min(max_available).max(min);
336
337 let (min_width, min_height) = axis.pack(min, 0.0);
338 let (max_width, max_height) = axis.pack(
339 max,
340 if !cross_compress || meta.cross.fill_factor() == 0 {
341 max_cross
342 } else {
343 cross
344 },
345 );
346
347 let child_limits = Limits::with_compression(
348 Size::new(min_width, min_height),
349 Size::new(max_width, max_height),
350 compression,
351 );
352
353 let layout = child
354 .as_widget_mut()
355 .layout(&mut trees[i], renderer, &child_limits);
356
357 cross = cross.max(axis.cross(layout.size()));
358 remaining -= axis.main(layout.size());
359 fill_main_sum -= fill_main_factor;
360 nodes[i] = layout;
361 meta.resolved = true;
362 }
363
364 if remaining == current {
365 step = match stage {
366 Stage::Max if some_fill_min => Some(Stage::Min),
367 _ => None,
368 };
369 }
370 }
371
372 if !main_compress {
376 for (i, child) in items.iter_mut().enumerate() {
377 let meta = &mut metas[i];
378
379 if meta.resolved {
380 continue;
381 }
382
383 let Category::MainFluid = meta.category else {
384 continue;
385 };
386
387 let max_main = remaining * meta.main.fill_factor() as f32 / fill_main_sum as f32;
388
389 let max_main = if max_main.is_nan() {
390 f32::INFINITY
391 } else {
392 max_main
393 };
394
395 let min_main = if max_main.is_infinite() {
396 0.0
397 } else {
398 max_main
399 };
400
401 let (min_width, min_height) = axis.pack(min_main, 0.0);
402 let (max_width, max_height) = axis.pack(
403 max_main,
404 if !cross_compress || meta.cross.fill_factor() == 0 {
405 max_cross
406 } else {
407 cross
408 },
409 );
410
411 let child_limits = Limits::with_compression(
412 Size::new(min_width, min_height),
413 Size::new(max_width, max_height),
414 compression,
415 );
416
417 let layout = child
418 .as_widget_mut()
419 .layout(&mut trees[i], renderer, &child_limits);
420
421 cross = cross.max(axis.cross(layout.size()));
422 nodes[i] = layout;
423 }
424 }
425
426 if cross_compress && some_fill_cross {
431 for (i, child) in items.iter_mut().enumerate() {
432 let meta = metas[i];
433
434 let Category::CrossFluidDeferred(main) = meta.category else {
435 continue;
436 };
437
438 let (max_width, max_height) = axis.pack(main, cross);
439 let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height));
440
441 let layout = child
442 .as_widget_mut()
443 .layout(&mut trees[i], renderer, &child_limits);
444
445 let size = layout.size();
446
447 cross = cross.max(axis.cross(size));
448 nodes[i] = layout;
449 }
450 }
451
452 let pad = axis.pack(padding.left, padding.top);
453 let mut main = pad.0;
454
455 let cross = match axis {
456 Axis::Horizontal => limits.resolve_height(height, cross),
457 Axis::Vertical => limits.resolve_width(width, cross),
458 };
459
460 for (i, node) in nodes.iter_mut().enumerate() {
463 if i > 0 {
464 main += spacing;
465 }
466
467 node.move_to_mut(axis.pack(main, pad.1));
468
469 match axis {
470 Axis::Horizontal => {
471 node.align_mut(Alignment::Start, align_items, Size::new(0.0, cross));
472 }
473 Axis::Vertical => {
474 node.align_mut(align_items, Alignment::Start, Size::new(cross, 0.0));
475 }
476 }
477
478 main += axis.main(node.size());
479 }
480
481 let main = match axis {
482 Axis::Horizontal => limits.resolve_width(width, main - pad.0),
483 Axis::Vertical => limits.resolve_height(height, main - pad.0),
484 };
485
486 let size = Size::from(axis.pack(main, cross));
487
488 Node::with_children(size.expand(padding), nodes)
489}