diff --git a/src/func/mod.rs b/src/func/mod.rs index c8cf23c65..a5328314c 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -12,13 +12,7 @@ mod macros; /// Useful imports for creating your own functions. pub mod prelude { pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands}; - pub use crate::layout::{ - layout_tree, Layout, MultiLayout, - LayoutContext, LayoutSpace, LayoutSpaces, LayoutExpansion, - LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind, - LayoutAlignment, Alignment, - SpacingKind, LayoutResult, - }; + pub use crate::layout::prelude::*; pub use crate::syntax::{ parse, ParseContext, ParseResult, SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg, @@ -107,7 +101,7 @@ pub enum Command<'a> { Add(Layout), AddMultiple(MultiLayout), - AddSpacing(Size, SpacingKind, GenericAxisKind), + AddSpacing(Size, SpacingKind, GenericAxis), FinishLine, FinishRun, @@ -149,13 +143,13 @@ impl Scope { /// Associate the given name with a type that is parseable into a function. pub fn add(&mut self, name: &str) where F: ParseFunc + LayoutFunc + 'static { - self.add_with_metadata::(name, ()); + self.add_with_metadata::(name, ()); } /// Add a parseable type with additional metadata that is given to the /// parser (other than the default of `()`). - pub fn add_with_metadata(&mut self, name: &str, metadata: T) - where F: ParseFunc + LayoutFunc + 'static, T: 'static + Clone { + pub fn add_with_metadata(&mut self, name: &str, metadata: ::Meta) + where F: ParseFunc + LayoutFunc + 'static { self.parsers.insert( name.to_owned(), Box::new(move |a, b, c| { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 363eb14bc..58c56dd4a 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 = crate::TypesetResult; @@ -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(&self, f: &mut W) -> io::Result<()>; +} + +impl Serialize for Layout { + fn serialize(&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(&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)) - } - - /// 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, - } - } - - /// Returns the generic axis kind which is the vertical axis. - pub fn vertical(&self) -> GenericAxisKind { - self.horizontal().inv() - } - - /// 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, - } - } - - /// 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(), - }; - - if positive { Alignment::Origin } else { Alignment::End } - } - - /// Returns the generic alignment corresponding to bottom-alignment. - pub fn bottom(&self) -> Alignment { - self.top().inv() + /// Return the direction of the specified specific axis. + pub fn get_specific(self, axis: SpecificAxis) -> Direction { + self.get_generic(axis.to_generic(self)) } } -impl Default for LayoutAxes { - fn default() -> LayoutAxes { - LayoutAxes { - primary: Axis::LeftToRight, - secondary: Axis::TopToBottom, +/// The two generic layouting axes. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum GenericAxis { + Primary, + Secondary, +} + +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() + } + + /// The other axis. + pub fn inv(self) -> GenericAxis { + match self { + Primary => Secondary, + Secondary => Primary, + } + } +} + +/// The two specific layouting axes. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum SpecificAxis { + Horizontal, + Vertical, +} + +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 } + } + + /// 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. +/// Where to align a layout in a container. #[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. -#[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(&self, f: &mut W) -> io::Result<()>; -} - -impl Serialize for Layout { - fn serialize(&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(&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 cb1d867ee..a2ffdc9be 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 9c909d9c4..e9d8850df 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); } diff --git a/src/lib.rs b/src/lib.rs index 8e5d1441f..922ff76e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ impl<'p> Typesetter<'p> { /// Layout a syntax tree and return the produced layout. pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult { + use crate::layout::prelude::*; Ok(layout_tree( &tree, LayoutContext { @@ -98,12 +99,13 @@ impl<'p> Typesetter<'p> { style: &self.style, spaces: smallvec![LayoutSpace { dimensions: self.style.page.dimensions, - expand: LayoutExpansion::new(true, true), padding: self.style.page.margins, + expansion: LayoutExpansion::new(true, true), }], - axes: LayoutAxes::default(), - alignment: LayoutAlignment::default(), - top_level: true, + repeat: true, + axes: LayoutAxes::new(LeftToRight, TopToBottom), + alignment: LayoutAlignment::new(Origin, Origin), + nested: false, debug: false, }, )?) diff --git a/src/library/align.rs b/src/library/align.rs index 3b06fe2c3..616ede6b3 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -34,20 +34,20 @@ function! { let map = self.map.dedup(|key, alignment| { let axis = match key { - Key::First => alignment.axis(axes, GenericAxisKind::Primary), - Key::Second => alignment.axis(axes, GenericAxisKind::Secondary), - Key::Axis(AxisKey::Primary) => GenericAxisKind::Primary, - Key::Axis(AxisKey::Secondary) => GenericAxisKind::Secondary, - Key::Axis(AxisKey::Horizontal) => axes.horizontal(), - Key::Axis(AxisKey::Vertical) => axes.vertical(), + Key::First => alignment.axis(axes, Primary), + Key::Second => alignment.axis(axes, Secondary), + Key::Axis(AxisKey::Primary) => Primary, + Key::Axis(AxisKey::Secondary) => Secondary, + Key::Axis(AxisKey::Horizontal) => Horizontal.to_generic(axes), + Key::Axis(AxisKey::Vertical) => Vertical.to_generic(axes), }; - let alignment = alignment.generic(axes, axis)?; + let alignment = alignment.to_generic(axes, axis)?; Ok((axis, alignment)) })?; - map.with(GenericAxisKind::Primary, |&val| ctx.alignment.primary = val); - map.with(GenericAxisKind::Secondary, |&val| ctx.alignment.secondary = val); + map.with(Primary, |&val| ctx.alignment.primary = val); + map.with(Secondary, |&val| ctx.alignment.secondary = val); match &self.body { Some(body) => vec![AddMultiple(layout_tree(&body, ctx)?)], diff --git a/src/library/boxed.rs b/src/library/boxed.rs index 0428e746e..c205eec66 100644 --- a/src/library/boxed.rs +++ b/src/library/boxed.rs @@ -21,15 +21,13 @@ function! { } layout(self, mut ctx) { - use SpecificAxisKind::*; - ctx.debug = self.debug; let space = &mut ctx.spaces[0]; self.map.apply_with(ctx.axes, |axis, p| { let entity = match axis { - Horizontal => { space.expand.horizontal = true; &mut space.dimensions.x }, - Vertical => { space.expand.vertical = true; &mut space.dimensions.y }, + Horizontal => { space.expansion.horizontal = true; &mut space.dimensions.x }, + Vertical => { space.expansion.vertical = true; &mut space.dimensions.y }, }; *entity = p.concretize(*entity) diff --git a/src/library/direction.rs b/src/library/direction.rs index 7edad323a..ac1fac08f 100644 --- a/src/library/direction.rs +++ b/src/library/direction.rs @@ -5,25 +5,25 @@ use super::keys::AxisKey; function! { /// `direction`: Sets the directions of the layouting axes. #[derive(Debug, PartialEq)] - pub struct Direction { + pub struct DirectionChange { body: Option, - map: ConsistentMap, + map: ConsistentMap, } parse(args, body, ctx) { let mut map = ConsistentMap::new(); - map.add_opt_span(AxisKey::Primary, args.get_pos_opt::()?)?; - map.add_opt_span(AxisKey::Secondary, args.get_pos_opt::()?)?; + map.add_opt_span(AxisKey::Primary, args.get_pos_opt::()?)?; + map.add_opt_span(AxisKey::Secondary, args.get_pos_opt::()?)?; for arg in args.keys() { let axis = AxisKey::from_ident(&arg.v.key)?; - let value = Axis::from_expr(arg.v.value)?; + let value = Direction::from_expr(arg.v.value)?; map.add(axis, value)?; } - Direction { + DirectionChange { body: parse!(optional: body, ctx), map, } @@ -34,17 +34,17 @@ function! { let map = self.map.dedup(|key, &direction| { Ok((match key { - AxisKey::Primary => GenericAxisKind::Primary, - AxisKey::Secondary => GenericAxisKind::Secondary, - AxisKey::Horizontal => axes.horizontal(), - AxisKey::Vertical => axes.vertical(), + AxisKey::Primary => Primary, + AxisKey::Secondary => Secondary, + AxisKey::Horizontal => Horizontal.to_generic(axes), + AxisKey::Vertical => Vertical.to_generic(axes), }, direction)) })?; - map.with(GenericAxisKind::Primary, |&val| ctx.axes.primary = val); - map.with(GenericAxisKind::Secondary, |&val| ctx.axes.secondary = val); + map.with(Primary, |&val| ctx.axes.primary = val); + map.with(Secondary, |&val| ctx.axes.secondary = val); - if ctx.axes.primary.is_horizontal() == ctx.axes.secondary.is_horizontal() { + if ctx.axes.primary.axis() == ctx.axes.secondary.axis() { error!( "aligned primary and secondary axes: `{}`, `{}`", format!("{:?}", ctx.axes.primary).to_lowercase(), diff --git a/src/library/keys.rs b/src/library/keys.rs index 969d92be8..bee45638d 100644 --- a/src/library/keys.rs +++ b/src/library/keys.rs @@ -39,33 +39,26 @@ pub enum AxisKey { impl AxisKey { /// The generic version of this axis key in the given system of axes. - pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind { + pub fn to_generic(self, axes: LayoutAxes) -> GenericAxis { match self { - AxisKey::Primary => GenericAxisKind::Primary, - AxisKey::Secondary => GenericAxisKind::Secondary, - AxisKey::Vertical => axes.vertical(), - AxisKey::Horizontal => axes.horizontal(), + AxisKey::Primary => Primary, + AxisKey::Secondary => Secondary, + AxisKey::Vertical => Vertical.to_generic(axes), + AxisKey::Horizontal => Horizontal.to_generic(axes), } } /// The specific version of this axis key in the given system of axes. - pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind { + pub fn to_specific(self, axes: LayoutAxes) -> SpecificAxis { match self { - AxisKey::Primary => axes.primary(), - AxisKey::Secondary => axes.secondary(), - AxisKey::Vertical => SpecificAxisKind::Vertical, - AxisKey::Horizontal => SpecificAxisKind::Horizontal, + AxisKey::Primary => Primary.to_specific(axes), + AxisKey::Secondary => Secondary.to_specific(axes), + AxisKey::Vertical => Vertical, + AxisKey::Horizontal => Horizontal, } } } -kind!(AxisKey, "axis", - "horizontal" => AxisKey::Horizontal, - "vertical" => AxisKey::Vertical, - "primary" => AxisKey::Primary, - "secondary" => AxisKey::Secondary, -); - /// An argument key which describes a target alignment. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum AlignmentKey { @@ -81,29 +74,35 @@ pub enum AlignmentKey { impl AlignmentKey { /// The generic axis this alignment key corresopnds to in the given system /// of layouting axes. Falls back to `default` if the alignment is generic. - pub fn axis(&self, axes: LayoutAxes, default: GenericAxisKind) -> GenericAxisKind { + pub fn axis(self, axes: LayoutAxes, default: GenericAxis) -> GenericAxis { use AlignmentKey::*; match self { Origin | Center | End => default, - Left | Right => axes.horizontal(), - Top | Bottom => axes.vertical(), + Left | Right => Horizontal.to_generic(axes), + Top | Bottom => Vertical.to_generic(axes), } } /// The generic version of this alignment in the given system of layouting - /// axes. Returns an error if the alignment is invalid for the given axis. - pub fn generic(&self, axes: LayoutAxes, axis: GenericAxisKind) -> LayoutResult { - use AlignmentKey::*; + /// axes. + /// + /// Returns an error if the alignment is invalid for the given axis. + pub fn to_generic(self, axes: LayoutAxes, axis: GenericAxis) -> LayoutResult { + let specific = axis.to_specific(axes); + + Ok(match (self, specific) { + (AlignmentKey::Origin, _) => Origin, + (AlignmentKey::Center, _) => Center, + (AlignmentKey::End, _) => End, + + (AlignmentKey::Left, Horizontal) | (AlignmentKey::Top, Vertical) => { + if axes.get_specific(specific).is_positive() { Origin } else { End } + } + + (AlignmentKey::Right, Horizontal) | (AlignmentKey::Bottom, Vertical) => { + if axes.get_specific(specific).is_positive() { End } else { Origin } + } - let horizontal = axis == axes.horizontal(); - Ok(match self { - Origin => Alignment::Origin, - Center => Alignment::Center, - End => Alignment::End, - Left if horizontal => axes.left(), - Right if horizontal => axes.right(), - Top if !horizontal => axes.top(), - Bottom if !horizontal => axes.bottom(), _ => error!( "invalid alignment `{}` for {} axis", format!("{:?}", self).to_lowercase(), @@ -114,31 +113,20 @@ impl AlignmentKey { /// The specific version of this alignment in the given system of layouting /// axes. - pub fn specific(&self, axes: LayoutAxes, axis: SpecificAxisKind) -> AlignmentKey { + pub fn to_specific(self, axes: LayoutAxes, axis: SpecificAxis) -> AlignmentKey { use AlignmentKey::*; - use SpecificAxisKind::*; - let positive = axes.specific(axis).is_positive(); + let positive = axes.get_specific(axis).is_positive(); match (self, axis, positive) { (Origin, Horizontal, true) | (End, Horizontal, false) => Left, (End, Horizontal, true) | (Origin, Horizontal, false) => Right, (Origin, Vertical, true) | (End, Vertical, false) => Top, (End, Vertical, true) | (Origin, Vertical, false) => Bottom, - _ => *self, + _ => self, } } } -kind!(AlignmentKey, "alignment", - "left" => AlignmentKey::Left, - "top" => AlignmentKey::Top, - "right" => AlignmentKey::Right, - "bottom" => AlignmentKey::Bottom, - "origin" => AlignmentKey::Origin, - "center" => AlignmentKey::Center, - "end" => AlignmentKey::End, -); - /// An argument key which identifies a margin or padding target. /// /// A is the used axis type. @@ -152,30 +140,47 @@ pub enum PaddingKey { AxisAligned(A, AlignmentKey), } +kind!(AxisKey, "axis", + "horizontal" | "h" => AxisKey::Horizontal, + "vertical" | "v" => AxisKey::Vertical, + "primary" | "p" => AxisKey::Primary, + "secondary" | "s" => AxisKey::Secondary, +); + +kind!(AlignmentKey, "alignment", + "left" => AlignmentKey::Left, + "top" => AlignmentKey::Top, + "right" => AlignmentKey::Right, + "bottom" => AlignmentKey::Bottom, + "origin" => AlignmentKey::Origin, + "center" => AlignmentKey::Center, + "end" => AlignmentKey::End, +); + kind!(PaddingKey, "axis or side", - "horizontal" => PaddingKey::Axis(AxisKey::Horizontal), - "vertical" => PaddingKey::Axis(AxisKey::Vertical), - "primary" => PaddingKey::Axis(AxisKey::Primary), - "secondary" => PaddingKey::Axis(AxisKey::Secondary), + "horizontal" | "h" => PaddingKey::Axis(AxisKey::Horizontal), + "vertical" | "v" => PaddingKey::Axis(AxisKey::Vertical), + "primary" | "p" => PaddingKey::Axis(AxisKey::Primary), + "secondary" | "s" => PaddingKey::Axis(AxisKey::Secondary), - "left" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Left), - "right" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Right), - "top" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Top), - "bottom" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Bottom), + "left" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Left), + "right" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Right), + "top" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Top), + "bottom" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Bottom), - "primary-origin" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::Origin), - "primary-end" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::End), - "secondary-origin" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::Origin), - "secondary-end" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::End), + "primary-origin" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::Origin), + "primary-end" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::End), + "secondary-origin" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::Origin), + "secondary-end" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::End), "horizontal-origin" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Origin), - "horizontal-end" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::End), - "vertical-origin" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Origin), - "vertical-end" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::End), + "horizontal-end" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::End), + "vertical-origin" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Origin), + "vertical-end" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::End), ); -kind!(Axis, "direction", - "ltr" => Axis::LeftToRight, - "rtl" => Axis::RightToLeft, - "ttb" => Axis::TopToBottom, - "btt" => Axis::BottomToTop, +kind!(Direction, "direction", + "left-to-right" | "ltr" => LeftToRight, + "right-to-left" | "rtl" => RightToLeft, + "top-to-bottom" | "ttb" => TopToBottom, + "bottom-to-top" | "btt" => BottomToTop, ); diff --git a/src/library/maps.rs b/src/library/maps.rs index 077ebc5ea..4eafd9cc9 100644 --- a/src/library/maps.rs +++ b/src/library/maps.rs @@ -84,10 +84,11 @@ impl ExtentMap { for arg in args.keys() { let key = match arg.v.key.v.0.as_str() { - "width" | "w" => AxisKey::Horizontal, - "height" | "h" => AxisKey::Vertical, - "primary-size" | "ps" => AxisKey::Primary, + "width" | "w" => AxisKey::Horizontal, + "height" | "h" => AxisKey::Vertical, + "primary-size" | "ps" => AxisKey::Primary, "secondary-size" | "ss" => AxisKey::Secondary, + _ => if enforce { error!("expected dimension") } else { @@ -111,22 +112,22 @@ impl ExtentMap { size: F ) -> LayoutResult<()> where F: Fn(&E) -> Size { let map = self.dedup(axes)?; - map.with(SpecificAxisKind::Horizontal, |val| dimensions.x = size(val)); - map.with(SpecificAxisKind::Vertical, |val| dimensions.y = size(val)); + map.with(Horizontal, |val| dimensions.x = size(val)); + map.with(Vertical, |val| dimensions.y = size(val)); Ok(()) } /// Map from any axis key to the specific axis kind. pub fn apply_with(&self, axes: LayoutAxes, mut f: F) -> LayoutResult<()> - where F: FnMut(SpecificAxisKind, &E) { + where F: FnMut(SpecificAxis, &E) { for (&key, value) in self.dedup(axes)?.iter() { f(key, value); } Ok(()) } - fn dedup(&self, axes: LayoutAxes) -> LayoutResult> { - self.0.dedup(|key, &val| Ok((key.specific(axes), val))) + fn dedup(&self, axes: LayoutAxes) -> LayoutResult> { + self.0.dedup(|key, &val| Ok((key.to_specific(axes), val))) } } @@ -165,17 +166,17 @@ impl PaddingMap { let map = self.0.dedup(|key, &val| { Ok((match key { All => All, - Axis(axis) => Axis(axis.specific(axes)), + Axis(axis) => Axis(axis.to_specific(axes)), AxisAligned(axis, alignment) => { - let axis = axis.specific(axes); - AxisAligned(axis, alignment.specific(axes, axis)) + let axis = axis.to_specific(axes); + AxisAligned(axis, alignment.to_specific(axes, axis)) } }, val)) })?; map.with(All, |&val| padding.set_all(val)); - map.with(Axis(SpecificAxisKind::Horizontal), |&val| padding.set_horizontal(val)); - map.with(Axis(SpecificAxisKind::Vertical), |&val| padding.set_vertical(val)); + map.with(Axis(Horizontal), |&val| padding.set_horizontal(val)); + map.with(Axis(Vertical), |&val| padding.set_vertical(val)); for (key, &val) in map.iter() { if let AxisAligned(_, alignment) = key { diff --git a/src/library/mod.rs b/src/library/mod.rs index b6561d5a2..0fcc86470 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -19,7 +19,7 @@ pub fn std() -> Scope { std.add::("align"); std.add::("box"); - std.add::("direction"); + std.add::("direction"); std.add::("page.size"); std.add::("page.margins"); @@ -30,10 +30,10 @@ pub fn std() -> Scope { std.add::("font.size"); - std.add_with_metadata::>("spacing", None); + std.add_with_metadata::("spacing", None); for (name, key) in &[("h", AxisKey::Horizontal), ("v", AxisKey::Vertical)] { - std.add_with_metadata::>(name, Some(*key)); + std.add_with_metadata::(name, Some(*key)); } for (name, class) in &[ @@ -41,7 +41,7 @@ pub fn std() -> Scope { ("italic", FontClass::Italic), ("mono", FontClass::Monospace), ] { - std.add_with_metadata::(name, class.clone()); + std.add_with_metadata::(name, class.clone()); } std @@ -152,7 +152,7 @@ function! { } layout(self, ctx) { - let axis = self.axis.generic(ctx.axes); + let axis = self.axis.to_generic(ctx.axes); let spacing = self.spacing.concretize(ctx.style.text.font_size); vec![AddSpacing(spacing, SpacingKind::Hard, axis)] } diff --git a/src/size.rs b/src/size.rs index 97d04815d..420b815bd 100644 --- a/src/size.rs +++ b/src/size.rs @@ -5,8 +5,7 @@ use std::iter::Sum; use std::ops::*; use std::str::FromStr; -use crate::layout::{LayoutAxes, Axis, GenericAxisKind, LayoutAlignment, Alignment}; - +use crate::layout::prelude::*; /// A general spacing type. #[derive(Copy, Clone, PartialEq, PartialOrd)] @@ -59,11 +58,10 @@ impl Size { /// Set this size to the minimum of itself and the other size. pub fn min_eq(&mut self, other: Size) { *self = self.min(other); } - /// The anchor position along the given axis for an item with the given + /// The anchor position along the given direction for an item with the given /// alignment in a container with this size. - pub fn anchor(self, alignment: Alignment, axis: Axis) -> Size { - use Alignment::*; - match (axis.is_positive(), alignment) { + pub fn anchor(self, alignment: Alignment, direction: Direction) -> Size { + match (direction.is_positive(), alignment) { (true, Origin) | (false, End) => Size::ZERO, (_, Center) => self / 2, (true, End) | (false, Origin) => self, @@ -120,23 +118,23 @@ impl Size2D { pub fn with_all(s: Size) -> Size2D { Size2D { x: s, y: s } } /// Access the primary size of this specialized 2D-size. - pub fn primary(self, axes: LayoutAxes) -> Size { - if axes.primary.is_horizontal() { self.x } else { self.y } + pub fn get_primary(self, axes: LayoutAxes) -> Size { + if axes.primary.axis() == Horizontal { self.x } else { self.y } } /// Access the primary size of this specialized 2D-size mutably. - pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut Size { - if axes.primary.is_horizontal() { &mut self.x } else { &mut self.y } + pub fn get_primary_mut(&mut self, axes: LayoutAxes) -> &mut Size { + if axes.primary.axis() == Horizontal { &mut self.x } else { &mut self.y } } /// Access the secondary size of this specialized 2D-size. - pub fn secondary(self, axes: LayoutAxes) -> Size { - if axes.primary.is_horizontal() { self.y } else { self.x } + pub fn get_secondary(self, axes: LayoutAxes) -> Size { + if axes.primary.axis() == Horizontal { self.y } else { self.x } } /// Access the secondary size of this specialized 2D-size mutably. - pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut Size { - if axes.primary.is_horizontal() { &mut self.y } else { &mut self.x } + pub fn get_secondary_mut(&mut self, axes: LayoutAxes) -> &mut Size { + if axes.primary.axis() == Horizontal { &mut self.y } else { &mut self.x } } /// Returns the generalized version of a `Size2D` dependent on the layouting @@ -144,9 +142,9 @@ impl Size2D { /// - `x` describes the primary axis instead of the horizontal one. /// - `y` describes the secondary axis instead of the vertical one. pub fn generalized(self, axes: LayoutAxes) -> Size2D { - match axes.primary.is_horizontal() { - true => self, - false => Size2D { x: self.y, y: self.x }, + match axes.primary.axis() { + Horizontal => self, + Vertical => Size2D { x: self.y, y: self.x }, } } @@ -249,19 +247,19 @@ impl SizeBox { /// alignment. pub fn get_mut(&mut self, axes: LayoutAxes, - axis: GenericAxisKind, + axis: GenericAxis, alignment: Alignment, ) -> &mut Size { - let mut normalized = axes.generic(axis); - if alignment == Alignment::End { + let mut normalized = axes.get_generic(axis); + if alignment == End { normalized = normalized.inv(); } match normalized { - Axis::LeftToRight => &mut self.left, - Axis::RightToLeft => &mut self.right, - Axis::TopToBottom => &mut self.top, - Axis::BottomToTop => &mut self.bottom, + LeftToRight => &mut self.left, + RightToLeft => &mut self.right, + TopToBottom => &mut self.top, + BottomToTop => &mut self.bottom, } }