Completed stack and flex refactoring 🔋

This commit is contained in:
Laurenz 2019-11-21 16:03:15 +01:00
parent f24e9b44e0
commit 863a1a7a00
5 changed files with 197 additions and 179 deletions

View File

@ -133,9 +133,10 @@ impl LayoutActionList {
self.actions.is_empty() self.actions.is_empty()
} }
/// Return the list of actions as a vector. /// Return the list of actions as a vector, leaving an empty
pub fn into_vec(self) -> Vec<LayoutAction> { /// vector in its position.
self.actions pub fn to_vec(&mut self) -> Vec<LayoutAction> {
std::mem::replace(&mut self.actions, vec![])
} }
/// Append a cached move action if one is cached. /// Append a cached move action if one is cached.

View File

@ -2,13 +2,13 @@ use super::*;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FlexLayouter { pub struct FlexLayouter {
stack: StackLayouter,
axes: LayoutAxes, axes: LayoutAxes,
flex_spacing: Size, flex_spacing: Size,
stack: StackLayouter,
units: Vec<FlexUnit>, units: Vec<FlexUnit>,
line: FlexLine, line: FlexLine,
part: PartialLine,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -24,7 +24,6 @@ struct FlexLine {
usable: Size, usable: Size,
actions: LayoutActionList, actions: LayoutActionList,
combined_dimensions: Size2D, combined_dimensions: Size2D,
part: PartialLine,
} }
impl FlexLine { impl FlexLine {
@ -33,7 +32,6 @@ impl FlexLine {
usable, usable,
actions: LayoutActionList::new(), actions: LayoutActionList::new(),
combined_dimensions: Size2D::zero(), combined_dimensions: Size2D::zero(),
part: PartialLine::new(usable),
} }
} }
} }
@ -44,6 +42,7 @@ struct PartialLine {
content: Vec<(Size, Layout)>, content: Vec<(Size, Layout)>,
dimensions: Size2D, dimensions: Size2D,
space: Option<Size>, space: Option<Size>,
last_was_space: bool,
} }
impl PartialLine { impl PartialLine {
@ -53,6 +52,7 @@ impl PartialLine {
content: vec![], content: vec![],
dimensions: Size2D::zero(), dimensions: Size2D::zero(),
space: None, space: None,
last_was_space: false,
} }
} }
} }
@ -80,13 +80,13 @@ impl FlexLayouter {
let usable = stack.primary_usable(); let usable = stack.primary_usable();
FlexLayouter { FlexLayouter {
stack,
axes: ctx.axes, axes: ctx.axes,
flex_spacing: ctx.flex_spacing, flex_spacing: ctx.flex_spacing,
stack,
units: vec![], units: vec![],
line: FlexLine::new(usable) line: FlexLine::new(usable),
part: PartialLine::new(usable),
} }
} }
@ -105,7 +105,7 @@ impl FlexLayouter {
} }
pub fn add_primary_space(&mut self, space: Size, soft: bool) { pub fn add_primary_space(&mut self, space: Size, soft: bool) {
self.units.push(FlexUnit::Space(space, soft)); self.units.push(FlexUnit::Space(space, soft))
} }
pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> { pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> {
@ -122,7 +122,7 @@ impl FlexLayouter {
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) { pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
if replace_empty && self.run_is_empty() && self.stack.space_is_empty() { if replace_empty && self.run_is_empty() && self.stack.space_is_empty() {
self.stack.set_spaces(spaces, true); self.stack.set_spaces(spaces, true);
self.start_run(); self.start_line();
} else { } else {
self.stack.set_spaces(spaces, false); self.stack.set_spaces(spaces, false);
} }
@ -142,7 +142,6 @@ impl FlexLayouter {
Ok((flex_spaces, Some(stack_spaces))) Ok((flex_spaces, Some(stack_spaces)))
} }
} }
pub fn run_is_empty(&self) -> bool { pub fn run_is_empty(&self) -> bool {
@ -162,7 +161,9 @@ impl FlexLayouter {
if !self.run_is_empty() { if !self.run_is_empty() {
self.finish_run()?; self.finish_run()?;
} }
Ok(self.stack.finish_space(hard))
self.stack.finish_space(hard);
Ok(self.start_line())
} }
pub fn finish_run(&mut self) -> LayoutResult<Size2D> { pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
@ -172,7 +173,7 @@ impl FlexLayouter {
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?, FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
FlexUnit::Space(space, soft) => self.layout_space(space, soft), FlexUnit::Space(space, soft) => self.layout_space(space, soft),
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes), FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
FlexUnit::Break => self.layout_break(), FlexUnit::Break => { self.finish_line()?; },
} }
} }
@ -183,77 +184,91 @@ impl FlexLayouter {
self.finish_partial_line(); self.finish_partial_line();
self.stack.add(Layout { self.stack.add(Layout {
dimensions: self.axes.specialize(self.line.combined_dimensions), dimensions: self.axes.specialize(Size2D {
actions: self.line.actions.into_vec(), x: self.line.usable,
y: self.line.combined_dimensions.y + self.flex_spacing,
}),
actions: self.line.actions.to_vec(),
debug_render: false, debug_render: false,
})?; })?;
let remaining = self.axes.specialize(Size2D { let remaining = self.axes.specialize(Size2D {
x: self.line.usable - self.line.combined_dimensions.x, x: self.part.usable
- self.part.dimensions.x
- self.part.space.unwrap_or(Size::zero()),
y: self.line.combined_dimensions.y, y: self.line.combined_dimensions.y,
}); });
self.line = FlexLine::new(self.stack.primary_usable()); self.start_line();
Ok(remaining) Ok(remaining)
} }
fn finish_partial_line(&mut self) { fn start_line(&mut self) {
let part = self.line.part; let usable = self.stack.primary_usable();
self.line = FlexLine::new(usable);
self.part = PartialLine::new(usable);
}
fn finish_partial_line(&mut self) {
let factor = self.axes.primary.axis.factor(); let factor = self.axes.primary.axis.factor();
let anchor = let anchor =
self.axes.primary.anchor(self.line.usable) self.axes.primary.anchor(self.line.usable)
- self.axes.primary.anchor(part.dimensions.x); - self.axes.primary.anchor(self.part.dimensions.x);
for (offset, layout) in part.content { for (offset, layout) in self.part.content.drain(..) {
let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset)); let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset));
self.line.actions.add_layout(pos, layout); self.line.actions.add_layout(pos, layout);
} }
self.line.combined_dimensions.x.max_eq(part.dimensions.x); self.line.combined_dimensions.x = anchor + factor * self.part.dimensions.x;
self.line.part = PartialLine::new(self.line.usable - part.dimensions.x); self.line.combined_dimensions.y.max_eq(self.part.dimensions.x);
}
fn start_run(&mut self) {
let usable = self.stack.primary_usable();
self.line = FlexLine::new(usable);
} }
fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> { fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> {
let size = self.axes.generalize(boxed.dimensions); let size = self.axes.generalize(boxed.dimensions);
if size.x > self.size_left() { let new_dimension = self.part.dimensions.x
self.space = None; + self.part.space.unwrap_or(Size::zero());
if new_dimension > self.part.usable {
self.finish_line()?; self.finish_line()?;
while size.x > self.usable { while size.x > self.line.usable {
if self.stack.space_is_last() { if self.stack.space_is_last() {
Err(LayoutError::NotEnoughSpace("cannot fix box into flex run"))?; Err(LayoutError::NotEnoughSpace("failed to add box to flex run"))?;
} }
self.finish_space(true); self.stack.finish_space(true);
self.total_usable = self.stack.primary_usable();
self.usable = self.total_usable;
} }
} }
self.layout_space(); if let Some(space) = self.part.space.take() {
self.layout_space(space, false);
}
let offset = self.run.size.x; let offset = self.part.dimensions.x;
self.run.content.push((offset, boxed)); self.part.content.push((offset, boxed));
self.run.size.x += size.x; self.part.dimensions.x += size.x;
self.run.size.y = crate::size::max(self.run.size.y, size.y); self.part.dimensions.y.max_eq(size.y);
self.part.last_was_space = false;
Ok(()) Ok(())
} }
fn layout_space(&mut self, space: Size, soft: bool) { fn layout_space(&mut self, space: Size, soft: bool) {
if let Some(space) = self.space.take() { if soft {
if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable { if !self.part.last_was_space {
self.run.size.x += space; self.part.space = Some(space);
} }
} else {
if self.part.dimensions.x + space > self.part.usable {
self.part.dimensions.x = self.part.usable;
} else {
self.part.dimensions.x += space;
}
self.part.last_was_space = true;
} }
} }
@ -261,19 +276,17 @@ impl FlexLayouter {
if axes.primary != self.axes.primary { if axes.primary != self.axes.primary {
self.finish_partial_line(); self.finish_partial_line();
// self.usable = match axes.primary.alignment { let extent = self.line.combined_dimensions.x;
// Alignment::Origin => let usable = self.line.usable;
// if self.max_extent == Size::zero() {
// self.total_usable let new_usable = match axes.primary.alignment {
// } else { Alignment::Origin if extent == Size::zero() => usable,
// Size::zero() Alignment::Center if extent < usable / 2 => usable - 2 * extent,
// }, Alignment::End => usable - extent,
// Alignment::Center => crate::size::max( _ => Size::zero(),
// self.total_usable - 2 * self.max_extent, };
// Size::zero()
// ), self.part = PartialLine::new(new_usable);
// Alignment::End => self.total_usable - self.max_extent,
// };
} }
if axes.secondary != self.axes.secondary { if axes.secondary != self.axes.secondary {
@ -282,13 +295,4 @@ impl FlexLayouter {
self.axes = axes; self.axes = axes;
} }
fn layout_break(&mut self) {
}
fn size_left(&self) -> Size {
let space = self.space.unwrap_or(Size::zero());
self.usable - (self.run.size.x + space)
}
} }

View File

@ -6,22 +6,38 @@ pub struct StackLayouter {
ctx: StackContext, ctx: StackContext,
layouts: MultiLayout, layouts: MultiLayout,
space: usize, space: Space,
hard: bool,
actions: LayoutActionList,
combined_dimensions: Size2D, // <- specialized
sub: Subspace, sub: Subspace,
} }
#[derive(Debug, Clone)]
struct Space {
index: usize,
hard: bool,
actions: LayoutActionList,
combined_dimensions: Size2D,
}
impl Space {
fn new(index: usize, hard: bool) -> Space {
Space {
index,
hard,
actions: LayoutActionList::new(),
combined_dimensions: Size2D::zero(),
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Subspace { struct Subspace {
origin: Size2D, // <- specialized origin: Size2D,
usable: Size2D, usable: Size2D,
anchor: Size2D, // <- generic anchor: Size2D,
factor: i32, factor: i32,
dimensions: Size2D, // <- generic dimensions: Size2D,
space: Option<Size>, space: Option<Size>,
last_was_space: bool,
} }
impl Subspace { impl Subspace {
@ -33,6 +49,7 @@ impl Subspace {
factor: axes.secondary.axis.factor(), factor: axes.secondary.axis.factor(),
dimensions: Size2D::zero(), dimensions: Size2D::zero(),
space: None, space: None,
last_was_space: false,
} }
} }
} }
@ -56,29 +73,30 @@ impl StackLayouter {
StackLayouter { StackLayouter {
ctx, ctx,
layouts: MultiLayout::new(), layouts: MultiLayout::new(),
space: Space::new(0, true),
space: 0,
hard: true,
actions: LayoutActionList::new(),
combined_dimensions: Size2D::zero(),
sub: Subspace::new(space.start(), space.usable(), axes), sub: Subspace::new(space.start(), space.usable(), axes),
} }
} }
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> { pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
self.layout_space(); if let Some(space) = self.sub.space.take() {
self.add_space(space, false);
}
let size = self.ctx.axes.generalize(layout.dimensions); let size = self.ctx.axes.generalize(layout.dimensions);
let mut new_dimensions = merge(self.sub.dimensions, size);
let mut new_dimensions = Size2D {
x: crate::size::max(self.sub.dimensions.x, size.x),
y: self.sub.dimensions.y + size.y
};
while !self.sub.usable.fits(new_dimensions) { while !self.sub.usable.fits(new_dimensions) {
if self.space_is_empty() { if self.space_is_last() && self.space_is_empty() {
Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?; Err(LayoutError::NotEnoughSpace("failed to add box to stack"))?;
} }
self.finish_space(true); self.finish_space(true);
new_dimensions = merge(self.sub.dimensions, size); new_dimensions = size;
} }
let offset = self.sub.dimensions.y; let offset = self.sub.dimensions.y;
@ -86,11 +104,12 @@ impl StackLayouter {
let pos = self.sub.origin + self.ctx.axes.specialize( let pos = self.sub.origin + self.ctx.axes.specialize(
(self.sub.anchor - anchor) (self.sub.anchor - anchor)
+ Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset) + Size2D::with_y(self.space.combined_dimensions.y + self.sub.factor * offset)
); );
self.actions.add_layout(pos, layout); self.space.actions.add_layout(pos, layout);
self.sub.dimensions = new_dimensions; self.sub.dimensions = new_dimensions;
self.sub.last_was_space = false;
Ok(()) Ok(())
} }
@ -103,24 +122,36 @@ impl StackLayouter {
} }
pub fn add_space(&mut self, space: Size, soft: bool) { pub fn add_space(&mut self, space: Size, soft: bool) {
self.sub.space = Some(space); if soft {
if !soft { if !self.sub.last_was_space {
self.layout_space(); self.sub.space = Some(space);
}
} else {
if self.sub.dimensions.y + space > self.sub.usable.y {
self.sub.dimensions.y = self.sub.usable.y;
self.finish_space(false);
} else {
self.sub.dimensions.y += space;
}
self.sub.last_was_space = true;
} }
} }
pub fn set_axes(&mut self, axes: LayoutAxes) { pub fn set_axes(&mut self, axes: LayoutAxes) {
if axes != self.ctx.axes { if axes != self.ctx.axes {
self.finish_subspace(axes); self.finish_subspace();
let (origin, usable) = self.remaining_subspace();
self.ctx.axes = axes;
self.sub = Subspace::new(origin, usable, axes);
} }
} }
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) { pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
if replace_empty && self.space_is_empty() { if replace_empty && self.space_is_empty() {
self.ctx.spaces = spaces; self.ctx.spaces = spaces;
self.start_space(0, self.hard); self.start_space(0, self.space.hard);
} else { } else {
self.ctx.spaces.truncate(self.space + 1); self.ctx.spaces.truncate(self.space.index + 1);
self.ctx.spaces.extend(spaces); self.ctx.spaces.extend(spaces);
} }
} }
@ -143,34 +174,33 @@ impl StackLayouter {
} }
pub fn space_is_empty(&self) -> bool { pub fn space_is_empty(&self) -> bool {
self.combined_dimensions == Size2D::zero() self.space.combined_dimensions == Size2D::zero()
&& self.space.actions.is_empty()
&& self.sub.dimensions == Size2D::zero() && self.sub.dimensions == Size2D::zero()
&& self.actions.is_empty()
} }
pub fn space_is_last(&self) -> bool { pub fn space_is_last(&self) -> bool {
self.space == self.ctx.spaces.len() - 1 self.space.index == self.ctx.spaces.len() - 1
} }
pub fn finish(mut self) -> MultiLayout { pub fn finish(mut self) -> MultiLayout {
if self.hard || !self.space_is_empty() { if self.space.hard || !self.space_is_empty() {
self.finish_space(false); self.finish_space(false);
} }
self.layouts self.layouts
} }
pub fn finish_space(&mut self, hard: bool) { pub fn finish_space(&mut self, hard: bool) {
self.finish_subspace(self.ctx.axes); self.finish_subspace();
let space = self.ctx.spaces[self.space]; let space = self.ctx.spaces[self.space.index];
let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
self.layouts.add(Layout { self.layouts.add(Layout {
dimensions: match self.ctx.expand { dimensions: match self.ctx.expand {
true => self.combined_dimensions.padded(space.padding), true => self.space.combined_dimensions.padded(space.padding),
false => space.dimensions, false => space.dimensions,
}, },
actions: actions.into_vec(), actions: self.space.actions.to_vec(),
debug_render: true, debug_render: true,
}); });
@ -178,19 +208,17 @@ impl StackLayouter {
} }
fn start_space(&mut self, space: usize, hard: bool) { fn start_space(&mut self, space: usize, hard: bool) {
self.space = space; self.space = Space::new(space, hard);
let space = self.ctx.spaces[space];
self.hard = hard; let space = self.ctx.spaces[space];
self.combined_dimensions = Size2D::zero();
self.sub = Subspace::new(space.start(), space.usable(), self.ctx.axes); self.sub = Subspace::new(space.start(), space.usable(), self.ctx.axes);
} }
fn next_space(&self) -> usize { fn next_space(&self) -> usize {
(self.space + 1).min(self.ctx.spaces.len() - 1) (self.space.index + 1).min(self.ctx.spaces.len() - 1)
} }
fn finish_subspace(&mut self, new_axes: LayoutAxes) { fn finish_subspace(&mut self) {
if self.ctx.axes.primary.needs_expansion() { if self.ctx.axes.primary.needs_expansion() {
self.sub.dimensions.x = self.sub.usable.x; self.sub.dimensions.x = self.sub.usable.x;
} }
@ -199,46 +227,23 @@ impl StackLayouter {
self.sub.dimensions.y = self.sub.usable.y; self.sub.dimensions.y = self.sub.usable.y;
} }
let (new_origin, new_usable) = self.remaining_subspace(); let space = self.ctx.spaces[self.space.index];
let origin = self.sub.origin; let origin = self.sub.origin;
let dimensions = self.ctx.axes.specialize(self.sub.dimensions); let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
let space = self.ctx.spaces[self.space]; self.space.combined_dimensions.max_eq(origin - space.start() + dimensions);
self.combined_dimensions.max_eq(origin - space.start() + dimensions);
self.ctx.axes = new_axes;
self.sub = Subspace::new(new_origin, new_usable, new_axes);
} }
fn remaining_subspace(&self) -> (Size2D, Size2D) { fn remaining_subspace(&self) -> (Size2D, Size2D) {
let used = self.ctx.axes.specialize(self.sub.usable); let new_origin = self.sub.origin + match self.ctx.axes.secondary.axis.is_positive() {
let dimensions = self.ctx.axes.specialize(self.sub.dimensions); true => self.ctx.axes.specialize(Size2D::with_y(self.sub.dimensions.y)),
false => Size2D::zero(),
};
let new_usable = self.ctx.axes.specialize(Size2D { let new_usable = self.ctx.axes.specialize(Size2D {
x: self.sub.usable.x, x: self.sub.usable.x,
y: self.sub.usable.y - self.sub.dimensions.y, y: self.sub.usable.y - self.sub.dimensions.y,
}); });
let new_origin = self.sub.origin
+ Size2D::with_y(self.ctx.axes.specialize(self.sub.dimensions).y);
(new_origin, new_usable) (new_origin, new_usable)
} }
fn layout_space(&mut self) {
if let Some(space) = self.sub.space.take() {
if self.sub.dimensions.y + space > self.sub.usable.y {
self.finish_space(false);
} else {
self.sub.dimensions.y += space;
}
}
}
}
fn merge(a: Size2D, b: Size2D) -> Size2D {
Size2D {
x: crate::size::max(a.x, b.x),
y: a.y + b.y
}
} }

View File

@ -72,7 +72,7 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
Ok(Layout { Ok(Layout {
dimensions: Size2D::new(self.width, self.ctx.style.font_size), dimensions: Size2D::new(self.width, self.ctx.style.font_size),
actions: self.actions.into_vec(), actions: self.actions.to_vec(),
debug_render: false, debug_render: false,
}) })
} }

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
use smallvec::smallvec; use smallvec::smallvec;
/// Layouts syntax trees into boxes.
pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout> { pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout> {
let mut layouter = TreeLayouter::new(ctx); let mut layouter = TreeLayouter::new(ctx);
layouter.layout(tree)?; layouter.layout(tree)?;
@ -16,7 +15,7 @@ struct TreeLayouter<'a, 'p> {
} }
impl<'a, 'p> TreeLayouter<'a, 'p> { impl<'a, 'p> TreeLayouter<'a, 'p> {
/// Create a new layouter. /// Create a new syntax tree layouter.
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> { fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
TreeLayouter { TreeLayouter {
flex: FlexLayouter::new(FlexContext { flex: FlexLayouter::new(FlexContext {
@ -30,28 +29,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
} }
} }
/// Layout a syntax tree.
fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> { fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> {
for node in &tree.nodes { for node in &tree.nodes {
match &node.val { match &node.val {
Node::Text(text) => { Node::Text(text) => self.layout_text(text)?,
self.flex.add(layout_text(text, TextContext {
loader: &self.ctx.loader,
style: &self.style,
})?);
}
Node::Space => { Node::Space => self.layout_space(),
if !self.flex.run_is_empty() && !self.flex.run_last_is_space() { Node::Newline => self.layout_paragraph()?,
let space = self.style.word_spacing * self.style.font_size;
self.flex.add_primary_space(space, true);
}
}
Node::Newline => {
if !self.flex.run_is_empty() {
self.break_paragraph()?;
}
}
Node::ToggleItalics => self.style.toggle_class(FontClass::Italic), Node::ToggleItalics => self.style.toggle_class(FontClass::Italic),
Node::ToggleBold => self.style.toggle_class(FontClass::Bold), Node::ToggleBold => self.style.toggle_class(FontClass::Bold),
@ -64,27 +48,53 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Ok(()) Ok(())
} }
/// Layout a function. fn layout_text(&mut self, text: &str) -> LayoutResult<()> {
let layout = layout_text(text, TextContext {
loader: &self.ctx.loader,
style: &self.style,
})?;
Ok(self.flex.add(layout))
}
fn layout_space(&mut self) {
if !self.flex.run_is_empty() {
self.flex.add_primary_space(word_spacing(&self.style), true);
}
}
fn layout_paragraph(&mut self) -> LayoutResult<()> {
if !self.flex.run_is_empty() {
self.flex.add_secondary_space(paragraph_spacing(&self.style), true)?;
}
Ok(())
}
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
let (first, second) = self.flex.remaining()?; let (first, second) = self.flex.remaining()?;
let ctx = |spaces| LayoutContext { let ctx = |spaces| {
loader: self.ctx.loader, LayoutContext {
top_level: false, loader: self.ctx.loader,
text_style: &self.style, top_level: false,
page_style: self.ctx.page_style, text_style: &self.style,
spaces, page_style: self.ctx.page_style,
axes: self.ctx.axes.expanding(false), spaces,
expand: false, axes: self.ctx.axes.expanding(false),
expand: false,
}
}; };
let commands = match func.body.val.layout(ctx(first)) { let commands = match func.body.val.layout(ctx(first)) {
Ok(c) => c, Ok(c) => c,
Err(e) => match (e, second) { Err(e) => {
(LayoutError::NotEnoughSpace(_), Some(space)) match (e, second) {
=> func.body.val.layout(ctx(space))?, (LayoutError::NotEnoughSpace(_), Some(space)) => {
_ => Err(e)?, func.body.val.layout(ctx(space))?
}, }
(e, _) => Err(e)?,
}
}
}; };
for command in commands { for command in commands {
@ -108,7 +118,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Command::FinishRun => { self.flex.finish_run()?; }, Command::FinishRun => { self.flex.finish_run()?; },
Command::FinishSpace => self.flex.finish_space(true)?, Command::FinishSpace => self.flex.finish_space(true)?,
Command::BreakParagraph => self.break_paragraph()?, Command::BreakParagraph => self.layout_paragraph()?,
Command::SetTextStyle(style) => self.style = style, Command::SetTextStyle(style) => self.style = style,
Command::SetPageStyle(style) => { Command::SetPageStyle(style) => {
@ -134,15 +144,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Ok(()) Ok(())
} }
/// Finish the layout.
fn finish(self) -> LayoutResult<MultiLayout> { fn finish(self) -> LayoutResult<MultiLayout> {
self.flex.finish() self.flex.finish()
} }
}
/// Finish the current flex layout and add space after it. fn word_spacing(style: &TextStyle) -> Size {
fn break_paragraph(&mut self) -> LayoutResult<()> { style.word_spacing * style.font_size
self.flex.add_secondary_space(paragraph_spacing(&self.style), true)
}
} }
fn flex_spacing(style: &TextStyle) -> Size { fn flex_spacing(style: &TextStyle) -> Size {