diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-10-15 18:15:36 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-10-15 18:15:36 +0200 |
| commit | a3c667895e4e5d5673931415397523b9615008d3 (patch) | |
| tree | fc416e7ceeff0fa45f7ddfaaf74cd3354f09048a /src/layout/mod.rs | |
| parent | 0727713424878fed3936d4b6e7cecd4f0c58d888 (diff) | |
Refactor the main layouter ♻
Diffstat (limited to 'src/layout/mod.rs')
| -rw-r--r-- | src/layout/mod.rs | 182 |
1 files changed, 18 insertions, 164 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b760ca1e..031226b9 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::io::{self, Write}; -use std::mem; use toddle::query::{FontClass, SharedFontLoader}; use toddle::Error as FontError; @@ -13,16 +12,23 @@ use crate::style::TextStyle; use crate::syntax::{FuncCall, Node, SyntaxTree}; mod actions; +mod tree; mod flex; mod stacked; mod text; +/// Different kinds of layouters (fully re-exported). +pub mod layouters { + pub use super::tree::layout_tree; + pub use super::flex::{FlexLayouter, FlexContext}; + pub use super::stacked::{StackLayouter, StackContext}; + pub use super::text::{layout_text, TextContext}; +} + pub use actions::{LayoutAction, LayoutActionList}; -pub use flex::{FlexContext, FlexLayouter}; -pub use stacked::{StackContext, StackLayouter}; -pub use text::{layout_text, TextContext}; +pub use layouters::*; -/// A box layout has a fixed width and height and composes of actions. +/// A sequence of layouting actions inside a box. #[derive(Debug, Clone)] pub struct Layout { /// The size of the box. @@ -50,19 +56,19 @@ impl Layout { } } -/// A collection of box layouts. +/// A collection of layouts. #[derive(Debug, Clone)] pub struct MultiLayout { pub layouts: Vec<Layout>, } impl MultiLayout { - /// Create an empty multibox layout. + /// Create an empty multi-layout. pub fn new() -> MultiLayout { MultiLayout { layouts: vec![] } } - /// Extract a single sublayout and panic if this layout does not have + /// Extract the single sublayout. This panics if the layout does not have /// exactly one child. pub fn into_single(mut self) -> Layout { if self.layouts.len() != 1 { @@ -105,8 +111,7 @@ impl<'a> IntoIterator for &'a MultiLayout { } } - -/// The context for layouting. +/// The general context for layouting. #[derive(Copy, Clone)] pub struct LayoutContext<'a, 'p> { pub loader: &'a SharedFontLoader<'p>, @@ -115,7 +120,7 @@ pub struct LayoutContext<'a, 'p> { pub extra_space: Option<LayoutSpace>, } -/// Spacial constraints for layouting. +/// Spacial layouting constraints. #[derive(Debug, Copy, Clone)] pub struct LayoutSpace { /// The maximum size of the box to layout in. @@ -125,12 +130,12 @@ pub struct LayoutSpace { /// The alignment to use for the content. pub alignment: Alignment, /// Whether to shrink the dimensions to fit the content or the keep the - /// original ones. + /// dimensions from the layout space. pub shrink_to_fit: bool, } impl LayoutSpace { - /// The actually usable area. + /// The actually usable area (dimensions minus padding). pub fn usable(&self) -> Size2D { Size2D { x: self.dimensions.x - self.padding.left - self.padding.right, @@ -146,157 +151,6 @@ pub enum Alignment { Right, } -pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout> { - let mut layouter = Layouter::new(ctx); - layouter.layout(tree)?; - layouter.finish() -} - -/// Transforms a syntax tree into a box layout. -struct Layouter<'a, 'p> { - ctx: LayoutContext<'a, 'p>, - stack_layouter: StackLayouter, - flex_layouter: FlexLayouter, - style: Cow<'a, TextStyle>, -} - -impl<'a, 'p> Layouter<'a, 'p> { - /// Create a new layouter. - fn new(ctx: LayoutContext<'a, 'p>) -> Layouter<'a, 'p> { - Layouter { - ctx, - stack_layouter: StackLayouter::new(StackContext { space: ctx.space }), - flex_layouter: FlexLayouter::new(FlexContext { - space: LayoutSpace { - dimensions: ctx.space.usable(), - padding: SizeBox::zero(), - alignment: ctx.space.alignment, - shrink_to_fit: true, - }, - flex_spacing: (ctx.style.line_spacing - 1.0) * Size::pt(ctx.style.font_size), - }), - style: Cow::Borrowed(ctx.style), - } - } - - /// Layout the tree into a box. - fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> { - // Walk all nodes and layout them. - for node in &tree.nodes { - match node { - // Layout a single piece of text. - Node::Text(text) => self.layout_text(text, false)?, - - // Add a space. - Node::Space => { - if !self.flex_layouter.is_empty() { - self.layout_text(" ", true)?; - } - } - - // Finish the current flex layout and add it to the box layouter. - Node::Newline => { - // Finish the current paragraph into a box and add it. - self.layout_flex()?; - - // Add some paragraph spacing. - let size = Size::pt(self.style.font_size) - * (self.style.line_spacing * self.style.paragraph_spacing - 1.0); - self.stack_layouter.add_space(size)?; - } - - // Toggle the text styles. - Node::ToggleItalics => self.style.to_mut().toggle_class(FontClass::Italic), - Node::ToggleBold => self.style.to_mut().toggle_class(FontClass::Bold), - Node::ToggleMonospace => self.style.to_mut().toggle_class(FontClass::Monospace), - - // Execute a function. - Node::Func(func) => self.layout_func(func)?, - } - } - - Ok(()) - } - - fn finish(mut self) -> LayoutResult<MultiLayout> { - // If there are remainings, add them to the layout. - if !self.flex_layouter.is_empty() { - self.layout_flex()?; - } - - Ok(MultiLayout { - layouts: vec![self.stack_layouter.finish()], - }) - } - - /// Layout a piece of text into a box. - fn layout_text(&mut self, text: &str, glue: bool) -> LayoutResult<()> { - let boxed = layout_text( - text, - TextContext { - loader: &self.ctx.loader, - style: &self.style, - }, - )?; - - if glue { - self.flex_layouter.add_glue(boxed); - } else { - self.flex_layouter.add(boxed); - } - - Ok(()) - } - - /// Finish the current flex run and return the resulting box. - fn layout_flex(&mut self) -> LayoutResult<()> { - if self.flex_layouter.is_empty() { - return Ok(()); - } - - let mut layout = FlexLayouter::new(FlexContext { - space: LayoutSpace { - dimensions: self.stack_layouter.ctx().space.usable(), - padding: SizeBox::zero(), - alignment: self.ctx.space.alignment, - shrink_to_fit: true, - }, - flex_spacing: (self.style.line_spacing - 1.0) * Size::pt(self.style.font_size), - }); - mem::swap(&mut layout, &mut self.flex_layouter); - - let boxed = layout.finish()?; - - self.stack_layouter.add(boxed) - } - - /// Layout a function. - fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { - let commands = func.body.layout(LayoutContext { - loader: &self.ctx.loader, - style: &self.style, - space: LayoutSpace { - dimensions: self.stack_layouter.remaining(), - padding: SizeBox::zero(), - alignment: self.ctx.space.alignment, - shrink_to_fit: true, - }, - extra_space: self.ctx.extra_space, - })?; - - for command in commands { - match command { - Command::Layout(tree) => self.layout(tree)?, - Command::Add(layout) => self.stack_layouter.add(layout)?, - Command::AddMany(layouts) => self.stack_layouter.add_many(layouts)?, - Command::ToggleStyleClass(class) => self.style.to_mut().toggle_class(class), - } - } - - Ok(()) - } -} - /// The error type for layouting. pub enum LayoutError { /// There is not enough space to add an item. |
