From 1987e5861cf2c033e3a540a5ef7c0f7106016929 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 17 Oct 2019 12:55:34 +0200 Subject: [PATCH] =?UTF-8?q?Create=20basic=20box=20and=20line-break=20funct?= =?UTF-8?q?ions=20=F0=9F=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/func.rs | 1 + src/layout/flex.rs | 43 +++++++++++++++++++++++++--------------- src/layout/tree.rs | 11 +++++----- src/library/boxed.rs | 29 +++++++++++++++++++++++++++ src/library/breaks.rs | 23 +++++++++++++++++++++ src/library/mod.rs | 13 ++++++++---- tests/layouts/boxes.typ | 10 ++++++++++ tests/layouts/styles.typ | 7 +++++++ 8 files changed, 111 insertions(+), 26 deletions(-) create mode 100644 src/library/boxed.rs create mode 100644 tests/layouts/boxes.typ diff --git a/src/func.rs b/src/func.rs index eaaa476da..7925af2de 100644 --- a/src/func.rs +++ b/src/func.rs @@ -119,6 +119,7 @@ pub enum Command<'a> { SetAlignment(Alignment), SetStyle(TextStyle), FinishLayout, + FinishFlexRun, } macro_rules! commands { diff --git a/src/layout/flex.rs b/src/layout/flex.rs index 39c16aefa..a6f2e0912 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -73,6 +73,8 @@ enum FlexUnit { /// is only present if there was no flow break in between the two /// surrounding boxes. Glue(Size2D), + /// A forced break of the current flex run. + Break, } #[derive(Debug, Clone)] @@ -114,6 +116,11 @@ impl FlexLayouter { self.units.push(FlexUnit::Glue(glue)); } + /// Add a forced line break. + pub fn add_break(&mut self) { + self.units.push(FlexUnit::Break); + } + /// Compute the justified layout. /// /// The layouter is not consumed by this to prevent ownership problems @@ -127,6 +134,7 @@ impl FlexLayouter { match unit { FlexUnit::Boxed(boxed) => self.layout_box(boxed)?, FlexUnit::Glue(glue) => self.layout_glue(glue), + FlexUnit::Break => self.layout_break()?, } } @@ -157,14 +165,12 @@ impl FlexLayouter { } self.finish_run()?; - } else { - // Only add the glue if we did not move to a new line. - self.flush_glue(); } + self.flush_glue(); + let dimensions = boxed.dimensions; self.run.content.push((self.run.size.x, boxed)); - self.grow_run(dimensions); Ok(()) @@ -174,20 +180,12 @@ impl FlexLayouter { self.cached_glue = Some(glue); } - fn flush_glue(&mut self) { - if let Some(glue) = self.cached_glue.take() { - let new_line_width = self.run.size.x + glue.x; - if !self.overflows_line(new_line_width) { - self.grow_run(glue); - } - } - } - - fn grow_run(&mut self, dimensions: Size2D) { - self.run.size.x += dimensions.x; - self.run.size.y = crate::size::max(self.run.size.y, dimensions.y); + fn layout_break(&mut self) -> LayoutResult<()> { + self.cached_glue = None; + self.finish_run() } + /// Finish the current flex run. fn finish_run(&mut self) -> LayoutResult<()> { self.run.size.y += self.ctx.flex_spacing; @@ -208,6 +206,19 @@ impl FlexLayouter { Ok(()) } + fn flush_glue(&mut self) { + if let Some(glue) = self.cached_glue.take() { + if self.run.size.x > Size::zero() && !self.overflows_line(self.run.size.x + glue.x) { + self.grow_run(glue); + } + } + } + + fn grow_run(&mut self, dimensions: Size2D) { + self.run.size.x += dimensions.x; + self.run.size.y = crate::size::max(self.run.size.y, dimensions.y); + } + /// Whether this layouter contains any items. pub fn is_empty(&self) -> bool { self.units.is_empty() diff --git a/src/layout/tree.rs b/src/layout/tree.rs index bd4adb8aa..2c904369f 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -88,6 +88,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { 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_stack = self.stack.clone(); let layouts = self.flex.clone().finish()?; lookahead_stack.add_many(layouts)?; @@ -106,9 +107,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { for command in commands { match command { - Command::Layout(tree) => { - self.layout(tree)?; - } + Command::Layout(tree) => self.layout(tree)?, Command::Add(layout) => { self.finish_flex()?; @@ -130,15 +129,15 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { self.start_new_flex(); } - Command::SetStyle(style) => { - *self.style.to_mut() = style; - } + Command::SetStyle(style) => *self.style.to_mut() = style, Command::FinishLayout => { self.finish_flex()?; self.stack.finish_layout(true)?; self.start_new_flex(); } + + Command::FinishFlexRun => self.flex.add_break(), } } diff --git a/src/library/boxed.rs b/src/library/boxed.rs new file mode 100644 index 000000000..975888f4c --- /dev/null +++ b/src/library/boxed.rs @@ -0,0 +1,29 @@ +use super::prelude::*; + +/// Wraps content into a box. +#[derive(Debug, PartialEq)] +pub struct BoxFunc { + body: SyntaxTree +} + +impl Function for BoxFunc { + fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult + where Self: Sized { + if has_arguments(header) { + return err("pagebreak: expected no arguments"); + } + + if let Some(body) = body { + Ok(BoxFunc { + body: parse(body, ctx)? + }) + } else { + err("box: expected body") + } + } + + fn layout(&self, ctx: LayoutContext) -> LayoutResult { + let layout = layout_tree(&self.body, ctx)?; + Ok(commands![Command::AddMany(layout)]) + } +} diff --git a/src/library/breaks.rs b/src/library/breaks.rs index 22d572f0e..a622350f2 100644 --- a/src/library/breaks.rs +++ b/src/library/breaks.rs @@ -1,5 +1,28 @@ use super::prelude::*; +/// Ends the current line. +#[derive(Debug, PartialEq)] +pub struct LinebreakFunc; + +impl Function for LinebreakFunc { + fn parse(header: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult + where Self: Sized { + if has_arguments(header) { + return err("linebreak: expected no arguments"); + } + + if body.is_some() { + return err("linebreak: expected no body"); + } + + Ok(LinebreakFunc) + } + + fn layout(&self, _: LayoutContext) -> LayoutResult { + Ok(commands![Command::FinishFlexRun]) + } +} + /// Ends the current page. #[derive(Debug, PartialEq)] pub struct PagebreakFunc; diff --git a/src/library/mod.rs b/src/library/mod.rs index 7c54a9f69..d0c987a44 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -3,8 +3,9 @@ use crate::func::Scope; mod align; -mod styles; +mod boxed; mod breaks; +mod styles; /// Useful imports for creating your own functions. pub mod prelude { @@ -17,17 +18,21 @@ pub mod prelude { } pub use align::AlignFunc; -pub use breaks::PagebreakFunc; +pub use boxed::BoxFunc; +pub use breaks::{LinebreakFunc, PagebreakFunc}; pub use styles::{BoldFunc, ItalicFunc, MonospaceFunc}; /// Create a scope with all standard functions. pub fn std() -> Scope { let mut std = Scope::new(); + std.add::("align"); + std.add::("box"); + std.add::("linebreak"); + std.add::("n"); + std.add::("pagebreak"); std.add::("bold"); std.add::("italic"); std.add::("mono"); - std.add::("align"); - std.add::("pagebreak"); std } diff --git a/tests/layouts/boxes.typ b/tests/layouts/boxes.typ new file mode 100644 index 000000000..055e09fe5 --- /dev/null +++ b/tests/layouts/boxes.typ @@ -0,0 +1,10 @@ +{size:400pt*250pt} + +[box][ + *Technical University Berlin* [n] + *Faculty II, Institute for Mathematics* [n] + Secretary Example [n] + Prof. Dr. Example [n] + Assistant #1, Assistant #2, Assistant #3 +] +[align: right][*WiSe 2019/2020* [n] Week 1] diff --git a/tests/layouts/styles.typ b/tests/layouts/styles.typ index b3f671327..c76feb217 100644 --- a/tests/layouts/styles.typ +++ b/tests/layouts/styles.typ @@ -17,3 +17,10 @@ This is not italic anymore, but still bold. [bold] This is completely reset. 😀 + +[box][ + [italic] + Styles are scoped by boxes. +] + +Outside of the box: No effect.