diff --git a/core/src/widget/request.rs b/core/src/widget/request.rs index 764773e..b975731 100644 --- a/core/src/widget/request.rs +++ b/core/src/widget/request.rs @@ -40,6 +40,16 @@ enum Operand { Node(u32), } +impl Operand { + /// The length itself, where no comparison is waiting on an allocation. + fn linear(&self) -> Option { + match *self { + Self::Linear(len) => Some(len), + Self::Node(_) => None, + } + } +} + /// One sum or comparison, with its two operands. Whether anything under it /// divides leftover space is carried on the node rather than walked for, /// because every caller of one asks. @@ -63,14 +73,6 @@ impl Nodes { self.0[index as usize] } - /// The length itself, where no comparison is waiting on an allocation. - fn linear(&self, at: Operand) -> Option { - match at { - Operand::Linear(len) => Some(len), - Operand::Node(_) => None, - } - } - fn leftover(&self, at: Operand) -> bool { match at { Operand::Linear(len) => len.leftover > Weight::ZERO, @@ -82,7 +84,7 @@ impl Nodes { /// lengths that keep their order whatever the room comes to are already /// decided, and so are two operands that are the same thing. fn combine(&mut self, op: Op, a: Operand, b: Operand) -> Operand { - if let (Some(x), Some(y)) = (self.linear(a), self.linear(b)) { + if let (Some(x), Some(y)) = (a.linear(), b.linear()) { if matches!(op, Op::Sum) { return Operand::Linear(x + y); } @@ -150,7 +152,7 @@ impl Nodes { /// An expression is the same nodes the layout pass allocates, in an arena of /// its own: importing one copies those nodes into the pass's arena, so there /// is no second shape to keep in step and one place where folding is decided. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, PartialEq)] pub enum SizeRequest { Linear(LayoutLen), /// Behind a pointer, because a plain length is what nearly every rule @@ -237,6 +239,14 @@ impl std::fmt::Display for SizeRequest { } } +/// The same, since an arena printed as a struct is not a tree anyone can +/// write out again, which is what a request is printed for. +impl std::fmt::Debug for SizeRequest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + /// A discovered length. Deferred values belong to the current layout pass; /// widgets must not retain them. Ordinary requests remain inline lengths. #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/tests/cases/deferred.rs b/tests/cases/deferred.rs index faecdb1..eb1ff1f 100644 --- a/tests/cases/deferred.rs +++ b/tests/cases/deferred.rs @@ -132,6 +132,27 @@ fn a_deferred_comparison_can_compare_two_different_weights() { assert_corners!(h, b, (200, 0), (300, 100)); } +#[test] +fn a_comparison_between_two_comparisons_keeps_both_of_them() { + // Joining two expressions is the one path that copies a request's nodes + // into another's arena; the floor puts a node under the copied one, so + // its operands have to be renumbered as they land. + let capped = leftover(1).min(px(40)); + let floored = leftover(2).min(px(70)).max(px(10)); + let mut h = Harness::new((90, 100)); + let both = rect(Color::RED).width(capped.max(floored)).add(&mut h.rsc); + let rest = rect(Color::BLUE).add(&mut h.rsc); + h.set_root((both, rest).span(Dir::RIGHT)); + // Under either cap, so the doubled share is the longer of the two. + assert_corners!(h, both, (0, 0), (60, 100)); + assert_corners!(h, rest, (60, 0), (90, 100)); + h.resize((300, 100)); + h.frame(); + // Over both caps, so the comparison is between 40 and 70. + assert_corners!(h, both, (0, 0), (70, 100)); + assert_corners!(h, rest, (70, 0), (300, 100)); +} + #[test] fn a_length_expression_is_resolved_before_wrapping_text() { let mut h = Harness::new((300, 500)); diff --git a/tests/deferred_generated.rs b/tests/deferred_generated.rs index a079fd3..8d3a93a 100644 --- a/tests/deferred_generated.rs +++ b/tests/deferred_generated.rs @@ -38,6 +38,9 @@ fn deferred_requests_agree_warm_and_cold() { 0 => leftover(1).clamp(20, 120).into(), 1 => leftover(1).min(rel(0.5)).into(), 2 => (leftover(1) + px(30)).min(leftover(2)).into(), + // Both sides an expression, the one shape that + // copies a request's nodes into another's. + 3 => leftover(1).min(px(40)).max(leftover(2).min(px(70))).into(), _ => rules[axis].clone(), }; } diff --git a/tests/scenario/mod.rs b/tests/scenario/mod.rs index b6f1e87..aed34c6 100644 --- a/tests/scenario/mod.rs +++ b/tests/scenario/mod.rs @@ -357,7 +357,7 @@ fn describe(id: WidgetId, h: &Harness) -> String { let mut out = r .request .as_ref() - .map_or_else(String::new, |r| format!("{r}")); + .map_or_else(String::new, ToString::to_string); if let Some(min) = r.bound.min { out += &format!(">{}", LayoutLen::from(min)); }