summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-10-10 23:36:17 +0200
committerLaurenz <laurmaedje@gmail.com>2019-10-10 23:38:03 +0200
commit8f788f9a4f5e970bbe6147987b711470d57aca8d (patch)
treecc89008dfdfc62ecf4eb2517d92ec7c7095fa573 /src/layout
parent61470fba68e9f19b481034427add5f3d8cfbc0a8 (diff)
Add standard `align` function and support right-alignment ➡️
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/boxed.rs32
-rw-r--r--src/layout/flex.rs78
-rw-r--r--src/layout/mod.rs28
3 files changed, 90 insertions, 48 deletions
diff --git a/src/layout/boxed.rs b/src/layout/boxed.rs
index a860b267..afcd5278 100644
--- a/src/layout/boxed.rs
+++ b/src/layout/boxed.rs
@@ -2,7 +2,7 @@
use crate::doc::{Document, Page, LayoutAction};
use crate::size::{Size, Size2D};
-use super::{ActionList, LayoutSpace, LayoutResult, LayoutError};
+use super::{ActionList, LayoutSpace, Alignment, LayoutResult, LayoutError};
/// A box layout has a fixed width and height and composes of actions.
@@ -37,7 +37,7 @@ pub struct BoxContext {
/// Layouts boxes block-style.
#[derive(Debug)]
pub struct BoxLayouter {
- ctx: BoxContext,
+ pub ctx: BoxContext,
actions: ActionList,
dimensions: Size2D,
usable: Size2D,
@@ -51,9 +51,15 @@ impl BoxLayouter {
BoxLayouter {
ctx,
actions: ActionList::new(),
- dimensions: Size2D::zero(),
+ dimensions: match ctx.space.alignment {
+ Alignment::Left => Size2D::zero(),
+ Alignment::Right => Size2D::with_x(space.usable().x),
+ },
usable: space.usable(),
- cursor: Size2D::new(space.padding.left, space.padding.right),
+ cursor: Size2D::new(match ctx.space.alignment {
+ Alignment::Left => space.padding.left,
+ Alignment::Right => space.dimensions.x - space.padding.right,
+ }, space.padding.top),
}
}
@@ -71,12 +77,18 @@ impl BoxLayouter {
return Err(LayoutError::NotEnoughSpace);
}
- // Apply the dimensions as they fit.
- let height = layout.dimensions.y;
+ // Apply the dimensions if they fit.
self.dimensions = new;
+ let width = layout.dimensions.x;
+ let height = layout.dimensions.y;
+
+ let position = match self.ctx.space.alignment {
+ Alignment::Left => self.cursor,
+ Alignment::Right => self.cursor - Size2D::with_x(width),
+ };
// Add the box.
- self.add_box_absolute(self.cursor, layout);
+ self.add_box_absolute(position, layout);
// Adjust the cursor.
self.cursor.y += height;
@@ -86,11 +98,7 @@ impl BoxLayouter {
/// 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(LayoutAction::MoveAbsolute(position));
- self.actions.set_origin(position);
- self.actions.extend(layout.actions);
+ self.actions.add_box_absolute(position, layout);
}
/// Add some space in between two boxes.
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
index 730b876e..8b692691 100644
--- a/src/layout/flex.rs
+++ b/src/layout/flex.rs
@@ -1,8 +1,7 @@
//! Flexible and lazy layouting of boxes.
-use crate::doc::LayoutAction;
use crate::size::{Size, Size2D};
-use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError};
+use super::{BoxLayout, ActionList, LayoutSpace, Alignment, LayoutResult, LayoutError};
/// A flex layout consists of a yet unarranged list of boxes.
@@ -81,7 +80,9 @@ struct FlexFinisher {
dimensions: Size2D,
usable: Size2D,
cursor: Size2D,
- line: Size2D,
+ line_metrics: Size2D,
+ line_content: Vec<(Size2D, BoxLayout)>,
+ glue: Option<BoxLayout>,
}
impl FlexFinisher {
@@ -92,10 +93,15 @@ impl FlexFinisher {
units: layout.units,
ctx,
actions: ActionList::new(),
- dimensions: Size2D::zero(),
+ dimensions: match ctx.space.alignment {
+ Alignment::Left => Size2D::zero(),
+ Alignment::Right => Size2D::with_x(space.usable().x),
+ },
usable: space.usable(),
cursor: Size2D::new(space.padding.left, space.padding.top),
- line: Size2D::zero(),
+ line_metrics: Size2D::zero(),
+ line_content: vec![],
+ glue: None,
}
}
@@ -128,14 +134,20 @@ impl FlexFinisher {
/// Layout the box.
fn boxed(&mut self, boxed: BoxLayout) -> LayoutResult<()> {
+ let last_glue_x = self.glue.as_ref()
+ .map(|g| g.dimensions.x)
+ .unwrap_or(Size::zero());
+
// Move to the next line if necessary.
- if self.line.x + boxed.dimensions.x > self.usable.x {
+ if self.line_metrics.x + boxed.dimensions.x + last_glue_x > self.usable.x {
// If it still does not fit, we stand no chance.
if boxed.dimensions.x > self.usable.x {
return Err(LayoutError::NotEnoughSpace);
}
self.newline();
+ } else if let Some(glue) = self.glue.take() {
+ self.append(glue);
}
self.append(boxed);
@@ -145,37 +157,51 @@ impl FlexFinisher {
/// Layout the glue.
fn glue(&mut self, glue: BoxLayout) {
- // Only add the glue if it fits on the line, otherwise move to the next line.
- if self.line.x + glue.dimensions.x > self.usable.x {
- self.newline();
- } else {
- self.append(glue);
- }
+ self.glue = Some(glue);
}
/// Append a box to the layout without checking anything.
fn append(&mut self, layout: BoxLayout) {
- // Move all actions into this layout and translate absolute positions.
- self.actions.reset_origin();
- self.actions.add(LayoutAction::MoveAbsolute(self.cursor));
- self.actions.set_origin(self.cursor);
- self.actions.extend(layout.actions);
-
- // Adjust the sizes.
- self.line.x += layout.dimensions.x;
- self.line.y = crate::size::max(self.line.y, layout.dimensions.y);
- self.cursor.x += layout.dimensions.x;
+ let dim = layout.dimensions;
+ self.line_content.push((self.cursor, layout));
+
+ self.line_metrics.x += dim.x;
+ self.line_metrics.y = crate::size::max(self.line_metrics.y, dim.y);
+ self.cursor.x += dim.x;
}
/// Move to the next line.
fn newline(&mut self) {
- self.dimensions.x = crate::size::max(self.dimensions.x, self.line.x);
+ // Move all actions into this layout and translate absolute positions.
+ let remaining_space = Size2D::with_x(self.ctx.space.usable().x - self.line_metrics.x);
+ for (cursor, layout) in self.line_content.drain(..) {
+ let position = match self.ctx.space.alignment {
+ Alignment::Left => cursor,
+ Alignment::Right => {
+ // Right align everything by shifting it right by the
+ // amount of space left to the right of the line.
+ cursor + remaining_space
+ },
+ };
+
+ self.actions.add_box_absolute(position, layout);
+ }
+
+ // Stretch the dimensions to at least the line width.
+ self.dimensions.x = crate::size::max(self.dimensions.x, self.line_metrics.x);
+
+ // If we wrote a line previously add the inter-line spacing.
if self.dimensions.y > Size::zero() {
self.dimensions.y += self.ctx.flex_spacing;
}
- self.dimensions.y += self.line.y;
+
+ self.dimensions.y += self.line_metrics.y;
+
+ // Reset the cursor the left and move down by the line and the inter-line spacing.
self.cursor.x = self.ctx.space.padding.left;
- self.cursor.y += self.line.y + self.ctx.flex_spacing;
- self.line = Size2D::zero();
+ self.cursor.y += self.line_metrics.y + self.ctx.flex_spacing;
+
+ // Reset the current line metrics.
+ self.line_metrics = Size2D::zero();
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 6b040089..e5fdc42d 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -52,11 +52,20 @@ pub struct LayoutSpace {
pub dimensions: Size2D,
/// Padding that should be respected on each side.
pub padding: SizeBox,
+ /// 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.
pub shrink_to_fit: bool,
}
+/// Where to align content.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Alignment {
+ Left,
+ Right,
+}
+
impl LayoutSpace {
/// The actually usable area.
pub fn usable(&self) -> Size2D {
@@ -157,6 +166,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
space: LayoutSpace {
dimensions: self.box_layouter.remaining(),
padding: SizeBox::zero(),
+ alignment: self.box_layouter.ctx.space.alignment,
shrink_to_fit: true,
},
flex_spacing: (self.style.line_spacing - 1.0) * Size::pt(self.style.font_size),
@@ -173,6 +183,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
space: LayoutSpace {
dimensions: self.box_layouter.remaining(),
padding: SizeBox::zero(),
+ alignment: self.box_layouter.ctx.space.alignment,
shrink_to_fit: true,
},
})?;
@@ -196,8 +207,8 @@ impl<'a, 'p> Layouter<'a, 'p> {
/// Manipulates and optimizes a list of actions.
#[derive(Debug, Clone)]
pub struct ActionList {
+ pub origin: Size2D,
actions: Vec<LayoutAction>,
- origin: Size2D,
active_font: (usize, f32),
}
@@ -232,15 +243,12 @@ impl ActionList {
}
}
- /// Move the origin for the upcomming actions. Absolute moves will be
- /// changed by that origin.
- pub fn set_origin(&mut self, origin: Size2D) {
- self.origin = origin;
- }
-
- /// Reset the origin to zero.
- pub fn reset_origin(&mut self) {
- self.origin = Size2D::zero();
+ /// Add all actions from a box layout at a position. A move to the position
+ /// is generated and all moves inside the box layout are translated as necessary.
+ pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) {
+ self.actions.push(LayoutAction::MoveAbsolute(position));
+ self.origin = position;
+ self.extend(layout.actions);
}
/// Whether there are any actions in this list.