diff options
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/flex.rs | 87 | ||||
| -rw-r--r-- | src/layout/mod.rs | 11 | ||||
| -rw-r--r-- | src/layout/stacked.rs | 64 | ||||
| -rw-r--r-- | src/layout/tree.rs | 72 |
4 files changed, 108 insertions, 126 deletions
diff --git a/src/layout/flex.rs b/src/layout/flex.rs index 01086c32..dfe38bba 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -20,8 +20,8 @@ use super::*; #[derive(Debug, Clone)] pub struct FlexLayouter { ctx: FlexContext, - units: Vec<FlexUnit>, stack: StackLayouter, + units: Vec<FlexUnit>, usable: Size, run: FlexRun, @@ -35,6 +35,7 @@ pub struct FlexLayouter { pub struct FlexContext { pub spaces: LayoutSpaces, pub axes: LayoutAxes, + pub shrink_to_fit: bool, /// The spacing between two lines of boxes. pub flex_spacing: Size, } @@ -63,6 +64,7 @@ impl FlexLayouter { let stack = StackLayouter::new(StackContext { spaces: ctx.spaces, axes: ctx.axes, + shrink_to_fit: ctx.shrink_to_fit, }); FlexLayouter { @@ -88,14 +90,20 @@ impl FlexLayouter { } } + /// Add a forced run break. + pub fn add_run_break(&mut self) { + self.units.push(FlexUnit::Break); + } + /// Add a space box which can be replaced by a run break. - pub fn add_space(&mut self, space: Size) { + pub fn add_primary_space(&mut self, space: Size) { self.units.push(FlexUnit::Space(space)); } - /// Add a forced run break. - pub fn add_break(&mut self) { - self.units.push(FlexUnit::Break); + pub fn add_secondary_space(&mut self, space: Size) -> LayoutResult<()> { + self.finish_box()?; + self.stack.add_space(space); + Ok(()) } /// Update the axes in use by this flex layouter. @@ -109,6 +117,21 @@ impl FlexLayouter { /// with borrowed layouters. The state of the layouter is not reset. /// Therefore, it should not be further used after calling `finish`. pub fn finish(&mut self) -> LayoutResult<MultiLayout> { + self.finish_box()?; + Ok(self.stack.finish()) + } + + pub fn finish_layout(&mut self, hard: bool) -> LayoutResult<()> { + self.finish_box()?; + self.stack.finish_layout(hard); + Ok(()) + } + + pub fn finish_box(&mut self) -> LayoutResult<()> { + if self.box_is_empty() { + return Ok(()); + } + // Move the units out of the layout because otherwise, we run into // ownership problems. let units = std::mem::replace(&mut self.units, vec![]); @@ -131,7 +154,28 @@ impl FlexLayouter { // Finish the last flex run. self.finish_run()?; - Ok(self.stack.finish()) + Ok(()) + } + + /// Finish the current flex run. + fn finish_run(&mut self) -> LayoutResult<()> { + let mut actions = LayoutActionList::new(); + for (x, layout) in self.run.content.drain(..) { + let position = self.ctx.axes.specialize(Size2D::with_x(x)); + actions.add_layout(position, layout); + } + + self.run.size.y += self.ctx.flex_spacing; + + self.stack.add(Layout { + dimensions: self.ctx.axes.specialize(self.run.size), + actions: actions.into_vec(), + debug_render: false, + })?; + + self.run.size = Size2D::zero(); + + Ok(()) } /// Layout a content box into the current flex run or start a new run if @@ -150,7 +194,7 @@ impl FlexLayouter { Err(LayoutError::NotEnoughSpace("cannot fix box into flex run"))?; } - self.stack.add_break(true); + self.stack.finish_layout(true); self.usable = self.stack.usable().x; } @@ -174,34 +218,19 @@ impl FlexLayouter { // TODO } - /// Finish the current flex run. - fn finish_run(&mut self) -> LayoutResult<()> { - let mut actions = LayoutActionList::new(); - for (x, layout) in self.run.content.drain(..) { - let position = self.ctx.axes.specialize(Size2D::with_x(x)); - actions.add_layout(position, layout); - } - - self.run.size.y += self.ctx.flex_spacing; - - self.stack.add(Layout { - dimensions: self.ctx.axes.specialize(self.run.size), - actions: actions.into_vec(), - debug_render: false, - })?; - - self.run.size = Size2D::zero(); - - Ok(()) - } - /// This layouter's context. pub fn ctx(&self) -> FlexContext { self.ctx } + pub fn remaining(&self) -> LayoutResult<LayoutSpaces> { + let mut future = self.clone(); + future.finish_box()?; + Ok(future.stack.remaining()) + } + /// Whether this layouter contains any items. - pub fn is_empty(&self) -> bool { + pub fn box_is_empty(&self) -> bool { self.units.is_empty() } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 442747b5..9bb91956 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -150,6 +150,10 @@ pub struct LayoutContext<'a, 'p> { /// The axes to flow on. pub axes: LayoutAxes, + + /// Whether to shrink the spaces to fit the content or to keep + /// the original dimensions. + pub shrink_to_fit: bool, } /// A possibly stack-allocated vector of layout spaces. @@ -163,10 +167,6 @@ pub struct LayoutSpace { /// Padding that should be respected on each side. pub padding: SizeBox, - - /// Whether to shrink the space to fit the content or to keep - /// the original dimensions. - pub shrink_to_fit: bool, } impl LayoutSpace { @@ -182,11 +182,10 @@ impl LayoutSpace { } /// A layout space without padding and dimensions reduced by the padding. - pub fn usable_space(&self, shrink_to_fit: bool) -> LayoutSpace { + pub fn usable_space(&self) -> LayoutSpace { LayoutSpace { dimensions: self.usable(), padding: SizeBox::zero(), - shrink_to_fit, } } } diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs index d80ebf7a..764b8325 100644 --- a/src/layout/stacked.rs +++ b/src/layout/stacked.rs @@ -27,6 +27,7 @@ pub struct StackLayouter { pub struct StackContext { pub spaces: LayoutSpaces, pub axes: LayoutAxes, + pub shrink_to_fit: bool, } impl StackLayouter { @@ -61,7 +62,7 @@ impl StackLayouter { Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?; } - self.add_break(true); + self.finish_layout(true); new_dimensions = merge_sizes(self.dimensions, size); } @@ -85,16 +86,20 @@ impl StackLayouter { /// Add space after the last layout. pub fn add_space(&mut self, space: Size) { if self.dimensions.y + space > self.usable.y { - self.add_break(false); + self.finish_layout(false); } else { self.dimensions.y += space; } } - /// Finish the current layout and start a new one in a new space. - pub fn add_break(&mut self, hard: bool) { - self.finish_layout(); - self.start_new_space(hard); + /// Update the axes in use by this stack layouter. + pub fn set_axes(&self, axes: LayoutAxes) { + if axes != self.ctx.axes { + self.finish_boxes(); + self.usable = self.remains(); + self.dimensions = Size2D::zero(); + self.ctx.axes = axes; + } } /// Finish the layouting. @@ -103,19 +108,20 @@ impl StackLayouter { /// Nevertheless, it should not be used further. pub fn finish(&mut self) -> MultiLayout { if self.hard || !self.boxes.is_empty() { - self.finish_layout(); + self.finish_layout(false); } std::mem::replace(&mut self.layouts, MultiLayout::new()) } - fn finish_layout(&mut self) { + /// Finish the current layout and start a new one in a new space. + pub fn finish_layout(&mut self, hard: bool) { self.finish_boxes(); let space = self.ctx.spaces[self.active_space]; let actions = std::mem::replace(&mut self.merged_actions, LayoutActionList::new()); self.layouts.add(Layout { - dimensions: if space.shrink_to_fit { + dimensions: if self.ctx.shrink_to_fit { self.merged_dimensions.padded(space.padding) } else { space.dimensions @@ -123,6 +129,16 @@ impl StackLayouter { actions: actions.into_vec(), debug_render: true, }); + + let next_space = self.next_space(); + let space = self.ctx.spaces[next_space]; + + self.merged_dimensions = Size2D::zero(); + + self.usable = self.ctx.axes.generalize(space.usable()); + self.dimensions = Size2D::zero(); + self.active_space = next_space; + self.hard = hard; } /// Compose all cached boxes into a layout. @@ -154,29 +170,6 @@ impl StackLayouter { self.merged_dimensions = merge_sizes(self.merged_dimensions, dimensions); } - /// Set up layouting in the next space. - fn start_new_space(&mut self, hard: bool) { - let next_space = self.next_space(); - let space = self.ctx.spaces[next_space]; - - self.merged_dimensions = Size2D::zero(); - - self.usable = self.ctx.axes.generalize(space.usable()); - self.dimensions = Size2D::zero(); - self.active_space = next_space; - self.hard = hard; - } - - /// Update the axes in use by this stack layouter. - pub fn set_axes(&self, axes: LayoutAxes) { - if axes != self.ctx.axes { - self.finish_boxes(); - self.usable = self.remains(); - self.dimensions = Size2D::zero(); - self.ctx.axes = axes; - } - } - /// This layouter's context. pub fn ctx(&self) -> StackContext { self.ctx @@ -187,16 +180,15 @@ impl StackLayouter { self.usable } - /// The remaining spaces for new layouts in the current space. - pub fn remaining(&self, shrink_to_fit: bool) -> LayoutSpaces { + /// The remaining usable spaces for new layouts. + pub fn remaining(&self) -> LayoutSpaces { let mut spaces = smallvec![LayoutSpace { dimensions: self.ctx.axes.specialize(self.remains()), padding: SizeBox::zero(), - shrink_to_fit, }]; for space in &self.ctx.spaces[self.next_space()..] { - spaces.push(space.usable_space(shrink_to_fit)); + spaces.push(space.usable_space()); } spaces diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 89e47c91..4742de23 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -10,7 +10,6 @@ pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiL #[derive(Debug, Clone)] struct TreeLayouter<'a, 'p> { ctx: LayoutContext<'a, 'p>, - stack: StackLayouter, flex: FlexLayouter, style: Cow<'a, TextStyle>, } @@ -20,14 +19,11 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> { TreeLayouter { ctx, - stack: StackLayouter::new(StackContext { - spaces: ctx.spaces, - axes: ctx.axes, - }), flex: FlexLayouter::new(FlexContext { flex_spacing: flex_spacing(&ctx.style), - spaces: ctx.spaces.iter().map(|space| space.usable_space(true)).collect(), + spaces: ctx.spaces, axes: ctx.axes, + shrink_to_fit: ctx.shrink_to_fit, }), style: Cow::Borrowed(ctx.style), } @@ -45,13 +41,14 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { } Node::Space => { - if !self.flex.is_empty() { - self.flex.add_space(self.style.word_spacing * self.style.font_size); + if !self.flex.box_is_empty() { + let space = self.style.word_spacing * self.style.font_size; + self.flex.add_primary_space(space); } } Node::Newline => { - if !self.flex.is_empty() { - self.finish_paragraph()?; + if !self.flex.box_is_empty() { + self.break_paragraph()?; } } @@ -68,15 +65,9 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { /// Layout a function. fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { - // Finish the current flex layout on a copy to find out how - // much space would be remaining if we finished. - let mut lookahead = self.stack.clone(); - lookahead.add_multiple(self.flex.clone().finish()?)?; - let spaces = lookahead.remaining(true); - let commands = func.body.val.layout(LayoutContext { style: &self.style, - spaces, + spaces: self.flex.remaining()?, .. self.ctx })?; @@ -94,16 +85,15 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { Command::Add(layout) => self.flex.add(layout), Command::AddMultiple(layouts) => self.flex.add_multiple(layouts), - Command::BreakFlex => self.flex.add_break(), - Command::FinishFlex => self.finish_paragraph()?, - Command::BreakStack => self.finish_layout()?, + Command::FinishRun => self.flex.add_run_break(), + Command::FinishBox => self.flex.finish_box()?, + Command::FinishLayout => self.flex.finish_layout(true)?, + + Command::BreakParagraph => self.break_paragraph()?, Command::SetStyle(style) => *self.style.to_mut() = style, Command::SetAxes(axes) => { self.flex.set_axes(axes); - if axes.secondary != self.ctx.axes.secondary { - self.stack.set_axes(axes); - } self.ctx.axes = axes; } } @@ -113,43 +103,15 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { /// Finish the layout. fn finish(mut self) -> LayoutResult<MultiLayout> { - self.finish_flex()?; - Ok(self.stack.finish()) - } - - /// Finish the current stack layout with a hard break. - fn finish_layout(&mut self) -> LayoutResult<()> { - self.finish_flex()?; - self.stack.add_break(true); - self.start_new_flex(); - Ok(()) + self.flex.finish() } /// Finish the current flex layout and add space after it. - fn finish_paragraph(&mut self) -> LayoutResult<()> { - self.finish_flex()?; - self.stack.add_space(paragraph_spacing(&self.style)); - self.start_new_flex(); - Ok(()) - } - - /// Finish the current flex layout and add it the stack. - fn finish_flex(&mut self) -> LayoutResult<()> { - if !self.flex.is_empty() { - let layouts = self.flex.finish()?; - self.stack.add_multiple(layouts)?; - } + fn break_paragraph(&mut self) -> LayoutResult<()> { + self.flex.finish_box()?; + self.flex.add_secondary_space(paragraph_spacing(&self.style))?; Ok(()) } - - /// Start a new flex layout. - fn start_new_flex(&mut self) { - self.flex = FlexLayouter::new(FlexContext { - flex_spacing: flex_spacing(&self.style), - spaces: self.stack.remaining(true), - axes: self.ctx.axes, - }); - } } fn flex_spacing(style: &TextStyle) -> Size { |
