summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-10-17 12:55:34 +0200
committerLaurenz <laurmaedje@gmail.com>2019-10-17 12:55:34 +0200
commit1987e5861cf2c033e3a540a5ef7c0f7106016929 (patch)
treedcbf2d32c88d394e63b60e7473b2f6ba79fc83e3 /src
parentf22f9513aea21408ebf6febd01912e630e9ad5e6 (diff)
Create basic box and line-break functions 📦
Diffstat (limited to 'src')
-rw-r--r--src/func.rs1
-rw-r--r--src/layout/flex.rs43
-rw-r--r--src/layout/tree.rs11
-rw-r--r--src/library/boxed.rs29
-rw-r--r--src/library/breaks.rs23
-rw-r--r--src/library/mod.rs13
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
}