diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-06-22 12:25:01 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-06-22 12:25:01 +0200 |
| commit | f6fe3b5cdd2805f3975985752f9cb0e04e3daf49 (patch) | |
| tree | daddf46ba6add0b7cead1f9015b8f24364672138 /src/layout | |
| parent | e39a6efccff7ba2b5dd546ddf86f23d2714161e7 (diff) | |
Implement function layouting ✒
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/boxed.rs | 28 | ||||
| -rw-r--r-- | src/layout/flex.rs | 36 | ||||
| -rw-r--r-- | src/layout/mod.rs | 58 |
3 files changed, 83 insertions, 39 deletions
diff --git a/src/layout/boxed.rs b/src/layout/boxed.rs index 89c641bc..e712f650 100644 --- a/src/layout/boxed.rs +++ b/src/layout/boxed.rs @@ -86,6 +86,15 @@ impl BoxLayouter { Ok(()) } + /// Add a sublayout at an absolute position. + pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) { + // Move all actions into this layout and translate absolute positions. + self.actions.reset_origin(); + self.actions.add(TextAction::MoveAbsolute(position)); + self.actions.set_origin(position); + self.actions.extend(layout.actions); + } + /// Add some space in between two boxes. pub fn add_space(&mut self, space: Size) -> LayoutResult<()> { // Check whether this space fits. @@ -100,20 +109,6 @@ impl BoxLayouter { Ok(()) } - /// Add a sublayout at an absolute position. - pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) { - // Move all actions into this layout and translate absolute positions. - self.actions.reset_origin(); - self.actions.add(TextAction::MoveAbsolute(position)); - self.actions.set_origin(position); - self.actions.extend(layout.actions); - } - - /// Whether this layouter contains any items. - pub fn is_empty(&self) -> bool { - self.actions.is_empty() - } - /// The remaining space for new boxes. pub fn remaining(&self) -> Size2D { Size2D { @@ -122,6 +117,11 @@ impl BoxLayouter { } } + /// Whether this layouter contains any items. + pub fn is_empty(&self) -> bool { + self.actions.is_empty() + } + /// Finish the layouting and create a box layout from this. pub fn finish(self) -> BoxLayout { BoxLayout { diff --git a/src/layout/flex.rs b/src/layout/flex.rs index 5fd7b157..f966eae2 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -1,7 +1,7 @@ //! Flexible and lazy layouting of boxes. use crate::doc::TextAction; -use crate::size::Size2D; +use crate::size::{Size, Size2D}; use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError}; @@ -10,8 +10,6 @@ use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError}; pub struct FlexLayout { /// The sublayouts composing this layout. pub units: Vec<FlexUnit>, - /// The layout space to arrange in. - pub ctx: FlexContext, } /// A unit in a flex layout. @@ -25,14 +23,20 @@ pub enum FlexUnit { } impl FlexLayout { - /// Create a new flex layouter. - pub fn new(ctx: FlexContext) -> FlexLayout { + /// Create a new flex layout. + pub fn new() -> FlexLayout { FlexLayout { - ctx, units: vec![], } } + /// Create a new flex layout containing just one box. + pub fn from_box(boxed: BoxLayout) -> FlexLayout { + FlexLayout { + units: vec![FlexUnit::Boxed(boxed)], + } + } + /// Add a sublayout. pub fn add_box(&mut self, layout: BoxLayout) { self.units.push(FlexUnit::Boxed(layout)); @@ -54,8 +58,8 @@ impl FlexLayout { } /// Compute the justified layout. - pub fn into_box(self) -> LayoutResult<BoxLayout> { - FlexFinisher::new(self).finish() + pub fn finish(self, ctx: FlexContext) -> LayoutResult<BoxLayout> { + FlexFinisher::new(self, ctx).finish() } } @@ -64,8 +68,8 @@ impl FlexLayout { pub struct FlexContext { /// The space to layout the boxes in. pub space: LayoutSpace, - /// The flex spacing (like line spacing). - pub flex_spacing: f32, + /// The flex spacing between two lines of boxes. + pub flex_spacing: Size, } /// Finishes a flex layout by justifying the positions of the individual boxes. @@ -82,11 +86,11 @@ struct FlexFinisher { impl FlexFinisher { /// Create the finisher from the layout. - fn new(layout: FlexLayout) -> FlexFinisher { - let space = layout.ctx.space; + fn new(layout: FlexLayout, ctx: FlexContext) -> FlexFinisher { + let space = ctx.space; FlexFinisher { units: layout.units, - ctx: layout.ctx, + ctx, actions: ActionList::new(), dimensions: Size2D::zero(), usable: space.usable(), @@ -165,11 +169,13 @@ impl FlexFinisher { /// Move to the next line. fn newline(&mut self) { - self.line.y *= self.ctx.flex_spacing; self.dimensions.x = crate::size::max(self.dimensions.x, self.line.x); + if self.dimensions.y > Size::zero() { + self.dimensions.y += self.ctx.flex_spacing; + } self.dimensions.y += self.line.y; self.cursor.x = self.ctx.space.padding.left; - self.cursor.y += self.line.y; + self.cursor.y += self.line.y + self.ctx.flex_spacing; self.line = Size2D::zero(); } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index f8819d0b..40948a13 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -70,6 +70,7 @@ struct Layouter<'a, 'p> { flex_layout: FlexLayout, flex_ctx: FlexContext, text_ctx: TextContext<'a, 'p>, + func_ctx: LayoutContext<'a, 'p>, } impl<'a, 'p> Layouter<'a, 'p> { @@ -85,7 +86,7 @@ impl<'a, 'p> Layouter<'a, 'p> { padding: SizeBox::zero(), shrink_to_fit: true, }, - flex_spacing: ctx.style.line_spacing, + flex_spacing: (ctx.style.line_spacing - 1.0) * Size::points(ctx.style.font_size), }; // The mutable context for layouting single pieces of text. @@ -94,12 +95,24 @@ impl<'a, 'p> Layouter<'a, 'p> { style: ctx.style.clone(), }; + // The mutable context for layouting single functions. + let func_ctx = LayoutContext { + loader: &ctx.loader, + style: ctx.style.clone(), + space: LayoutSpace { + dimensions: ctx.space.usable(), + padding: SizeBox::zero(), + shrink_to_fit: true, + }, + }; + Layouter { tree, box_layouter: BoxLayouter::new(box_ctx), - flex_layout: FlexLayout::new(flex_ctx), + flex_layout: FlexLayout::new(), flex_ctx, text_ctx, + func_ctx, } } @@ -124,28 +137,53 @@ impl<'a, 'p> Layouter<'a, 'p> { // Then start a new flex layouting process. Node::Newline => { // Finish the current paragraph into a box and add it. - self.add_paragraph_spacing()?; - let boxed = self.flex_layout.into_box()?; + let boxed = self.flex_layout.finish(self.flex_ctx)?; self.box_layouter.add_box(boxed)?; // Create a fresh flex layout for the next paragraph. self.flex_ctx.space.dimensions = self.box_layouter.remaining(); - self.flex_layout = FlexLayout::new(self.flex_ctx); + self.flex_layout = FlexLayout::new(); + self.add_paragraph_spacing()?; }, // Toggle the text styles. - Node::ToggleItalics => self.text_ctx.style.italic = !self.text_ctx.style.italic, - Node::ToggleBold => self.text_ctx.style.bold = !self.text_ctx.style.bold, + Node::ToggleItalics => { + self.text_ctx.style.italic = !self.text_ctx.style.italic; + self.func_ctx.style.italic = !self.func_ctx.style.italic; + }, + Node::ToggleBold => { + self.text_ctx.style.bold = !self.text_ctx.style.bold; + self.func_ctx.style.bold = !self.func_ctx.style.bold; + }, // Execute a function. - Node::Func(_) => unimplemented!(), + Node::Func(func) => { + self.func_ctx.space.dimensions = self.box_layouter.remaining(); + let layout = func.body.layout(&self.func_ctx)?; + + // Add the potential layout. + if let Some(layout) = layout { + match layout { + Layout::Boxed(boxed) => { + // Finish the previous flex run before adding the box. + let previous = self.flex_layout.finish(self.flex_ctx)?; + self.box_layouter.add_box(previous)?; + self.box_layouter.add_box(boxed)?; + + // Create a fresh flex layout for the following content. + self.flex_ctx.space.dimensions = self.box_layouter.remaining(); + self.flex_layout = FlexLayout::new(); + }, + Layout::Flex(flex) => self.flex_layout.add_flexible(flex), + } + } + }, } } // If there are remainings, add them to the layout. if !self.flex_layout.is_empty() { - self.add_paragraph_spacing()?; - let boxed = self.flex_layout.into_box()?; + let boxed = self.flex_layout.finish(self.flex_ctx)?; self.box_layouter.add_box(boxed)?; } |
