Skip to content

Commit

Permalink
TMP
Browse files Browse the repository at this point in the history
  • Loading branch information
PoignardAzur committed Dec 14, 2024
1 parent f672118 commit 3879e42
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 156 deletions.
111 changes: 24 additions & 87 deletions masonry/src/box_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
}

Expand All @@ -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),
};

Expand All @@ -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.
Expand All @@ -84,19 +54,14 @@ impl BoxConstraints {
///
/// [rounded away from zero]: Size::expand
pub fn constrain(&self, size: impl Into<Size>) -> Size {
size.into().expand().clamp(self.min, self.max)
size.into().expand().clamp(Size::ZERO, self.max)
}

/// Returns the max size of these constraints.
pub fn max(&self) -> Size {
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()
Expand All @@ -115,62 +80,38 @@ impl BoxConstraints {
return;
}

if self.min.width.is_nan() {
debug_panic!("Minimum width constraint passed to {name} is NaN");
}
if self.min.height.is_nan() {
debug_panic!("Minimum height constraint passed to {name} is NaN");
}
if self.max.width.is_nan() {
debug_panic!("Maximum width constraint passed to {name} is NaN");
}
if self.max.height.is_nan() {
debug_panic!("Maximum height constraint passed to {name} is NaN");
}

if self.min.width.is_infinite() {
debug_panic!("Infinite minimum width constraint passed to {name}");
}
if self.min.height.is_infinite() {
debug_panic!("Infinite minimum height constraint passed to {name}");
}

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) {
debug_panic!("Bad BoxConstraints passed to {name}: {self:?}",);
}
}

/// 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.
///
/// [rounded away from zero]: Size::expand
pub fn shrink(&self, diff: impl Into<Size>) -> 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<Size>) -> 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
Expand Down Expand Up @@ -203,9 +144,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
Expand All @@ -219,22 +160,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
Expand All @@ -251,11 +192,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
Expand All @@ -278,13 +219,11 @@ impl BoxConstraints {
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),
)
fn bc(_min_width: f64, _min_height: f64, max_width: f64, max_height: f64) -> BoxConstraints {
BoxConstraints::new(Size::new(max_width, max_height))
}

// TODO - Fix this test
#[test]
fn constrain_aspect_ratio() {
for (bc, aspect_ratio, width, output) in [
Expand Down Expand Up @@ -373,7 +312,5 @@ mod tests {
fn unbounded() {
assert!(!BoxConstraints::UNBOUNDED.is_width_bounded());
assert!(!BoxConstraints::UNBOUNDED.is_height_bounded());

assert_eq!(BoxConstraints::UNBOUNDED.min(), Size::ZERO);
}
}
2 changes: 1 addition & 1 deletion masonry/src/widget/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Widget for Align {
}

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);

Expand Down
2 changes: 1 addition & 1 deletion masonry/src/widget/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,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);

Expand Down
31 changes: 10 additions & 21 deletions masonry/src/widget/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,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,
}
}
Expand Down Expand Up @@ -701,21 +701,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)),
}
}
}
Expand Down Expand Up @@ -923,10 +912,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;
Expand Down Expand Up @@ -1021,7 +1010,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 {
Expand Down Expand Up @@ -1057,8 +1046,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());
Expand Down Expand Up @@ -1099,7 +1088,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.
Expand Down
2 changes: 1 addition & 1 deletion masonry/src/widget/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ impl Widget for Grid {
(child.width as f64 * width_unit - self.grid_spacing).max(0.0),
(child.height as f64 * height_unit - self.grid_spacing).max(0.0),
);
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,
Expand Down
2 changes: 1 addition & 1 deletion masonry/src/widget/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,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_zero_area() {
let size = bc.min();
let size = Size::ZERO;
return size;
}
let image_aspect_ratio = image_size.height / image_size.width;
Expand Down
3 changes: 1 addition & 2 deletions masonry/src/widget/portal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,9 @@ impl<W: Widget> Widget for Portal<W> {

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);
Expand Down
Loading

0 comments on commit 3879e42

Please sign in to comment.