summaryrefslogtreecommitdiff
path: root/src/layout/boxed.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-06-21 21:37:29 +0200
committerLaurenz <laurmaedje@gmail.com>2019-06-21 21:41:02 +0200
commit968e121697a96a2e3b05a560176c34f4bb6693c3 (patch)
treeb937cf208d7a8bfb318227a46e44f91da4ef7a49 /src/layout/boxed.rs
parentb53ad6b1ec8b2fd05566a83c9b895f265e61d281 (diff)
Implement flex and box layouting 📏
Diffstat (limited to 'src/layout/boxed.rs')
-rw-r--r--src/layout/boxed.rs94
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
}
}