diff --git a/masonry/src/box_constraints.rs b/masonry/src/box_constraints.rs index a550ebd3a..8d60d503c 100644 --- a/masonry/src/box_constraints.rs +++ b/masonry/src/box_constraints.rs @@ -8,9 +8,10 @@ use vello::kurbo::Size; /// The layout strategy for Masonry is strongly inspired by Flutter, /// and this struct is similar to the [Flutter BoxConstraints] class. /// -/// At the moment, it represents simply a minimum and maximum size. +/// At the moment, it represents only a maximum size. /// A widget's [`layout`] method should choose an appropriate size that -/// meets these constraints. +/// meets that constraint. +/// The algorithm is supposed to use a minimum constraint, but we're phasing that out. /// /// Further, a container widget should compute appropriate constraints /// for each of its child widgets, and pass those down when recursing. @@ -23,7 +24,6 @@ use vello::kurbo::Size; /// [rounded away from zero]: Size::expand #[derive(Clone, Copy, Debug, PartialEq)] pub struct BoxConstraints { - min: Size, max: Size, } @@ -32,7 +32,6 @@ impl BoxConstraints { /// /// Can be satisfied by any nonnegative size. pub const UNBOUNDED: BoxConstraints = BoxConstraints { - min: Size::ZERO, max: Size::new(f64::INFINITY, f64::INFINITY), }; @@ -44,37 +43,8 @@ impl BoxConstraints { /// so that the layout is aligned to integers. /// /// [rounded away from zero]: Size::expand - pub fn new(min: Size, max: Size) -> BoxConstraints { - BoxConstraints { - min: min.expand(), - max: max.expand(), - } - } - - /// Create a "tight" box constraints object. - /// - /// A "tight" constraint can only be satisfied by a single size. - /// - /// The given size is also [rounded away from zero], - /// so that the layout is aligned to integers. - /// - /// [rounded away from zero]: Size::expand - pub fn tight(size: Size) -> BoxConstraints { - let size = size.expand(); - BoxConstraints { - min: size, - max: size, - } - } - - /// Create a "loose" version of the constraints. - /// - /// Make a version with zero minimum size, but the same maximum size. - pub fn loosen(&self) -> BoxConstraints { - BoxConstraints { - min: Size::ZERO, - max: self.max, - } + pub fn new(max: Size) -> BoxConstraints { + BoxConstraints { max: max.expand() } } /// Clamp a given size so that it fits within the constraints. @@ -84,7 +54,7 @@ impl BoxConstraints { /// /// [rounded away from zero]: Size::expand pub fn constrain(&self, size: impl Into) -> Size { - size.into().expand().clamp(self.min, self.max) + size.into().expand().clamp(Size::ZERO, self.max) } /// Returns the max size of these constraints. @@ -92,11 +62,6 @@ impl BoxConstraints { self.max } - /// Returns the min size of these constraints. - pub fn min(&self) -> Size { - self.min - } - /// Whether there is an upper bound on the width. pub fn is_width_bounded(&self) -> bool { self.max.width.is_finite() @@ -115,27 +80,13 @@ impl BoxConstraints { return; } - if !(0.0 <= self.min.width - && self.min.width <= self.max.width - && 0.0 <= self.min.height - && self.min.height <= self.max.height - && self.min.expand() == self.min - && self.max.expand() == self.max) - { + if !(0.0 <= self.max.width && 0.0 <= self.max.height && self.max.expand() == self.max) { tracing::warn!("Bad BoxConstraints passed to {}:", name); tracing::warn!("{:?}", self); } - - if self.min.width.is_infinite() { - tracing::warn!("Infinite minimum width constraint passed to {}:", name); - } - - if self.min.height.is_infinite() { - tracing::warn!("Infinite minimum height constraint passed to {}:", name); - } } - /// Shrink min and max constraints by size + /// Shrink max constraint by size /// /// The given size is also [rounded away from zero], /// so that the layout is aligned to integers. @@ -143,23 +94,18 @@ impl BoxConstraints { /// [rounded away from zero]: Size::expand pub fn shrink(&self, diff: impl Into) -> BoxConstraints { let diff = diff.into().expand(); - let min = Size::new( - (self.min().width - diff.width).max(0.), - (self.min().height - diff.height).max(0.), - ); let max = Size::new( (self.max().width - diff.width).max(0.), (self.max().height - diff.height).max(0.), ); - BoxConstraints::new(min, max) + BoxConstraints::new(max) } /// Test whether these constraints contain the given `Size`. pub fn contains(&self, size: impl Into) -> bool { let size = size.into(); - (self.min.width <= size.width && size.width <= self.max.width) - && (self.min.height <= size.height && size.height <= self.max.height) + (size.width <= self.max.width) && (size.height <= self.max.height) } /// Find the `Size` within these `BoxConstraint`s that minimises the difference between the @@ -192,9 +138,9 @@ impl BoxConstraints { // Then we check if any `Size`s with our desired aspect ratio are inside the constraints. // TODO this currently outputs garbage when things are < 0 - See https://github.com/linebender/xilem/issues/377 - let min_w_min_h = self.min.height / self.min.width; - let max_w_min_h = self.min.height / self.max.width; - let min_w_max_h = self.max.height / self.min.width; + let min_w_min_h = 0.0 / 0.0; + let max_w_min_h = 0.0 / self.max.width; + let min_w_max_h = self.max.height / 0.0; let max_w_max_h = self.max.height / self.max.width; // When the aspect ratio line crosses the constraints, the closest point must be one of the @@ -208,22 +154,22 @@ impl BoxConstraints { if aspect_ratio > min_w_max_h { // outside max height min width Size { - width: self.min.width, + width: 0.0, height: self.max.height, } } else if aspect_ratio < max_w_min_h { // outside min height max width Size { width: self.max.width, - height: self.min.height, + height: 0.0, } } else if aspect_ratio > min_w_min_h { // hits the constraints on the min width line - if width < self.min.width { + if width < 0.0 { // we take the point on the min width Size { - width: self.min.width, - height: self.min.width * aspect_ratio, + width: 0.0, + height: 0.0 * aspect_ratio, } } else if aspect_ratio < max_w_max_h { // exits through max.width @@ -240,11 +186,11 @@ impl BoxConstraints { } } else { // final case is where we hit constraints on the min height line - if width < self.min.width { + if width < 0.0 { // take the point on the min height Size { - width: self.min.height * aspect_ratio.recip(), - height: self.min.height, + width: 0.0 * aspect_ratio.recip(), + height: 0.0, } } else if aspect_ratio > max_w_max_h { // exit thru max height @@ -268,10 +214,7 @@ mod tests { use super::*; fn bc(min_width: f64, min_height: f64, max_width: f64, max_height: f64) -> BoxConstraints { - BoxConstraints::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ) + BoxConstraints::new(Size::new(max_width, max_height)) } #[test] @@ -362,7 +305,5 @@ mod tests { fn unbounded() { assert!(!BoxConstraints::UNBOUNDED.is_width_bounded()); assert!(!BoxConstraints::UNBOUNDED.is_height_bounded()); - - assert_eq!(BoxConstraints::UNBOUNDED.min(), Size::ZERO); } } diff --git a/masonry/src/render_root.rs b/masonry/src/render_root.rs index 6ecffa6d9..4dc3a929d 100644 --- a/masonry/src/render_root.rs +++ b/masonry/src/render_root.rs @@ -455,7 +455,7 @@ impl RenderRoot { pub(crate) fn root_layout(&mut self) { let window_size = self.get_kurbo_size(); let bc = match self.size_policy { - WindowSizePolicy::User => BoxConstraints::tight(window_size), + WindowSizePolicy::User => BoxConstraints::new(window_size), WindowSizePolicy::Content => BoxConstraints::UNBOUNDED, }; diff --git a/masonry/src/widget/align.rs b/masonry/src/widget/align.rs index f93e38ff1..ddde00272 100644 --- a/masonry/src/widget/align.rs +++ b/masonry/src/widget/align.rs @@ -98,7 +98,7 @@ impl Widget for Align { fn on_status_change(&mut self, _ctx: &mut LifeCycleCtx, _event: &StatusChange) {} fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size { - let size = ctx.run_layout(&mut self.child, &bc.loosen()); + let size = ctx.run_layout(&mut self.child, &bc); log_size_warnings(size); diff --git a/masonry/src/widget/button.rs b/masonry/src/widget/button.rs index 1abb58382..b97c6d534 100644 --- a/masonry/src/widget/button.rs +++ b/masonry/src/widget/button.rs @@ -121,7 +121,7 @@ impl Widget for Button { fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size { let padding = Size::new(LABEL_INSETS.x_value(), LABEL_INSETS.y_value()); - let label_bc = bc.shrink(padding).loosen(); + let label_bc = bc.shrink(padding); let label_size = ctx.run_layout(&mut self.label, &label_bc); diff --git a/masonry/src/widget/flex.rs b/masonry/src/widget/flex.rs index 1c43a33a5..0d40645fb 100644 --- a/masonry/src/widget/flex.rs +++ b/masonry/src/widget/flex.rs @@ -127,7 +127,7 @@ impl Flex { cross_alignment: CrossAxisAlignment::Center, main_alignment: MainAxisAlignment::Start, fill_major_axis: false, - old_bc: BoxConstraints::tight(Size::ZERO), + old_bc: BoxConstraints::new(Size::ZERO), gap: None, } } @@ -687,21 +687,10 @@ impl Axis { } /// Generate constraints with new values on the major axis. - pub(crate) fn constraints( - self, - bc: &BoxConstraints, - min_major: f64, - major: f64, - ) -> BoxConstraints { + pub(crate) fn constraints(self, bc: &BoxConstraints, major: f64) -> BoxConstraints { match self { - Axis::Horizontal => BoxConstraints::new( - Size::new(min_major, bc.min().height), - Size::new(major, bc.max().height), - ), - Axis::Vertical => BoxConstraints::new( - Size::new(bc.min().width, min_major), - Size::new(bc.max().width, major), - ), + Axis::Horizontal => BoxConstraints::new(Size::new(major, bc.max().height)), + Axis::Vertical => BoxConstraints::new(Size::new(bc.max().width, major)), } } } @@ -911,10 +900,10 @@ impl Widget for Flex { fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size { // we loosen our constraints when passing to children. - let loosened_bc = bc.loosen(); + let loosened_bc = bc; // minor-axis values for all children - let mut minor = self.direction.minor(bc.min()); + let mut minor = 0_f64; // these two are calculated but only used if we're baseline aligned let mut max_above_baseline = 0_f64; let mut max_below_baseline = 0_f64; @@ -1007,7 +996,7 @@ impl Widget for Flex { remainder = desired_major - actual_major; let old_size = ctx.widget_state.layout_rect().size(); - let child_bc = self.direction.constraints(&loosened_bc, 0.0, actual_major); + let child_bc = self.direction.constraints(&loosened_bc, actual_major); let child_size = ctx.run_layout(widget, &child_bc); if old_size != child_size { @@ -1043,8 +1032,8 @@ impl Widget for Flex { (remaining - major_flex).max(0.0) } else { // if we are *not* expected to fill our available space this usually - // means we don't have any extra, unless dictated by our constraints. - (self.direction.major(bc.min()) - (major_non_flex + major_flex)).max(0.0) + // means we don't have any extra + 0.0 }; let mut spacing = Spacing::new(self.main_alignment, extra, self.children.len()); @@ -1085,7 +1074,7 @@ impl Widget for Flex { .pack(self.direction.major(child_size), minor_dim) .into(); if ctx.widget_state.layout_rect().size() != fill_size { - let child_bc = BoxConstraints::tight(fill_size); + let child_bc = BoxConstraints::new(fill_size); //TODO: this is the second call of layout on the same child, which // is bad, because it can lead to exponential increase in layout calls // when used multiple times in the widget hierarchy. diff --git a/masonry/src/widget/grid.rs b/masonry/src/widget/grid.rs index bf617f00e..9281bcb0e 100644 --- a/masonry/src/widget/grid.rs +++ b/masonry/src/widget/grid.rs @@ -255,7 +255,7 @@ impl Widget for Grid { child.width as f64 * width_unit - self.grid_spacing, child.height as f64 * height_unit - self.grid_spacing, ); - let child_bc = BoxConstraints::new(cell_size, cell_size); + let child_bc = BoxConstraints::new(cell_size); let _ = ctx.run_layout(&mut child.widget, &child_bc); ctx.place_child( &mut child.widget, diff --git a/masonry/src/widget/image.rs b/masonry/src/widget/image.rs index a96754c95..ca0cec646 100644 --- a/masonry/src/widget/image.rs +++ b/masonry/src/widget/image.rs @@ -89,7 +89,7 @@ impl Widget for Image { // the image. let image_size = Size::new(self.image_data.width as f64, self.image_data.height as f64); if image_size.is_empty() { - let size = bc.min(); + let size = Size::ZERO; trace!("Computed size: {}", size); return size; } diff --git a/masonry/src/widget/label.rs b/masonry/src/widget/label.rs index 400d2c985..eb8d9d9eb 100644 --- a/masonry/src/widget/label.rs +++ b/masonry/src/widget/label.rs @@ -215,8 +215,6 @@ impl Widget for Label { None } else if bc.max().width.is_finite() { Some(bc.max().width as f32 - 2. * LABEL_X_PADDING as f32) - } else if bc.min().width.is_sign_negative() { - Some(0.0) } else { None }; diff --git a/masonry/src/widget/portal.rs b/masonry/src/widget/portal.rs index c4442406e..3a010245d 100644 --- a/masonry/src/widget/portal.rs +++ b/masonry/src/widget/portal.rs @@ -371,10 +371,9 @@ impl Widget for Portal { fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size { // TODO - How Portal handles BoxConstraints is due for a rework - let min_child_size = if self.must_fill { bc.min() } else { Size::ZERO }; let max_child_size = bc.max(); - let child_bc = BoxConstraints::new(min_child_size, max_child_size); + let child_bc = BoxConstraints::new(max_child_size); let content_size = ctx.run_layout(&mut self.child, &child_bc); let portal_size = bc.constrain(content_size); diff --git a/masonry/src/widget/prose.rs b/masonry/src/widget/prose.rs index 3215d4d44..d334b5648 100644 --- a/masonry/src/widget/prose.rs +++ b/masonry/src/widget/prose.rs @@ -239,8 +239,6 @@ impl Widget for Prose { } else if bc.max().width.is_finite() { // TODO: Does Prose have different needs here? Some(bc.max().width as f32 - 2. * LABEL_X_PADDING as f32) - } else if bc.min().width.is_sign_negative() { - Some(0.0) } else { None }; diff --git a/masonry/src/widget/sized_box.rs b/masonry/src/widget/sized_box.rs index a144a6ccd..eeea1bab1 100644 --- a/masonry/src/widget/sized_box.rs +++ b/masonry/src/widget/sized_box.rs @@ -274,26 +274,17 @@ impl SizedBox { fn child_constraints(&self, bc: &BoxConstraints) -> BoxConstraints { // if we don't have a width/height, we don't change that axis. // if we have a width/height, we clamp it on that axis. - let (min_width, max_width) = match self.width { - Some(width) => { - let w = width.max(bc.min().width).min(bc.max().width); - (w, w) - } - None => (bc.min().width, bc.max().width), + let max_width = match self.width { + Some(width) => width.min(bc.max().width), + None => bc.max().width, }; - let (min_height, max_height) = match self.height { - Some(height) => { - let h = height.max(bc.min().height).min(bc.max().height); - (h, h) - } - None => (bc.min().height, bc.max().height), + let max_height = match self.height { + Some(height) => height.min(bc.max().height), + None => bc.max().height, }; - BoxConstraints::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ) + BoxConstraints::new(Size::new(max_width, max_height)) } #[allow(dead_code)] @@ -417,20 +408,11 @@ mod tests { use crate::testing::TestHarness; use crate::widget::Label; - #[test] - fn expand() { - let expand = SizedBox::new(Label::new("hello!")).expand(); - let bc = BoxConstraints::tight(Size::new(400., 400.)).loosen(); - let child_bc = expand.child_constraints(&bc); - assert_eq!(child_bc.min(), Size::new(400., 400.,)); - } - #[test] fn no_width() { let expand = SizedBox::new(Label::new("hello!")).height(200.); - let bc = BoxConstraints::tight(Size::new(400., 400.)).loosen(); + let bc = BoxConstraints::new(Size::new(400., 400.)); let child_bc = expand.child_constraints(&bc); - assert_eq!(child_bc.min(), Size::new(0., 200.,)); assert_eq!(child_bc.max(), Size::new(400., 200.,)); } diff --git a/masonry/src/widget/split.rs b/masonry/src/widget/split.rs index 20a803f86..08a3fa6ee 100644 --- a/masonry/src/widget/split.rs +++ b/masonry/src/widget/split.rs @@ -490,14 +490,8 @@ impl Widget for Split { .max(0.0); let child2_width = (reduced_size.width - child1_width).max(0.0); ( - BoxConstraints::new( - Size::new(child1_width, bc.min().height), - Size::new(child1_width, bc.max().height), - ), - BoxConstraints::new( - Size::new(child2_width, bc.min().height), - Size::new(child2_width, bc.max().height), - ), + BoxConstraints::new(Size::new(child1_width, bc.max().height)), + BoxConstraints::new(Size::new(child2_width, bc.max().height)), ) } Axis::Vertical => { @@ -506,14 +500,8 @@ impl Widget for Split { .max(0.0); let child2_height = (reduced_size.height - child1_height).max(0.0); ( - BoxConstraints::new( - Size::new(bc.min().width, child1_height), - Size::new(bc.max().width, child1_height), - ), - BoxConstraints::new( - Size::new(bc.min().width, child2_height), - Size::new(bc.max().width, child2_height), - ), + BoxConstraints::new(Size::new(bc.max().width, child1_height)), + BoxConstraints::new(Size::new(bc.max().width, child2_height)), ) } }; diff --git a/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__app_creation.snap.new b/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__app_creation.snap.new new file mode 100644 index 000000000..d9db3f770 --- /dev/null +++ b/masonry/src/widget/tests/snapshots/masonry__widget__tests__lifecycle_basic__app_creation.snap.new @@ -0,0 +1,18 @@ +--- +source: masonry/src/widget/tests/lifecycle_basic.rs +assertion_line: 22 +expression: record +--- +[ + RegisterChildren, + L( + WidgetAdded, + ), + L( + BuildFocusChain, + ), + Layout( + 0.0W×0.0H, + ), + Compose, +] diff --git a/masonry/src/widget/textbox.rs b/masonry/src/widget/textbox.rs index b2c8e1a91..bad559f4f 100644 --- a/masonry/src/widget/textbox.rs +++ b/masonry/src/widget/textbox.rs @@ -271,8 +271,6 @@ impl Widget for Textbox { None } else if bc.max().width.is_finite() { Some((bc.max().width - 2. * TEXTBOX_PADDING - 2. * TEXTBOX_MARGIN) as f32) - } else if bc.min().width.is_sign_negative() { - Some(0.0) } else { None }; diff --git a/masonry/src/widget/variable_label.rs b/masonry/src/widget/variable_label.rs index 43db984c0..bdaa8d29c 100644 --- a/masonry/src/widget/variable_label.rs +++ b/masonry/src/widget/variable_label.rs @@ -346,8 +346,6 @@ impl Widget for VariableLabel { None } else if bc.max().width.is_finite() { Some(bc.max().width as f32 - 2. * LABEL_X_PADDING as f32) - } else if bc.min().width.is_sign_negative() { - Some(0.0) } else { None };