summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/mod.rs307
-rw-r--r--src/layout/stack.rs53
-rw-r--r--src/layout/tree.rs17
3 files changed, 162 insertions, 215 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 363eb14b..58c56dd4 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -13,6 +13,20 @@ mod flex;
mod stack;
mod text;
+/// Common types for layouting.
+pub mod prelude {
+ pub use super::{
+ layout_tree, LayoutResult,
+ MultiLayout, Layout, LayoutContext, LayoutSpaces, LayoutSpace,
+ LayoutExpansion, LayoutAxes, GenericAxis, SpecificAxis, Direction,
+ LayoutAlignment, Alignment, SpacingKind,
+ };
+ pub use GenericAxis::*;
+ pub use SpecificAxis::*;
+ pub use Direction::*;
+ pub use Alignment::*;
+}
+
/// Different kinds of layouters (fully re-exported).
pub mod layouters {
pub use super::tree::layout_tree;
@@ -23,6 +37,7 @@ pub mod layouters {
pub use self::actions::{LayoutAction, LayoutActions};
pub use self::layouters::*;
+pub use self::prelude::*;
/// The result type for layouting.
pub type LayoutResult<T> = crate::TypesetResult<T>;
@@ -56,6 +71,34 @@ impl Layout {
}
}
+/// Layout components that can be serialized.
+pub trait Serialize {
+ /// Serialize the data structure into an output writable.
+ fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()>;
+}
+
+impl Serialize for Layout {
+ fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
+ writeln!(f, "{:.4} {:.4}", self.dimensions.x.to_pt(), self.dimensions.y.to_pt())?;
+ writeln!(f, "{}", self.actions.len())?;
+ for action in &self.actions {
+ action.serialize(f)?;
+ writeln!(f)?;
+ }
+ Ok(())
+ }
+}
+
+impl Serialize for MultiLayout {
+ fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
+ writeln!(f, "{}", self.len())?;
+ for layout in self {
+ layout.serialize(f)?;
+ }
+ Ok(())
+ }
+}
+
/// The general context for layouting.
#[derive(Debug, Clone)]
pub struct LayoutContext<'a, 'p> {
@@ -66,12 +109,16 @@ pub struct LayoutContext<'a, 'p> {
pub style: &'a LayoutStyle,
/// The spaces to layout in.
pub spaces: LayoutSpaces,
+ /// Whether to repeat the last space or quit with an error if more space
+ /// would be needed.
+ pub repeat: bool,
/// The initial axes along which content is laid out.
pub axes: LayoutAxes,
/// The alignment of the finished layout.
pub alignment: LayoutAlignment,
- /// Whether this layouting process handles the top-level pages.
- pub top_level: bool,
+ /// Whether the layout that is to be created will be nested in a parent
+ /// container.
+ pub nested: bool,
/// Whether to debug render a box around the layout.
pub debug: bool,
}
@@ -89,7 +136,7 @@ pub struct LayoutSpace {
/// Whether to expand the dimensions of the resulting layout to the full
/// dimensions of this space or to shrink them to fit the content for the
/// horizontal and vertical axis.
- pub expand: LayoutExpansion,
+ pub expansion: LayoutExpansion,
}
impl LayoutSpace {
@@ -109,7 +156,7 @@ impl LayoutSpace {
LayoutSpace {
dimensions: self.usable(),
padding: SizeBox::ZERO,
- expand: LayoutExpansion::new(false, false),
+ expansion: LayoutExpansion::new(false, false),
}
}
}
@@ -130,195 +177,125 @@ impl LayoutExpansion {
/// The axes along which the content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LayoutAxes {
- pub primary: Axis,
- pub secondary: Axis,
+ pub primary: Direction,
+ pub secondary: Direction,
}
impl LayoutAxes {
- pub fn new(primary: Axis, secondary: Axis) -> LayoutAxes {
- if primary.is_horizontal() == secondary.is_horizontal() {
- panic!("LayoutAxes::new: invalid parallel axes {:?} and {:?}", primary, secondary);
+ pub fn new(primary: Direction, secondary: Direction) -> LayoutAxes {
+ if primary.axis() == secondary.axis() {
+ panic!("LayoutAxes::new: invalid aligned axes {:?} and {:?}",
+ primary, secondary);
}
LayoutAxes { primary, secondary }
}
- /// Return the specified generic axis.
- pub fn generic(&self, axis: GenericAxisKind) -> Axis {
+ /// Return the direction of the specified generic axis.
+ pub fn get_generic(self, axis: GenericAxis) -> Direction {
match axis {
- GenericAxisKind::Primary => self.primary,
- GenericAxisKind::Secondary => self.secondary,
+ Primary => self.primary,
+ Secondary => self.secondary,
}
}
- /// Return the specified specific axis.
- pub fn specific(&self, axis: SpecificAxisKind) -> Axis {
- self.generic(axis.generic(*self))
+ /// Return the direction of the specified specific axis.
+ pub fn get_specific(self, axis: SpecificAxis) -> Direction {
+ self.get_generic(axis.to_generic(self))
}
+}
- /// Returns the generic axis kind which is the horizontal axis.
- pub fn horizontal(&self) -> GenericAxisKind {
- match self.primary.is_horizontal() {
- true => GenericAxisKind::Primary,
- false => GenericAxisKind::Secondary,
- }
- }
+/// The two generic layouting axes.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum GenericAxis {
+ Primary,
+ Secondary,
+}
- /// Returns the generic axis kind which is the vertical axis.
- pub fn vertical(&self) -> GenericAxisKind {
- self.horizontal().inv()
+impl GenericAxis {
+ /// The specific version of this axis in the given system of axes.
+ pub fn to_specific(self, axes: LayoutAxes) -> SpecificAxis {
+ axes.get_generic(self).axis()
}
- /// Returns the specific axis kind which is the primary axis.
- pub fn primary(&self) -> SpecificAxisKind {
- match self.primary.is_horizontal() {
- true => SpecificAxisKind::Horizontal,
- false => SpecificAxisKind::Vertical,
+ /// The other axis.
+ pub fn inv(self) -> GenericAxis {
+ match self {
+ Primary => Secondary,
+ Secondary => Primary,
}
}
+}
- /// Returns the specific axis kind which is the secondary axis.
- pub fn secondary(&self) -> SpecificAxisKind {
- self.primary().inv()
- }
-
- /// Returns the generic alignment corresponding to left-alignment.
- pub fn left(&self) -> Alignment {
- let positive = match self.primary.is_horizontal() {
- true => self.primary.is_positive(),
- false => self.secondary.is_positive(),
- };
-
- if positive { Alignment::Origin } else { Alignment::End }
- }
-
- /// Returns the generic alignment corresponding to right-alignment.
- pub fn right(&self) -> Alignment {
- self.left().inv()
- }
-
- /// Returns the generic alignment corresponding to top-alignment.
- pub fn top(&self) -> Alignment {
- let positive = match self.primary.is_horizontal() {
- true => self.secondary.is_positive(),
- false => self.primary.is_positive(),
- };
+/// The two specific layouting axes.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum SpecificAxis {
+ Horizontal,
+ Vertical,
+}
- if positive { Alignment::Origin } else { Alignment::End }
+impl SpecificAxis {
+ /// The generic version of this axis in the given system of axes.
+ pub fn to_generic(self, axes: LayoutAxes) -> GenericAxis {
+ if self == axes.primary.axis() { Primary } else { Secondary }
}
- /// Returns the generic alignment corresponding to bottom-alignment.
- pub fn bottom(&self) -> Alignment {
- self.top().inv()
- }
-}
-
-impl Default for LayoutAxes {
- fn default() -> LayoutAxes {
- LayoutAxes {
- primary: Axis::LeftToRight,
- secondary: Axis::TopToBottom,
+ /// The other axis.
+ pub fn inv(self) -> SpecificAxis {
+ match self {
+ Horizontal => Vertical,
+ Vertical => Horizontal,
}
}
}
/// Directions along which content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Axis {
+pub enum Direction {
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop,
}
-impl Axis {
- /// Whether this is a horizontal axis.
- pub fn is_horizontal(&self) -> bool {
+impl Direction {
+ /// The specific axis this direction belongs to.
+ pub fn axis(self) -> SpecificAxis {
match self {
- Axis::LeftToRight | Axis::RightToLeft => true,
- Axis::TopToBottom | Axis::BottomToTop => false,
+ LeftToRight | RightToLeft => Horizontal,
+ TopToBottom | BottomToTop => Vertical,
}
}
/// Whether this axis points into the positive coordinate direction.
- pub fn is_positive(&self) -> bool {
+ pub fn is_positive(self) -> bool {
match self {
- Axis::LeftToRight | Axis::TopToBottom => true,
- Axis::RightToLeft | Axis::BottomToTop => false,
+ LeftToRight | TopToBottom => true,
+ RightToLeft | BottomToTop => false,
}
}
/// The inverse axis.
- pub fn inv(&self) -> Axis {
+ pub fn inv(self) -> Direction {
match self {
- Axis::LeftToRight => Axis::RightToLeft,
- Axis::RightToLeft => Axis::LeftToRight,
- Axis::TopToBottom => Axis::BottomToTop,
- Axis::BottomToTop => Axis::TopToBottom,
+ LeftToRight => RightToLeft,
+ RightToLeft => LeftToRight,
+ TopToBottom => BottomToTop,
+ BottomToTop => TopToBottom,
}
}
- /// The direction factor for this axis.
+ /// The factor for this direction.
///
- /// - 1 if the axis is positive.
- /// - -1 if the axis is negative.
- pub fn factor(&self) -> i32 {
+ /// - `1` if the direction is positive.
+ /// - `-1` if the direction is negative.
+ pub fn factor(self) -> i32 {
if self.is_positive() { 1 } else { -1 }
}
}
-/// The two generic kinds of layouting axes.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum GenericAxisKind {
- Primary,
- Secondary,
-}
-
-impl GenericAxisKind {
- /// The specific version of this axis in the given system of axes.
- pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind {
- match self {
- GenericAxisKind::Primary => axes.primary(),
- GenericAxisKind::Secondary => axes.secondary(),
- }
- }
-
- /// The other axis.
- pub fn inv(&self) -> GenericAxisKind {
- match self {
- GenericAxisKind::Primary => GenericAxisKind::Secondary,
- GenericAxisKind::Secondary => GenericAxisKind::Primary,
- }
- }
-}
-
-/// The two specific kinds of layouting axes.
+/// Where to align a layout in a container.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum SpecificAxisKind {
- Horizontal,
- Vertical,
-}
-
-impl SpecificAxisKind {
- /// The generic version of this axis in the given system of axes.
- pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind {
- match self {
- SpecificAxisKind::Horizontal => axes.horizontal(),
- SpecificAxisKind::Vertical => axes.vertical(),
- }
- }
-
- /// The other axis.
- pub fn inv(&self) -> SpecificAxisKind {
- match self {
- SpecificAxisKind::Horizontal => SpecificAxisKind::Vertical,
- SpecificAxisKind::Vertical => SpecificAxisKind::Horizontal,
- }
- }
-}
-
-/// The place to put a layout in a container.
-#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LayoutAlignment {
pub primary: Alignment,
pub secondary: Alignment,
@@ -328,6 +305,14 @@ impl LayoutAlignment {
pub fn new(primary: Alignment, secondary: Alignment) -> LayoutAlignment {
LayoutAlignment { primary, secondary }
}
+
+ /// Return the alignment of the specified generic axis.
+ pub fn get(self, axis: GenericAxis) -> Alignment {
+ match axis {
+ Primary => self.primary,
+ Secondary => self.secondary,
+ }
+ }
}
/// Where to align content.
@@ -340,21 +325,15 @@ pub enum Alignment {
impl Alignment {
/// The inverse alignment.
- pub fn inv(&self) -> Alignment {
+ pub fn inv(self) -> Alignment {
match self {
- Alignment::Origin => Alignment::End,
- Alignment::Center => Alignment::Center,
- Alignment::End => Alignment::Origin,
+ Origin => End,
+ Center => Center,
+ End => Origin,
}
}
}
-impl Default for Alignment {
- fn default() -> Alignment {
- Alignment::Origin
- }
-}
-
/// Whitespace between boxes with different interaction properties.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SpacingKind {
@@ -380,38 +359,10 @@ enum LastSpacing {
impl LastSpacing {
/// The size of the soft space if this is a soft space or zero otherwise.
- fn soft_or_zero(&self) -> Size {
+ fn soft_or_zero(self) -> Size {
match self {
- LastSpacing::Soft(space, _) => *space,
+ LastSpacing::Soft(space, _) => space,
_ => Size::ZERO,
}
}
}
-
-/// Layout components that can be serialized.
-pub trait Serialize {
- /// Serialize the data structure into an output writable.
- fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()>;
-}
-
-impl Serialize for Layout {
- fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
- writeln!(f, "{:.4} {:.4}", self.dimensions.x.to_pt(), self.dimensions.y.to_pt())?;
- writeln!(f, "{}", self.actions.len())?;
- for action in &self.actions {
- action.serialize(f)?;
- writeln!(f)?;
- }
- Ok(())
- }
-}
-
-impl Serialize for MultiLayout {
- fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
- writeln!(f, "{}", self.len())?;
- for layout in self {
- layout.serialize(f)?;
- }
- Ok(())
- }
-}
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index cb1d867e..a2ffdc9b 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -125,13 +125,13 @@ impl StackLayouter {
// A hard space is simply an empty box.
SpacingKind::Hard => {
// Reduce the spacing such that it definitely fits.
- spacing.min_eq(self.space.usable.secondary(self.ctx.axes));
+ spacing.min_eq(self.space.usable.get_secondary(self.ctx.axes));
let dimensions = Size2D::with_y(spacing);
self.update_metrics(dimensions);
self.space.layouts.push((self.ctx.axes, Layout {
dimensions: dimensions.specialized(self.ctx.axes),
- alignment: LayoutAlignment::default(),
+ alignment: LayoutAlignment::new(Origin, Origin),
actions: vec![]
}));
@@ -169,9 +169,9 @@ impl StackLayouter {
}
/// Whether the given alignment is still allowed according to the rulers.
- fn alignment_allowed(&mut self, axis: Axis, alignment: Alignment) -> bool {
- alignment >= *self.space.rulers.get(axis)
- && alignment <= self.space.rulers.get(axis.inv()).inv()
+ fn alignment_allowed(&mut self, direction: Direction, alignment: Alignment) -> bool {
+ alignment >= *self.space.rulers.get(direction)
+ && alignment <= self.space.rulers.get(direction.inv()).inv()
}
/// Update the size metrics to reflect that a layout or spacing with the
@@ -190,7 +190,7 @@ impl StackLayouter {
self.space.size = size.specialized(axes);
self.space.extra = extra.specialized(axes);
- *self.space.usable.secondary_mut(axes) -= dimensions.y;
+ *self.space.usable.get_secondary_mut(axes) -= dimensions.y;
}
/// Change the layouting axes used by this layouter.
@@ -231,7 +231,7 @@ impl StackLayouter {
let mut spaces = smallvec![LayoutSpace {
dimensions,
padding: SizeBox::ZERO,
- expand: LayoutExpansion::new(false, false),
+ expansion: LayoutExpansion::new(false, false),
}];
for space in &self.ctx.spaces[self.next_space()..] {
@@ -243,7 +243,7 @@ impl StackLayouter {
/// The usable size along the primary axis.
pub fn primary_usable(&self) -> Size {
- self.space.usable.primary(self.ctx.axes)
+ self.space.usable.get_primary(self.ctx.axes)
}
/// Whether the current layout space (not subspace) is empty.
@@ -274,8 +274,8 @@ impl StackLayouter {
// expand if necessary.)
let usable = space.usable();
- if space.expand.horizontal { self.space.size.x = usable.x; }
- if space.expand.vertical { self.space.size.y = usable.y; }
+ if space.expansion.horizontal { self.space.size.x = usable.x; }
+ if space.expansion.vertical { self.space.size.y = usable.y; }
let dimensions = self.space.size.padded(space.padding);
@@ -304,8 +304,8 @@ impl StackLayouter {
// layout uses up space from the origin to the end. Thus, it reduces
// the usable space for following layouts at it's origin by its
// extent along the secondary axis.
- *bound.get_mut(*axes, GenericAxisKind::Secondary, Alignment::Origin)
- += axes.secondary.factor() * layout.dimensions.secondary(*axes);
+ *bound.get_mut(*axes, Secondary, Origin)
+ += axes.secondary.factor() * layout.dimensions.get_secondary(*axes);
}
// ------------------------------------------------------------------ //
@@ -315,7 +315,7 @@ impl StackLayouter {
// The `x` field stores the maximal primary extent in one axis-aligned
// run, while the `y` fields stores the accumulated secondary extent.
let mut extent = Size2D::ZERO;
- let mut rotated = false;
+ let mut rotation = Vertical;
for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() {
let (axes, layout) = entry;
@@ -324,17 +324,16 @@ impl StackLayouter {
// (`extent.x`) dictates how much secondary extent the whole run
// had. This value is thus stored in `extent.y`. The primary extent
// is reset for this new axis-aligned run.
- let is_horizontal = axes.secondary.is_horizontal();
- if is_horizontal != rotated {
+ if rotation != axes.secondary.axis() {
extent.y = extent.x;
extent.x = Size::ZERO;
- rotated = is_horizontal;
+ rotation = axes.secondary.axis();
}
// We reduce the bounding box of this layout at it's end by the
// accumulated secondary extent of all layouts we have seen so far,
// which are the layouts after this one since we iterate reversed.
- *bound.get_mut(*axes, GenericAxisKind::Secondary, Alignment::End)
+ *bound.get_mut(*axes, Secondary, End)
-= axes.secondary.factor() * extent.y;
// Then, we add this layout's secondary extent to the accumulator.
@@ -404,10 +403,10 @@ impl Space {
usable,
extra: Size2D::ZERO,
rulers: Rulers {
- top: Alignment::Origin,
- bottom: Alignment::Origin,
- left: Alignment::Origin,
- right: Alignment::Origin,
+ top: Origin,
+ bottom: Origin,
+ left: Origin,
+ right: Origin,
},
last_spacing: LastSpacing::Hard,
}
@@ -415,12 +414,12 @@ impl Space {
}
impl Rulers {
- fn get(&mut self, axis: Axis) -> &mut Alignment {
- match axis {
- Axis::TopToBottom => &mut self.top,
- Axis::BottomToTop => &mut self.bottom,
- Axis::LeftToRight => &mut self.left,
- Axis::RightToLeft => &mut self.right,
+ fn get(&mut self, direction: Direction) -> &mut Alignment {
+ match direction {
+ TopToBottom => &mut self.top,
+ BottomToTop => &mut self.bottom,
+ LeftToRight => &mut self.left,
+ RightToLeft => &mut self.right,
}
}
}
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 9c909d9c..e9d8850d 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -76,13 +76,10 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
- let spaces = self.stack.remaining();
-
let commands = func.0.layout(LayoutContext {
- loader: self.ctx.loader,
style: &self.style,
- spaces,
- top_level: false,
+ spaces: self.stack.remaining(),
+ nested: true,
debug: true,
.. self.ctx
})?;
@@ -103,8 +100,8 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Add(layout) => self.stack.add(layout)?,
AddMultiple(layouts) => self.stack.add_multiple(layouts)?,
AddSpacing(space, kind, axis) => match axis {
- GenericAxisKind::Primary => {},
- GenericAxisKind::Secondary => self.stack.add_spacing(space, kind),
+ Primary => {},
+ Secondary => self.stack.add_spacing(space, kind),
}
FinishLine => {},
@@ -114,8 +111,8 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
SetTextStyle(style) => self.style.text = style,
SetPageStyle(style) => {
- if !self.ctx.top_level {
- error!("the page style cannot only be altered from a top-level context");
+ if self.ctx.nested {
+ error!("page style cannot be altered in nested context");
}
self.style.page = style;
@@ -123,7 +120,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
LayoutSpace {
dimensions: style.dimensions,
padding: style.margins,
- expand: LayoutExpansion::new(true, true),
+ expansion: LayoutExpansion::new(true, true),
}
], true);
}