diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-10-10 23:36:17 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-10-10 23:38:03 +0200 |
| commit | 8f788f9a4f5e970bbe6147987b711470d57aca8d (patch) | |
| tree | cc89008dfdfc62ecf4eb2517d92ec7c7095fa573 /src/layout | |
| parent | 61470fba68e9f19b481034427add5f3d8cfbc0a8 (diff) | |
Add standard `align` function and support right-alignment ➡️
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/boxed.rs | 32 | ||||
| -rw-r--r-- | src/layout/flex.rs | 78 | ||||
| -rw-r--r-- | src/layout/mod.rs | 28 |
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. |
