diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-10-17 12:55:34 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-10-17 12:55:34 +0200 |
| commit | 1987e5861cf2c033e3a540a5ef7c0f7106016929 (patch) | |
| tree | dcbf2d32c88d394e63b60e7473b2f6ba79fc83e3 /src | |
| parent | f22f9513aea21408ebf6febd01912e630e9ad5e6 (diff) | |
Create basic box and line-break functions 📦
Diffstat (limited to 'src')
| -rw-r--r-- | src/func.rs | 1 | ||||
| -rw-r--r-- | src/layout/flex.rs | 43 | ||||
| -rw-r--r-- | src/layout/tree.rs | 11 | ||||
| -rw-r--r-- | src/library/boxed.rs | 29 | ||||
| -rw-r--r-- | src/library/breaks.rs | 23 | ||||
| -rw-r--r-- | src/library/mod.rs | 13 |
6 files changed, 94 insertions, 26 deletions
diff --git a/src/func.rs b/src/func.rs index eaaa476d..7925af2d 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 39c16aef..a6f2e091 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 bd4adb8a..2c904369 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 00000000..975888f4 --- /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<Self> + 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<CommandList> { + 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 22d572f0..a622350f 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<Self> + 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<CommandList> { + 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 7c54a9f6..d0c987a4 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::<AlignFunc>("align"); + std.add::<BoxFunc>("box"); + std.add::<LinebreakFunc>("linebreak"); + std.add::<LinebreakFunc>("n"); + std.add::<PagebreakFunc>("pagebreak"); std.add::<BoldFunc>("bold"); std.add::<ItalicFunc>("italic"); std.add::<MonospaceFunc>("mono"); - std.add::<AlignFunc>("align"); - std.add::<PagebreakFunc>("pagebreak"); std } |
