diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-06-21 21:37:29 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-06-21 21:41:02 +0200 |
| commit | 968e121697a96a2e3b05a560176c34f4bb6693c3 (patch) | |
| tree | b937cf208d7a8bfb318227a46e44f91da4ef7a49 /src/layout/boxed.rs | |
| parent | b53ad6b1ec8b2fd05566a83c9b895f265e61d281 (diff) | |
Implement flex and box layouting 📏
Diffstat (limited to 'src/layout/boxed.rs')
| -rw-r--r-- | src/layout/boxed.rs | 94 |
1 files changed, 76 insertions, 18 deletions
diff --git a/src/layout/boxed.rs b/src/layout/boxed.rs index b75ea75a..2ee2dbdc 100644 --- a/src/layout/boxed.rs +++ b/src/layout/boxed.rs @@ -1,17 +1,18 @@ -//! Definitive layouting of boxes. +//! Block-style layouting of boxes. use crate::doc::{Document, Page, TextAction}; use crate::font::Font; -use super::{Layouter, LayoutContext, Size2D}; +use crate::size::{Size, Size2D}; +use super::LayoutSpace; -/// A box layout has a fixed width and height and consists of actions. +/// A box layout has a fixed width and height and composes of actions. #[derive(Debug, Clone)] pub struct BoxLayout { /// The size of the box. - dimensions: Size2D, + pub dimensions: Size2D, /// The actions composing this layout. - actions: Vec<TextAction>, + pub actions: Vec<TextAction>, } impl BoxLayout { @@ -28,25 +29,68 @@ impl BoxLayout { } } +/// The context for layouting boxes. +#[derive(Debug, Copy, Clone)] +pub struct BoxContext { + /// The space to layout the boxes in. + pub space: LayoutSpace, +} + /// Layouts boxes block-style. #[derive(Debug)] -pub struct BoxLayouter<'a, 'p> { - ctx: &'a LayoutContext<'a, 'p>, +pub struct BoxLayouter { + ctx: BoxContext, actions: Vec<TextAction>, + dimensions: Size2D, + usable: Size2D, + cursor: Size2D, } -impl<'a, 'p> BoxLayouter<'a, 'p> { +impl BoxLayouter { /// Create a new box layouter. - pub fn new(ctx: &'a LayoutContext<'a, 'p>) -> BoxLayouter<'a, 'p> { + pub fn new(ctx: BoxContext) -> BoxLayouter { + let space = ctx.space; BoxLayouter { ctx, actions: vec![], + dimensions: Size2D::zero(), + usable: space.usable(), + cursor: Size2D::new(space.padding.left, space.padding.right), } } /// Add a sublayout. pub fn add_box(&mut self, layout: BoxLayout) { - unimplemented!() + // In the flow direction (vertical) add the layout and in the second + // direction just consider the maximal size of any child layout. + let new = Size2D { + x: crate::size::max(self.dimensions.x, layout.dimensions.x), + y: self.dimensions.y + layout.dimensions.y, + }; + + if self.overflows(new) { + panic!("box layouter: would overflow in add_box"); + } + + // Apply the dimensions because they fit. + self.dimensions = new; + + // Move all actions into this layout and translate absolute positions. + self.actions.push(TextAction::MoveAbsolute(self.cursor)); + self.actions.extend(super::translate_actions(self.cursor, layout.actions)); + + // Adjust the cursor. + self.cursor.y += layout.dimensions.y; + } + + /// Add some space in between two boxes. + pub fn add_space(&mut self, space: Size) { + if self.overflows(self.dimensions + Size2D::with_y(space)) { + panic!("box layouter: would overflow in add_space"); + } + + self.cursor.y += space; + self.dimensions.y += space; } /// Add a sublayout at an absolute position. @@ -54,20 +98,34 @@ impl<'a, 'p> BoxLayouter<'a, 'p> { self.actions.push(TextAction::MoveAbsolute(position)); self.actions.extend(layout.actions); } -} -impl Layouter for BoxLayouter<'_, '_> { - type Layout = BoxLayout; + /// 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 { + x: self.usable.x, + y: self.usable.y - self.dimensions.y, + } + } /// Finish the layouting and create a box layout from this. - fn finish(self) -> BoxLayout { + pub fn finish(self) -> BoxLayout { BoxLayout { - dimensions: self.ctx.space.dimensions.clone(), - actions: self.actions + dimensions: if self.ctx.space.shrink_to_fit { + self.dimensions.padded(self.ctx.space.padding) + } else { + self.ctx.space.dimensions + }, + actions: self.actions, } } - fn is_empty(&self) -> bool { - self.actions.is_empty() + /// Whether the given box is bigger than what we can hold. + fn overflows(&self, dimensions: Size2D) -> bool { + dimensions.x > self.usable.x || dimensions.y > self.usable.y } } |
