diff --git a/src/layout/flex.rs b/src/layout/flex.rs index bae700ced..8142461f5 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -25,8 +25,7 @@ pub struct FlexLayouter { merged_actions: LayoutActionList, merged_dimensions: Size2D, - max_left: Size, - max_right: Size, + max_extent: Size, usable: Size, run: FlexRun, @@ -59,7 +58,7 @@ enum FlexUnit { #[derive(Debug, Clone)] struct FlexRun { - content: Vec<(Size, Size, Layout)>, + content: Vec<(Size, Layout)>, size: Size2D, } @@ -80,8 +79,7 @@ impl FlexLayouter { merged_actions: LayoutActionList::new(), merged_dimensions: Size2D::with_x(usable), - max_left: Size::zero(), - max_right: usable, + max_extent: Size::zero(), usable, run: FlexRun { content: vec![], size: Size2D::zero() }, @@ -179,6 +177,7 @@ impl FlexLayouter { if new_run_size > self.usable { self.space = None; + self.finish_run()?; while size.x > self.usable { if self.stack.in_last_space() { @@ -188,15 +187,12 @@ impl FlexLayouter { self.stack.finish_layout(true); self.usable = self.stack.usable().x; } - - self.finish_run()?; } self.layout_space(); let offset = self.run.size.x; - let anchor = self.ctx.axes.primary.anchor(size.x); - self.run.content.push((offset, anchor, boxed)); + self.run.content.push((offset, boxed)); self.run.size.x += size.x; self.run.size.y = crate::size::max(self.run.size.y, size.y); @@ -215,22 +211,35 @@ impl FlexLayouter { fn layout_set_axes(&mut self, axes: LayoutAxes) { if axes.primary != self.ctx.axes.primary { self.finish_aligned_run(); + self.usable = match axes.primary.alignment { - Alignment::Origin => self.max_right, - Alignment::Center => self.max_right - self.max_left, - Alignment::End => self.merged_dimensions.x - self.max_left, + Alignment::Origin => + if self.max_extent == Size::zero() { self.usable } else { Size::zero() }, + Alignment::Center => crate::size::max( + self.merged_dimensions.x - 2 * self.max_extent, + Size::zero() + ), + Alignment::End => self.merged_dimensions.x - self.max_extent, }; } if axes.secondary != self.ctx.axes.secondary { self.stack.set_axes(axes); } + + self.ctx.axes = axes; } /// Finish the current flex run. fn finish_run(&mut self) -> LayoutResult<()> { self.finish_aligned_run(); + if self.merged_dimensions.y == Size::zero() { + return Ok(()); + } + + self.merged_dimensions.y += self.ctx.flex_spacing; + let actions = std::mem::replace(&mut self.merged_actions, LayoutActionList::new()); self.stack.add(Layout { dimensions: self.ctx.axes.specialize(self.merged_dimensions), @@ -239,19 +248,25 @@ impl FlexLayouter { })?; self.merged_dimensions.y = Size::zero(); - self.max_left = Size::zero(); - self.max_right = self.merged_dimensions.x; + self.max_extent = Size::zero(); self.usable = self.merged_dimensions.x; Ok(()) } fn finish_aligned_run(&mut self) { - let anchor = self.ctx.axes.primary.anchor(self.merged_dimensions.x); - let factor = if self.ctx.axes.primary.axis.is_positive() { 1 } else { -1 }; + if self.run.content.is_empty() { + return; + } - for (offset, layout_anchor, layout) in self.run.content.drain(..) { - let general_position = Size2D::with_x(anchor - layout_anchor + factor * offset); + let factor = if self.ctx.axes.primary.axis.is_positive() { 1 } else { -1 }; + let anchor = self.ctx.axes.primary.anchor(self.merged_dimensions.x) + - self.ctx.axes.primary.anchor(self.run.size.x); + + self.max_extent = crate::size::max(self.max_extent, anchor + factor * self.run.size.x); + + for (offset, layout) in self.run.content.drain(..) { + let general_position = Size2D::with_x(anchor + factor * offset); let position = self.ctx.axes.specialize(general_position); self.merged_actions.add_layout(position, layout); @@ -274,6 +289,10 @@ impl FlexLayouter { /// Whether this layouter contains any items. pub fn box_is_empty(&self) -> bool { - self.units.is_empty() + !self.units.iter().any(|unit| matches!(unit, FlexUnit::Boxed(_))) + } + + pub fn last_is_space(&self) -> bool { + matches!(self.units.last(), Some(FlexUnit::Space(_))) } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index fb63ff86c..a45147048 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -41,15 +41,6 @@ pub struct Layout { } impl Layout { - /// Create an empty layout with the specified dimensions. - pub fn empty(width: Size, height: Size) -> Layout { - Layout { - dimensions: Size2D::new(width, height), - actions: vec![], - debug_render: true, - } - } - /// Serialize this layout into an output buffer. pub fn serialize(&self, f: &mut W) -> io::Result<()> { writeln!( diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs index 8113a4b7e..b77c0582f 100644 --- a/src/layout/stacked.rs +++ b/src/layout/stacked.rs @@ -58,7 +58,7 @@ impl StackLayouter { // Search for a suitable space to insert the box. while !self.usable.fits(new_dimensions) { - if self.in_last_space() { + if self.boxes.is_empty() && self.in_last_space() { Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?; } @@ -70,7 +70,7 @@ impl StackLayouter { let anchor = self.ctx.axes.anchor(size); self.boxes.push((offset, anchor, layout)); - self.dimensions.y += size.y; + self.dimensions = new_dimensions; Ok(()) } @@ -216,8 +216,8 @@ fn merge_sizes(a: Size2D, b: Size2D) -> Size2D { } fn needs_expansion(axis: AlignedAxis) -> bool { - match (axis.axis.is_positive(), axis.alignment) { - (true, Alignment::Origin) | (false, Alignment::End) => false, - _ => true, - } + !matches!( + (axis.axis.is_positive(), axis.alignment), + (true, Alignment::Origin) | (false, Alignment::End) + ) } diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 11e83209b..b60ead9cb 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -41,7 +41,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { } Node::Space => { - if !self.flex.box_is_empty() { + if !self.flex.box_is_empty() && !self.flex.last_is_space() { let space = self.style.word_spacing * self.style.font_size; self.flex.add_primary_space(space); } @@ -68,6 +68,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { let commands = func.body.val.layout(LayoutContext { style: &self.style, spaces: self.flex.remaining()?, + shrink_to_fit: true, .. self.ctx })?; diff --git a/src/macros.rs b/src/macros.rs index e71136724..b6f069b71 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -40,6 +40,16 @@ macro_rules! error_type { }; } +/// Shorthand for checking whether an expression matches a pattern. +macro_rules! matches { + ($expr:expr, $($pattern:tt)*) => { + match $expr { + $($pattern)* => true, + _ => false, + } + }; +} + /// Create a `Debug` implementation from a `Display` implementation. macro_rules! debug_display { ($type:ident) => ( @@ -58,6 +68,7 @@ macro_rules! debug_display { ); } +/// Declare a module and reexport all its contents. macro_rules! pub_use_mod { ($name:ident) => { mod $name;