diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b0fba4f2c..a5dfa0ad7 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -149,10 +149,7 @@ pub struct LayoutSpace { impl LayoutSpace { /// The actually usable area (dimensions minus padding). pub fn usable(&self) -> Size2D { - Size2D { - x: self.dimensions.x - self.padding.left - self.padding.right, - y: self.dimensions.y - self.padding.top - self.padding.bottom, - } + self.dimensions.unpadded(self.padding) } } @@ -161,6 +158,7 @@ impl LayoutSpace { pub enum Alignment { Left, Right, + Center, } /// The error type for layouting. diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs index 367a03d77..3a29f7229 100644 --- a/src/layout/stacked.rs +++ b/src/layout/stacked.rs @@ -73,6 +73,7 @@ impl StackLayouter { let position = match self.space.alignment { Alignment::Left => self.cursor, Alignment::Right => self.cursor - Size2D::with_x(layout.dimensions.x), + Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2), }; self.cursor.y += layout.dimensions.y; @@ -172,9 +173,9 @@ impl StackLayouter { } } - /// Whether this layouter contains any items. - pub fn is_empty(&self) -> bool { - self.layouts.is_empty() && self.actions.is_empty() + /// Whether the active space of this layouter contains no content. + pub fn current_space_is_empty(&self) -> bool { + !self.started || self.actions.is_empty() } fn overflows(&self, dimensions: Size2D) -> bool { @@ -185,7 +186,7 @@ impl StackLayouter { fn start_dimensions(space: LayoutSpace) -> Size2D { match space.alignment { Alignment::Left => Size2D::zero(), - Alignment::Right => Size2D::with_x(space.usable().x), + Alignment::Right | Alignment::Center => Size2D::with_x(space.usable().x), } } @@ -197,6 +198,7 @@ fn start_cursor(space: LayoutSpace) -> Size2D { x: match space.alignment { Alignment::Left => space.padding.left, Alignment::Right => space.dimensions.x - space.padding.right, + Alignment::Center => space.padding.left + (space.usable().x / 2), }, y: space.padding.top, } diff --git a/src/layout/tree.rs b/src/layout/tree.rs index c8695e837..7feed6106 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -47,8 +47,14 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { // Finish the current flex layouting process. Node::Newline => { - let space = paragraph_spacing(&self.style); - self.layout_flex(space)?; + self.layout_flex()?; + + if !self.stack.current_space_is_empty() { + let space = paragraph_spacing(&self.style); + self.stack.add_space(space)?; + } + + self.start_new_flex(); } // Toggle the text styles. @@ -65,11 +71,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { /// Finish the layout. fn finish(mut self) -> LayoutResult { - // If there are remainings, add them to the layout. - if !self.flex.is_empty() { - self.layout_flex(Size::zero())?; - } - + self.layout_flex()?; self.stack.finish() } @@ -93,22 +95,24 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { } /// Finish the current flex layout and add it the stack. - fn layout_flex(&mut self, after_space: Size) -> LayoutResult<()> { + fn layout_flex(&mut self) -> LayoutResult<()> { if self.flex.is_empty() { return Ok(()); } let layouts = self.flex.finish()?; self.stack.add_many(layouts)?; - self.stack.add_space(after_space)?; + Ok(()) + } + + /// Start a new flex layout. + fn start_new_flex(&mut self) { let mut ctx = self.flex.ctx(); ctx.space.dimensions = self.stack.remaining(); ctx.flex_spacing = flex_spacing(&self.style); self.flex = FlexLayouter::new(ctx); - - Ok(()) } /// Layout a function. @@ -118,12 +122,12 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { ctx.space.dimensions = self.stack.remaining(); ctx.space.padding = SizeBox::zero(); - ctx.space.shrink_to_fit = false; + ctx.space.shrink_to_fit = true; if let Some(space) = ctx.extra_space.as_mut() { - space.dimensions = space.dimensions.unpadded(space.padding); + space.dimensions = space.usable(); space.padding = SizeBox::zero(); - space.shrink_to_fit = false; + space.shrink_to_fit = true; } let commands = func.body.layout(ctx)?; diff --git a/src/library/align.rs b/src/library/align.rs index 4dc5f53c6..f81bae317 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -19,6 +19,7 @@ impl Function for AlignFunc { match ident.as_str() { "left" => Alignment::Left, "right" => Alignment::Right, + "center" => Alignment::Center, s => return err(format!("invalid alignment specifier: '{}'", s)), } } else { @@ -40,6 +41,10 @@ impl Function for AlignFunc { fn layout(&self, mut ctx: LayoutContext) -> LayoutResult { if let Some(body) = &self.body { ctx.space.alignment = self.alignment; + if let Some(space) = ctx.extra_space.as_mut() { + space.alignment = self.alignment; + } + let layouts = layout_tree(body, ctx)?; let mut commands = FuncCommands::new(); diff --git a/tests/layouts/align.typ b/tests/layouts/align.typ new file mode 100644 index 000000000..8cad240ce --- /dev/null +++ b/tests/layouts/align.typ @@ -0,0 +1,9 @@ +{size:150pt*206pt} + +[align: left][Left: {lorem:20}] + +[align: right][Right: {lorem:20}] + +[align: center][Center: {lorem:80}] + +[align: left][Left: {lorem:20}] diff --git a/tests/layouts/pagebreaks.typ b/tests/layouts/pagebreaks.typ index 6887613fd..00684f56d 100644 --- a/tests/layouts/pagebreaks.typ +++ b/tests/layouts/pagebreaks.typ @@ -1,2 +1,2 @@ {size:200pt*200pt} -{lorem:400} +{lorem:300} diff --git a/tests/layouts/shakespeare.typ b/tests/layouts/shakespeare.typ index 09edd3612..19450ce4e 100644 --- a/tests/layouts/shakespeare.typ +++ b/tests/layouts/shakespeare.typ @@ -1,8 +1,3 @@ -// Basic unboxed -{include:shakespeare.tpl} - -// Boxed, but still left-aligned +[align: center][{include:shakespeare.tpl}] [align: left][{include:shakespeare.tpl}] - -// Boxed, and right-aligned [align: right][{include:shakespeare.tpl}]