diff --git a/crates/layout/src/lib.rs b/crates/layout/src/lib.rs index 19de84afb..33ad233ff 100644 --- a/crates/layout/src/lib.rs +++ b/crates/layout/src/lib.rs @@ -629,14 +629,26 @@ fn distribute(items: &mut [Item], mut open: Vec, free: f64) { .collect(); let violation: f64 = sized.iter().map(|(_, want, v)| v - want).sum(); let frozen = |want: f64, v: f64| violation.abs() < 1e-9 || (violation > 0.0 && v > want + 1e-9) || (violation < 0.0 && v < want - 1e-9); + let mut progressed = false; for (i, want, v) in &sized { if frozen(*want, *v) && let Some(it) = items.get_mut(*i) { it.main = *v; free -= *v; + progressed = true; } } + if !progressed { + // Several clampings each under the tolerance added up past it, so none froze: settle + // them all as clamped rather than go round again with nothing changed. + for (i, _, v) in &sized { + if let Some(it) = items.get_mut(*i) { + it.main = *v; + } + } + break; + } free = free.max(0.0); open = sized.into_iter().filter(|(_, want, v)| !frozen(*want, *v)).map(|(i, _, _)| i).collect(); } diff --git a/crates/layout/src/tests.rs b/crates/layout/src/tests.rs index 0af9861b8..d48c1fb8d 100644 --- a/crates/layout/src/tests.rs +++ b/crates/layout/src/tests.rs @@ -447,3 +447,24 @@ fn grid_hostile_values() { assert!(x.is_finite() && y.is_finite() && w.is_finite() && h.is_finite()); } } + +#[test] +fn fill_children_whose_tiny_clampings_add_up_past_the_tolerance_still_settle() { + // Three fill children sharing 300 px, each with a minimum 5e-10 over its 100 px share: no + // single clamping passes the 1e-9 tolerance, but together they do, so no child froze and the + // distribution loop went round forever without changing anything. + let mut d = Document::new("t"); + let p = d.first_page().unwrap(); + let f = d.create(p, NodeKind::Frame, "F", 0.0, 0.0, 300.0, 100.0).unwrap(); + d.get_mut(f).unwrap().layout = Some(AutoLayout { mode: LayoutMode::Horizontal, gap: 0.0, padding: [0.0; 4], ..Default::default() }); + let kids: Vec = (0..3).map(|i| d.create(f, NodeKind::Rectangle, "K", 0.0, 0.0, 10.0, 20.0 + i as f64).unwrap()).collect(); + for k in &kids { + let n = d.get_mut(*k).unwrap(); + n.child_layout.sizing_h = Sizing::Fill; + n.child_layout.min_w = Some(100.0 + 5e-10); + } + layout_frame(&mut d, f, &NoMeasure); + for k in &kids { + assert!((pos(&d, *k).2 - 100.0).abs() < 1e-6, "{:?}", pos(&d, *k)); + } +}