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