Refined expansion model 🔛

This commit is contained in:
Laurenz 2019-11-20 17:28:58 +01:00
parent 1dafe2c2ea
commit f24e9b44e0
7 changed files with 208 additions and 162 deletions

View File

@ -43,7 +43,7 @@ struct PartialLine {
usable: Size, usable: Size,
content: Vec<(Size, Layout)>, content: Vec<(Size, Layout)>,
dimensions: Size2D, dimensions: Size2D,
space: Option<(Size, bool)>, space: Option<Size>,
} }
impl PartialLine { impl PartialLine {
@ -64,7 +64,7 @@ impl PartialLine {
pub struct FlexContext { pub struct FlexContext {
pub spaces: LayoutSpaces, pub spaces: LayoutSpaces,
pub axes: LayoutAxes, pub axes: LayoutAxes,
pub shrink_to_fit: bool, pub expand: bool,
pub flex_spacing: Size, pub flex_spacing: Size,
} }
@ -74,10 +74,11 @@ impl FlexLayouter {
let stack = StackLayouter::new(StackContext { let stack = StackLayouter::new(StackContext {
spaces: ctx.spaces, spaces: ctx.spaces,
axes: ctx.axes, axes: ctx.axes,
shrink_to_fit: ctx.shrink_to_fit, expand: ctx.expand,
}); });
let usable = stack.primary_usable(); let usable = stack.primary_usable();
FlexLayouter { FlexLayouter {
stack, stack,
@ -107,9 +108,11 @@ impl FlexLayouter {
self.units.push(FlexUnit::Space(space, soft)); self.units.push(FlexUnit::Space(space, soft));
} }
pub fn add_secondary_space(&mut self, space: Size) -> LayoutResult<()> { pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> {
self.finish_run()?; if !self.run_is_empty() {
Ok(self.stack.add_space(space)) self.finish_run()?;
}
Ok(self.stack.add_space(space, soft))
} }
pub fn set_axes(&mut self, axes: LayoutAxes) { pub fn set_axes(&mut self, axes: LayoutAxes) {
@ -120,28 +123,26 @@ impl FlexLayouter {
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_run();
// let usable = self.stack.primary_usable();
// self.line = FlexLine::new(usable);
// // self.total_usable = self.stack.primary_usable();
// // self.usable = self.total_usable;
// // self.space = None;
} else { } else {
self.stack.set_spaces(spaces, false); self.stack.set_spaces(spaces, false);
} }
} }
pub fn remaining(&self) -> LayoutResult<(LayoutSpaces, LayoutSpaces)> { pub fn remaining(&self) -> LayoutResult<(LayoutSpaces, Option<LayoutSpaces>)> {
let mut future = self.clone(); if self.run_is_empty() {
future.finish_run()?; Ok((self.stack.remaining(), None))
} else {
let mut future = self.clone();
let remaining_run = future.finish_run()?;
let stack_spaces = future.stack.remaining(); let stack_spaces = future.stack.remaining();
let mut flex_spaces = stack_spaces.clone(); let mut flex_spaces = stack_spaces.clone();
flex_spaces[0].dimensions.x = future.last_run_remaining.x; flex_spaces[0].dimensions.x = remaining_run.x;
flex_spaces[0].dimensions.y += future.last_run_remaining.y; flex_spaces[0].dimensions.y += remaining_run.y;
Ok((flex_spaces, Some(stack_spaces)))
}
Ok((flex_spaces, stack_spaces))
} }
pub fn run_is_empty(&self) -> bool { pub fn run_is_empty(&self) -> bool {
@ -149,7 +150,7 @@ impl FlexLayouter {
} }
pub fn run_last_is_space(&self) -> bool { pub fn run_last_is_space(&self) -> bool {
matches!(self.units.last(), Some(FlexUnit::Space(_))) matches!(self.units.last(), Some(FlexUnit::Space(_, _)))
} }
pub fn finish(mut self) -> LayoutResult<MultiLayout> { pub fn finish(mut self) -> LayoutResult<MultiLayout> {
@ -158,32 +159,65 @@ impl FlexLayouter {
} }
pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> { pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> {
self.finish_run()?; if !self.run_is_empty() {
self.finish_run()?;
}
Ok(self.stack.finish_space(hard)) Ok(self.stack.finish_space(hard))
} }
pub fn finish_run(&mut self) -> LayoutResult<()> { pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
let units = std::mem::replace(&mut self.units, vec![]); let units = std::mem::replace(&mut self.units, vec![]);
for unit in units { for unit in units {
match unit { match unit {
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?, FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
FlexUnit::Space(space, soft) => { FlexUnit::Space(space, soft) => self.layout_space(space, soft),
self.layout_space();
self.space = Some(space);
}
FlexUnit::Break => {
self.space = None;
self.finish_line()?;
},
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes), FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
FlexUnit::Break => self.layout_break(),
} }
} }
self.finish_line()?; self.finish_line()
}
Ok(()) fn finish_line(&mut self) -> LayoutResult<Size2D> {
self.finish_partial_line();
self.stack.add(Layout {
dimensions: self.axes.specialize(self.line.combined_dimensions),
actions: self.line.actions.into_vec(),
debug_render: false,
})?;
let remaining = self.axes.specialize(Size2D {
x: self.line.usable - self.line.combined_dimensions.x,
y: self.line.combined_dimensions.y,
});
self.line = FlexLine::new(self.stack.primary_usable());
Ok(remaining)
}
fn finish_partial_line(&mut self) {
let part = self.line.part;
let factor = self.axes.primary.axis.factor();
let anchor =
self.axes.primary.anchor(self.line.usable)
- self.axes.primary.anchor(part.dimensions.x);
for (offset, layout) in part.content {
let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset));
self.line.actions.add_layout(pos, layout);
}
self.line.combined_dimensions.x.max_eq(part.dimensions.x);
self.line.part = PartialLine::new(self.line.usable - 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<()> {
@ -215,7 +249,7 @@ impl FlexLayouter {
Ok(()) Ok(())
} }
fn layout_space(&mut self) { fn layout_space(&mut self, space: Size, soft: bool) {
if let Some(space) = self.space.take() { if let Some(space) = self.space.take() {
if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable { if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable {
self.run.size.x += space; self.run.size.x += space;
@ -227,19 +261,19 @@ 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 { // self.usable = match axes.primary.alignment {
Alignment::Origin => // Alignment::Origin =>
if self.max_extent == Size::zero() { // if self.max_extent == Size::zero() {
self.total_usable // self.total_usable
} else { // } else {
Size::zero() // Size::zero()
}, // },
Alignment::Center => crate::size::max( // Alignment::Center => crate::size::max(
self.total_usable - 2 * self.max_extent, // self.total_usable - 2 * self.max_extent,
Size::zero() // Size::zero()
), // ),
Alignment::End => self.total_usable - self.max_extent, // Alignment::End => self.total_usable - self.max_extent,
}; // };
} }
if axes.secondary != self.axes.secondary { if axes.secondary != self.axes.secondary {
@ -249,57 +283,8 @@ impl FlexLayouter {
self.axes = axes; self.axes = axes;
} }
fn finish_line(&mut self) -> LayoutResult<()> { fn layout_break(&mut self) {
self.finish_partial_line();
if self.merged_dimensions.y == Size::zero() {
return Ok(());
}
let actions = std::mem::replace(&mut self.merged_actions, LayoutActionList::new());
self.stack.add(Layout {
dimensions: self.axes.specialize(self.merged_dimensions),
actions: actions.into_vec(),
debug_render: false,
})?;
self.merged_dimensions = Size2D::zero();
self.max_extent = Size::zero();
self.usable = self.total_usable;
Ok(())
}
fn finish_partial_line(&mut self) {
if self.run.content.is_empty() {
return;
}
let factor = if self.axes.primary.axis.is_positive() { 1 } else { -1 };
let anchor = self.axes.primary.anchor(self.total_usable)
- self.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.axes.specialize(general_position);
self.merged_actions.add_layout(position, layout);
}
self.merged_dimensions.x = match self.axes.primary.alignment {
Alignment::Origin => self.run.size.x,
Alignment::Center | Alignment::End => self.total_usable,
};
self.merged_dimensions.y = crate::size::max(
self.merged_dimensions.y,
self.run.size.y + self.flex_spacing,
);
self.last_run_remaining = Size2D::new(self.size_left(), self.merged_dimensions.y);
self.run.size = Size2D::zero();
} }
fn size_left(&self) -> Size { fn size_left(&self) -> Size {

View File

@ -147,9 +147,9 @@ pub struct LayoutContext<'a, 'p> {
/// The axes to flow on. /// The axes to flow on.
pub axes: LayoutAxes, pub axes: LayoutAxes,
/// Whether to shrink the spaces to fit the content or to keep /// Whether layouts should expand to the full dimensions of the space
/// the original dimensions. /// they lie on or whether should tightly fit the content.
pub shrink_to_fit: bool, pub expand: bool,
} }
/// A possibly stack-allocated vector of layout spaces. /// A possibly stack-allocated vector of layout spaces.
@ -219,6 +219,14 @@ impl LayoutAxes {
pub fn anchor(&self, space: Size2D) -> Size2D { pub fn anchor(&self, space: Size2D) -> Size2D {
Size2D::new(self.primary.anchor(space.x), self.secondary.anchor(space.y)) Size2D::new(self.primary.anchor(space.x), self.secondary.anchor(space.y))
} }
/// This axes with `expand` set to the given value for both axes.
pub fn expanding(&self, expand: bool) -> LayoutAxes {
LayoutAxes {
primary: self.primary.expanding(expand),
secondary: self.secondary.expanding(expand),
}
}
} }
/// An axis with an alignment. /// An axis with an alignment.
@ -226,17 +234,13 @@ impl LayoutAxes {
pub struct AlignedAxis { pub struct AlignedAxis {
pub axis: Axis, pub axis: Axis,
pub alignment: Alignment, pub alignment: Alignment,
pub expand: bool,
} }
impl AlignedAxis { impl AlignedAxis {
/// Returns an aligned axis if the alignment is compatible with the axis. /// Creates an aligned axis from its three components.
pub fn new(axis: Axis, alignment: Alignment) -> AlignedAxis { pub fn new(axis: Axis, alignment: Alignment, expand: bool) -> AlignedAxis {
AlignedAxis { axis, alignment } AlignedAxis { axis, alignment, expand }
}
/// The pair of axis and alignment.
pub fn pair(&self) -> (Axis, Alignment) {
(self.axis, self.alignment)
} }
/// The position of the anchor specified by this axis on the given line. /// The position of the anchor specified by this axis on the given line.
@ -248,6 +252,16 @@ impl AlignedAxis {
(true, End) | (false, Origin) => line, (true, End) | (false, Origin) => line,
} }
} }
/// This axis with `expand` set to the given value.
pub fn expanding(&self, expand: bool) -> AlignedAxis {
AlignedAxis { expand, ..*self }
}
/// Whether this axis needs expansion.
pub fn needs_expansion(&self) -> bool {
self.expand || self.alignment != Alignment::Origin
}
} }
/// Where to put content. /// Where to put content.

View File

@ -8,28 +8,31 @@ pub struct StackLayouter {
space: usize, space: usize,
hard: bool, hard: bool,
start: Size2D,
actions: LayoutActionList, actions: LayoutActionList,
combined_dimensions: Size2D, combined_dimensions: Size2D, // <- specialized
sub: Subspace, sub: Subspace,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Subspace { struct Subspace {
origin: Size2D, // <- specialized
usable: Size2D, usable: Size2D,
anchor: Size2D, anchor: Size2D, // <- generic
factor: i32, factor: i32,
dimensions: Size2D, dimensions: Size2D, // <- generic
space: Option<Size>,
} }
impl Subspace { impl Subspace {
fn new(usable: Size2D, axes: LayoutAxes) -> Subspace { fn new(origin: Size2D, usable: Size2D, axes: LayoutAxes) -> Subspace {
Subspace { Subspace {
usable, origin,
usable: axes.generalize(usable),
anchor: axes.anchor(usable), anchor: axes.anchor(usable),
factor: axes.secondary.axis.factor(), factor: axes.secondary.axis.factor(),
dimensions: Size2D::zero(), dimensions: Size2D::zero(),
space: None,
} }
} }
} }
@ -41,7 +44,7 @@ impl Subspace {
pub struct StackContext { pub struct StackContext {
pub spaces: LayoutSpaces, pub spaces: LayoutSpaces,
pub axes: LayoutAxes, pub axes: LayoutAxes,
pub shrink_to_fit: bool, pub expand: bool,
} }
impl StackLayouter { impl StackLayouter {
@ -49,7 +52,6 @@ impl StackLayouter {
pub fn new(ctx: StackContext) -> StackLayouter { pub fn new(ctx: StackContext) -> StackLayouter {
let axes = ctx.axes; let axes = ctx.axes;
let space = ctx.spaces[0]; let space = ctx.spaces[0];
let usable = ctx.axes.generalize(space.usable());
StackLayouter { StackLayouter {
ctx, ctx,
@ -57,15 +59,16 @@ impl StackLayouter {
space: 0, space: 0,
hard: true, hard: true,
start: space.start(),
actions: LayoutActionList::new(), actions: LayoutActionList::new(),
combined_dimensions: Size2D::zero(), combined_dimensions: Size2D::zero(),
sub: Subspace::new(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();
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 = merge(self.sub.dimensions, size);
@ -81,10 +84,9 @@ impl StackLayouter {
let offset = self.sub.dimensions.y; let offset = self.sub.dimensions.y;
let anchor = self.ctx.axes.anchor(size); let anchor = self.ctx.axes.anchor(size);
let pos = self.ctx.axes.specialize( let pos = self.sub.origin + self.ctx.axes.specialize(
self.start (self.sub.anchor - anchor)
+ (self.sub.anchor - anchor) + Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset)
+ Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset)
); );
self.actions.add_layout(pos, layout); self.actions.add_layout(pos, layout);
@ -100,19 +102,16 @@ impl StackLayouter {
Ok(()) Ok(())
} }
pub fn add_space(&mut self, space: Size) { pub fn add_space(&mut self, space: Size, soft: bool) {
if self.sub.dimensions.y + space > self.sub.usable.y { self.sub.space = Some(space);
self.finish_space(false); if !soft {
} else { self.layout_space();
self.sub.dimensions.y += space;
} }
} }
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(); self.finish_subspace(axes);
self.ctx.axes = axes;
self.sub = Subspace::new(self.remaining_subspace(), axes);
} }
} }
@ -128,7 +127,7 @@ impl StackLayouter {
pub fn remaining(&self) -> LayoutSpaces { pub fn remaining(&self) -> LayoutSpaces {
let mut spaces = smallvec![LayoutSpace { let mut spaces = smallvec![LayoutSpace {
dimensions: self.ctx.axes.specialize(self.remaining_subspace()), dimensions: self.remaining_subspace().1,
padding: SizeBox::zero(), padding: SizeBox::zero(),
}]; }];
@ -161,13 +160,13 @@ impl StackLayouter {
} }
pub fn finish_space(&mut self, hard: bool) { pub fn finish_space(&mut self, hard: bool) {
self.finish_subspace(); self.finish_subspace(self.ctx.axes);
let space = self.ctx.spaces[self.space]; let space = self.ctx.spaces[self.space];
let actions = std::mem::replace(&mut self.actions, LayoutActionList::new()); let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
self.layouts.add(Layout { self.layouts.add(Layout {
dimensions: match self.ctx.shrink_to_fit { dimensions: match self.ctx.expand {
true => self.combined_dimensions.padded(space.padding), true => self.combined_dimensions.padded(space.padding),
false => space.dimensions, false => space.dimensions,
}, },
@ -180,27 +179,60 @@ 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;
let space = self.ctx.spaces[space]; let space = self.ctx.spaces[space];
let usable = self.ctx.axes.generalize(space.usable());
self.hard = hard; self.hard = hard;
self.start = space.start();
self.combined_dimensions = Size2D::zero(); self.combined_dimensions = Size2D::zero();
self.sub = Subspace::new(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 + 1).min(self.ctx.spaces.len() - 1)
} }
fn finish_subspace(&mut self) { fn finish_subspace(&mut self, new_axes: LayoutAxes) {
let dims = self.ctx.axes.specialize(self.sub.dimensions); if self.ctx.axes.primary.needs_expansion() {
self.combined_dimensions = merge(self.combined_dimensions, dims); self.sub.dimensions.x = self.sub.usable.x;
}
if self.ctx.axes.secondary.needs_expansion() {
self.sub.dimensions.y = self.sub.usable.y;
}
let (new_origin, new_usable) = self.remaining_subspace();
let origin = self.sub.origin;
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
let space = self.ctx.spaces[self.space];
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 { fn remaining_subspace(&self) -> (Size2D, Size2D) {
Size2D::new(self.sub.usable.x, self.sub.usable.y - self.sub.dimensions.y) let used = self.ctx.axes.specialize(self.sub.usable);
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
let new_usable = self.ctx.axes.specialize(Size2D {
x: self.sub.usable.x,
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)
}
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;
}
}
} }
} }

View File

@ -23,7 +23,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
flex_spacing: flex_spacing(&ctx.text_style), flex_spacing: flex_spacing(&ctx.text_style),
spaces: ctx.spaces.clone(), spaces: ctx.spaces.clone(),
axes: ctx.axes, axes: ctx.axes,
shrink_to_fit: ctx.shrink_to_fit, expand: ctx.expand,
}), }),
style: ctx.text_style.clone(), style: ctx.text_style.clone(),
ctx, ctx,
@ -66,24 +66,25 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
/// Layout a function. /// Layout a function.
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
let (flex_spaces, stack_spaces) = self.flex.remaining()?; let (first, second) = self.flex.remaining()?;
let ctx = |spaces| LayoutContext { let ctx = |spaces| LayoutContext {
loader: self.ctx.loader,
top_level: false, top_level: false,
text_style: &self.style, text_style: &self.style,
page_style: self.ctx.page_style,
spaces, spaces,
shrink_to_fit: true, axes: self.ctx.axes.expanding(false),
.. self.ctx expand: false,
}; };
// Try putting it in the flex space first, but if that is not enough let commands = match func.body.val.layout(ctx(first)) {
// space, use the other space.
let commands = match func.body.val.layout(ctx(flex_spaces)) {
Ok(c) => c, Ok(c) => c,
Err(LayoutError::NotEnoughSpace(_)) => { Err(e) => match (e, second) {
func.body.val.layout(ctx(stack_spaces))? (LayoutError::NotEnoughSpace(_), Some(space))
=> func.body.val.layout(ctx(space))?,
_ => Err(e)?,
}, },
e => e?,
}; };
for command in commands { for command in commands {
@ -101,10 +102,10 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Command::AddMultiple(layouts) => self.flex.add_multiple(layouts), Command::AddMultiple(layouts) => self.flex.add_multiple(layouts),
Command::AddPrimarySpace(space) => self.flex.add_primary_space(space, false), Command::AddPrimarySpace(space) => self.flex.add_primary_space(space, false),
Command::AddSecondarySpace(space) => self.flex.add_secondary_space(space)?, Command::AddSecondarySpace(space) => self.flex.add_secondary_space(space, false)?,
Command::FinishLine => self.flex.add_break(), Command::FinishLine => self.flex.add_break(),
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.break_paragraph()?,
@ -140,7 +141,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
/// Finish the current flex layout and add space after it. /// Finish the current flex layout and add space after it.
fn break_paragraph(&mut self) -> LayoutResult<()> { fn break_paragraph(&mut self) -> LayoutResult<()> {
self.flex.add_secondary_space(paragraph_spacing(&self.style)) self.flex.add_secondary_space(paragraph_spacing(&self.style), true)
} }
} }

View File

@ -106,10 +106,10 @@ impl<'p> Typesetter<'p> {
padding: self.page_style.margins, padding: self.page_style.margins,
}], }],
axes: LayoutAxes { axes: LayoutAxes {
primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Origin), primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Origin, false),
secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Origin), secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Origin, false),
}, },
shrink_to_fit: false, expand: true,
}, },
)?) )?)
} }

View File

@ -19,7 +19,7 @@ pub struct ParagraphBreak;
function! { function! {
data: ParagraphBreak, data: ParagraphBreak,
parse: plain, parse: plain,
layout(_, _) { Ok(commands![FinishRun]) } layout(_, _) { Ok(commands![BreakParagraph]) }
} }
macro_rules! space_func { macro_rules! space_func {

View File

@ -95,6 +95,12 @@ impl Size {
pub fn to_inches(&self) -> f32 { pub fn to_inches(&self) -> f32 {
self.points * 0.0138889 self.points * 0.0138889
} }
/// Set this size to the maximum of itself and the other size.
#[inline]
pub fn max_eq(&mut self, other: Size) {
*self = max(*self, other);
}
} }
impl Size2D { impl Size2D {
@ -146,6 +152,14 @@ impl Size2D {
pub fn fits(&self, other: Size2D) -> bool { pub fn fits(&self, other: Size2D) -> bool {
self.x >= other.x && self.y >= other.y self.x >= other.x && self.y >= other.y
} }
/// Set this size to the maximum of itself and the other size
/// (for both dimensions).
#[inline]
pub fn max_eq(&mut self, other: Size2D) {
self.x.max_eq(other.x);
self.y.max_eq(other.y);
}
} }
impl SizeBox { impl SizeBox {