summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-11-30 18:54:46 +0100
committerLaurenz <laurmaedje@gmail.com>2019-11-30 18:54:46 +0100
commit5782b82770f6923677942c3b4e2bf4f7258e47d8 (patch)
tree2b477fc285e9da5380e556648056b985ecbccfe0 /src
parentb13ed627fff73a599b34d760cd99aa2f08d58ea8 (diff)
Refactor layouting base ♻
Diffstat (limited to 'src')
-rw-r--r--src/func/mod.rs59
-rw-r--r--src/layout/actions.rs13
-rw-r--r--src/layout/flex.rs14
-rw-r--r--src/layout/mod.rs270
-rw-r--r--src/layout/stack.rs59
-rw-r--r--src/layout/text.rs1
-rw-r--r--src/layout/tree.rs44
-rw-r--r--src/lib.rs32
-rw-r--r--src/library/align.rs8
-rw-r--r--src/library/boxed.rs2
-rw-r--r--src/library/page.rs10
-rw-r--r--src/library/spacing.rs8
-rw-r--r--src/library/style.rs8
-rw-r--r--src/style.rs7
-rw-r--r--src/syntax/parsing.rs4
15 files changed, 218 insertions, 321 deletions
diff --git a/src/func/mod.rs b/src/func/mod.rs
index 9c29caf2..126fd824 100644
--- a/src/func/mod.rs
+++ b/src/func/mod.rs
@@ -13,7 +13,7 @@ pub mod helpers;
pub mod prelude {
pub use crate::func::{Command, CommandList, Function};
pub use crate::layout::{layout_tree, Layout, MultiLayout, LayoutContext, LayoutSpace};
- pub use crate::layout::{LayoutAxes, AlignedAxis, Axis, Alignment};
+ pub use crate::layout::{LayoutAxes, Axis, Alignment};
pub use crate::layout::{LayoutError, LayoutResult};
pub use crate::syntax::{SyntaxTree, FuncHeader, FuncArgs, Expression, Spanned, Span};
pub use crate::syntax::{parse, ParseContext, ParseError, ParseResult};
@@ -86,6 +86,9 @@ where T: Debug + PartialEq + 'static
}
}
+/// A sequence of commands requested for execution by a function.
+pub type CommandList<'a> = Vec<Command<'a>>;
+
/// Commands requested for execution by functions.
#[derive(Debug)]
pub enum Command<'a> {
@@ -109,60 +112,6 @@ pub enum Command<'a> {
SetAxes(LayoutAxes),
}
-/// A sequence of commands requested for execution by a function.
-#[derive(Debug)]
-pub struct CommandList<'a> {
- pub commands: Vec<Command<'a>>,
-}
-
-impl<'a> CommandList<'a> {
- /// Create an empty command list.
- pub fn new() -> CommandList<'a> {
- CommandList { commands: vec![] }
- }
-
- /// Create a command list with commands from a vector.
- pub fn from_vec(commands: Vec<Command<'a>>) -> CommandList<'a> {
- CommandList { commands }
- }
-
- /// Add a command to the sequence.
- pub fn add(&mut self, command: Command<'a>) {
- self.commands.push(command);
- }
-
- /// Whether there are any commands in this sequence.
- pub fn is_empty(&self) -> bool {
- self.commands.is_empty()
- }
-}
-
-impl<'a> IntoIterator for CommandList<'a> {
- type Item = Command<'a>;
- type IntoIter = std::vec::IntoIter<Command<'a>>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.commands.into_iter()
- }
-}
-
-impl<'a> IntoIterator for &'a CommandList<'a> {
- type Item = &'a Command<'a>;
- type IntoIter = std::slice::Iter<'a, Command<'a>>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.commands.iter()
- }
-}
-
-/// Create a list of commands.
-#[macro_export]
-macro_rules! commands {
- ($($x:expr),*$(,)*) => (
- $crate::func::CommandList::from_vec(vec![$($x,)*])
- );
-}
-
/// A map from identifiers to functions.
pub struct Scope {
parsers: HashMap<String, Box<ParseFunc>>,
diff --git a/src/layout/actions.rs b/src/layout/actions.rs
index c668cf75..2528fc85 100644
--- a/src/layout/actions.rs
+++ b/src/layout/actions.rs
@@ -1,10 +1,8 @@
//! Drawing and cofiguration actions composing layouts.
use std::fmt::{self, Display, Formatter};
-use std::io::{self, Write};
-use super::Layout;
-use crate::size::{Size, Size2D};
+use super::*;
use LayoutAction::*;
/// A layouting action.
@@ -21,9 +19,8 @@ pub enum LayoutAction {
DebugBox(Size2D, Size2D),
}
-impl LayoutAction {
- /// Serialize this layout action into an easy-to-parse string representation.
- pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
+impl Serialize for LayoutAction {
+ fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
match self {
MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()),
SetFont(i, s) => write!(f, "f {} {}", i, s.to_pt()),
@@ -121,10 +118,6 @@ impl LayoutActionList {
self.origin = position;
self.next_pos = Some(position);
- if layout.debug_render {
- self.actions.push(DebugBox(position, layout.dimensions));
- }
-
self.extend(layout.actions);
}
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
index 53f6dfdf..96b2aa85 100644
--- a/src/layout/flex.rs
+++ b/src/layout/flex.rs
@@ -41,7 +41,7 @@ struct PartialLine {
usable: Size,
content: Vec<(Size, Layout)>,
dimensions: Size2D,
- space: SpaceState,
+ space: LastSpacing,
}
impl PartialLine {
@@ -50,7 +50,7 @@ impl PartialLine {
usable,
content: vec![],
dimensions: Size2D::zero(),
- space: SpaceState::Forbidden,
+ space: LastSpacing::Forbidden,
}
}
}
@@ -237,7 +237,7 @@ impl FlexLayouter {
}
}
- if let SpaceState::Soft(space) = self.part.space {
+ if let LastSpacing::Soft(space) = self.part.space {
self.layout_space(space, SpaceKind::Hard);
}
@@ -246,15 +246,15 @@ impl FlexLayouter {
self.part.dimensions.x += size.x;
self.part.dimensions.y.max_eq(size.y);
- self.part.space = SpaceState::Allowed;
+ self.part.space = LastSpacing::Allowed;
Ok(())
}
fn layout_space(&mut self, space: Size, kind: SpaceKind) {
if kind == SpaceKind::Soft {
- if self.part.space != SpaceState::Forbidden {
- self.part.space = SpaceState::Soft(space);
+ if self.part.space != LastSpacing::Forbidden {
+ self.part.space = LastSpacing::Soft(space);
}
} else {
if self.part.dimensions.x + space > self.part.usable {
@@ -264,7 +264,7 @@ impl FlexLayouter {
}
if kind == SpaceKind::Hard {
- self.part.space = SpaceState::Forbidden;
+ self.part.space = LastSpacing::Forbidden;
}
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index cd4986d9..31064d40 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -9,7 +9,7 @@ use toddle::Error as FontError;
use crate::func::Command;
use crate::size::{Size, Size2D, SizeBox};
-use crate::style::{PageStyle, TextStyle};
+use crate::style::{LayoutStyle, TextStyle};
use crate::syntax::{FuncCall, Node, SyntaxTree};
mod actions;
@@ -29,99 +29,20 @@ pub mod layouters {
pub use actions::{LayoutAction, LayoutActionList};
pub use layouters::*;
+/// A collection of layouts.
+pub type MultiLayout = Vec<Layout>;
+
/// A sequence of layouting actions inside a box.
#[derive(Debug, Clone)]
pub struct Layout {
/// The size of the box.
pub dimensions: Size2D,
+ /// The baseline of the layout (as an offset from the top-left).
+ pub baseline: Option<Size>,
+ /// How to align this layout in a parent container.
+ pub alignment: LayoutAlignment,
/// The actions composing this layout.
pub actions: Vec<LayoutAction>,
- /// Whether to debug-render this box.
- pub debug_render: bool,
-}
-
-impl Layout {
- /// Serialize this layout into an output buffer.
- pub 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(())
- }
-}
-
-/// A collection of layouts.
-#[derive(Debug, Clone)]
-pub struct MultiLayout {
- pub layouts: Vec<Layout>,
-}
-
-impl MultiLayout {
- /// Create an empty multi-layout.
- pub fn new() -> MultiLayout {
- MultiLayout { layouts: vec![] }
- }
-
- /// Extract the single sublayout. This panics if the layout does not have
- /// exactly one child.
- pub fn into_single(mut self) -> Layout {
- if self.layouts.len() != 1 {
- panic!("into_single: contains not exactly one layout");
- }
- self.layouts.pop().unwrap()
- }
-
- /// Add a sublayout.
- pub fn add(&mut self, layout: Layout) {
- self.layouts.push(layout);
- }
-
- /// The count of sublayouts.
- pub fn count(&self) -> usize {
- self.layouts.len()
- }
-
- /// Whether this layout contains any sublayouts.
- pub fn is_empty(&self) -> bool {
- self.layouts.is_empty()
- }
-}
-
-impl MultiLayout {
- /// Serialize this collection of layouts into an output buffer.
- pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
- writeln!(f, "{}", self.count())?;
- for layout in self {
- layout.serialize(f)?;
- }
- Ok(())
- }
-}
-
-impl IntoIterator for MultiLayout {
- type Item = Layout;
- type IntoIter = std::vec::IntoIter<Layout>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.layouts.into_iter()
- }
-}
-
-impl<'a> IntoIterator for &'a MultiLayout {
- type Item = &'a Layout;
- type IntoIter = std::slice::Iter<'a, Layout>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.layouts.iter()
- }
}
/// The general context for layouting.
@@ -130,20 +51,16 @@ pub struct LayoutContext<'a, 'p> {
/// The font loader to retrieve fonts from when typesetting text
/// using [`layout_text`].
pub loader: &'a SharedFontLoader<'p>,
+ /// The style for pages and text.
+ pub style: &'a LayoutStyle,
/// Whether this layouting process handles the top-level pages.
pub top_level: bool,
- /// The style to set text with. This includes sizes and font classes
- /// which determine which font from the loaders selection is used.
- pub text_style: &'a TextStyle,
- /// The current size and margins of the top-level pages.
- pub page_style: PageStyle,
/// The spaces to layout in.
pub spaces: LayoutSpaces,
- /// The axes to flow on.
+ /// The initial axes along which content is laid out.
pub axes: LayoutAxes,
- /// Whether layouts should expand to the full dimensions of the space
- /// they lie on or whether should tightly fit the content.
- pub expand: bool,
+ /// The alignment for the two axes.
+ pub alignment: LayoutAlignment,
}
/// A possibly stack-allocated vector of layout spaces.
@@ -154,26 +71,31 @@ pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
pub struct LayoutSpace {
/// The maximum size of the box to layout in.
pub dimensions: Size2D,
+ /// 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
+ /// vertical and horizontal axis.
+ pub expand: (bool, bool),
/// Padding that should be respected on each side.
pub padding: SizeBox,
}
impl LayoutSpace {
- /// The actually usable area (dimensions minus padding).
- pub fn usable(&self) -> Size2D {
- self.dimensions.unpadded(self.padding)
- }
-
/// The offset from the origin to the start of content, that is,
/// `(padding.left, padding.top)`.
pub fn start(&self) -> Size2D {
Size2D::new(self.padding.left, self.padding.right)
}
+ /// The actually usable area (dimensions minus padding).
+ pub fn usable(&self) -> Size2D {
+ self.dimensions.unpadded(self.padding)
+ }
+
/// A layout space without padding and dimensions reduced by the padding.
pub fn usable_space(&self) -> LayoutSpace {
LayoutSpace {
dimensions: self.usable(),
+ expand: (false, false),
padding: SizeBox::zero(),
}
}
@@ -182,17 +104,21 @@ impl LayoutSpace {
/// The axes along which the content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LayoutAxes {
- pub primary: AlignedAxis,
- pub secondary: AlignedAxis,
+ pub primary: Axis,
+ pub secondary: Axis,
}
impl LayoutAxes {
+ pub fn new(primary: Axis, secondary: Axis) -> LayoutAxes {
+ LayoutAxes { primary, secondary }
+ }
+
/// Returns the generalized version of a `Size2D` dependent on
/// the layouting axes, that is:
/// - The x coordinate describes the primary axis instead of the horizontal one.
/// - The y coordinate describes the secondary axis instead of the vertical one.
pub fn generalize(&self, size: Size2D) -> Size2D {
- if self.primary.axis.is_horizontal() {
+ if self.primary.is_horizontal() {
size
} else {
Size2D { x: size.y, y: size.x }
@@ -206,58 +132,9 @@ impl LayoutAxes {
// at the call site, we still have this second function.
self.generalize(size)
}
-
- /// The position of the anchor specified by the two aligned axes
- /// in the given generalized space.
- pub fn anchor(&self, space: Size2D) -> Size2D {
- Size2D::new(self.primary.anchor(space.x), self.secondary.anchor(space.y))
- }
-
- /// This axes with `expand` set to the given value for both axes.
- pub fn expanding(&self, expand: bool) -> LayoutAxes {
- LayoutAxes {
- primary: self.primary.expanding(expand),
- secondary: self.secondary.expanding(expand),
- }
- }
-}
-
-/// An axis with an alignment.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct AlignedAxis {
- pub axis: Axis,
- pub alignment: Alignment,
- pub expand: bool,
-}
-
-impl AlignedAxis {
- /// Creates an aligned axis from its three components.
- pub fn new(axis: Axis, alignment: Alignment, expand: bool) -> AlignedAxis {
- AlignedAxis { axis, alignment, expand }
- }
-
- /// The position of the anchor specified by this axis on the given line.
- pub fn anchor(&self, line: Size) -> Size {
- use Alignment::*;
- match (self.axis.is_positive(), self.alignment) {
- (true, Origin) | (false, End) => Size::zero(),
- (_, Center) => line / 2,
- (true, End) | (false, Origin) => line,
- }
- }
-
- /// This axis with `expand` set to the given value.
- pub fn expanding(&self, expand: bool) -> AlignedAxis {
- AlignedAxis { expand, ..*self }
- }
-
- /// Whether this axis needs expansion.
- pub fn needs_expansion(&self) -> bool {
- self.expand || self.alignment != Alignment::Origin
- }
}
-/// Where to put content.
+/// Directions along which content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Axis {
LeftToRight,
@@ -292,6 +169,19 @@ impl Axis {
}
}
+/// The place to put a layout in a container.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct LayoutAlignment {
+ pub primary: Alignment,
+ pub secondary: Alignment,
+}
+
+impl LayoutAlignment {
+ pub fn new(primary: Alignment, secondary: Alignment) -> LayoutAlignment {
+ LayoutAlignment { primary, secondary }
+ }
+}
+
/// Where to align content.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Alignment {
@@ -300,26 +190,74 @@ pub enum Alignment {
End,
}
+/// The specialized anchor position for an item with the given alignment in a
+/// container with a given size along the given axis.
+pub fn anchor(axis: Axis, size: Size, alignment: Alignment) -> Size {
+ use Alignment::*;
+ match (axis.is_positive(), alignment) {
+ (true, Origin) | (false, End) => Size::zero(),
+ (_, Center) => size / 2,
+ (true, End) | (false, Origin) => size,
+ }
+}
+
+/// Whitespace between boxes with different interaction properties.
#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum SpaceKind {
- /// Soft spaces are eaten up by hard spaces before or after them.
- Soft,
- /// Independent do not eat up soft spaces and are not eaten up by hard spaces.
- Independent,
- /// Hard spaces eat up soft spaces before or after them.
+pub enum SpacingKind {
+ /// A hard space consumes surrounding soft spaces and is always layouted.
Hard,
+ /// A soft space consumes surrounding soft spaces with higher value.
+ Soft(u32),
}
+/// The standard spacing kind used for paragraph spacing.
+const PARAGRAPH_KIND: SpacingKind = SpacingKind::Soft(1);
+
+/// The standard spacing kind used for normal spaces between boxes.
+const SPACE_KIND: SpacingKind = SpacingKind::Soft(2);
+
+/// The last appeared spacing.
#[derive(Debug, Copy, Clone, PartialEq)]
-enum SpaceState {
- Soft(Size),
- Forbidden,
- Allowed,
+enum LastSpacing {
+ Hard,
+ Soft(Size, u32),
+ None,
}
-impl SpaceState {
+impl LastSpacing {
fn soft_or_zero(&self) -> Size {
- if let SpaceState::Soft(space) = self { *space } else { Size::zero() }
+ match self {
+ LastSpacing::Soft(space, _) => *space,
+ _ => Size::zero(),
+ }
+ }
+}
+
+/// Layout components that can be serialized.
+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 f46c3da0..fd88ce98 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -1,23 +1,54 @@
use smallvec::smallvec;
use super::*;
+/// The stack layouter arranges boxes stacked onto each other.
+///
+/// The boxes are laid out in the direction of the secondary layouting axis and
+/// are aligned along both axes.
#[derive(Debug, Clone)]
pub struct StackLayouter {
+ /// The context for layouter.
ctx: StackContext,
+ /// The output layouts.
layouts: MultiLayout,
-
+ /// The full layout space.
space: Space,
+ /// The currently active subspace.
sub: Subspace,
}
#[derive(Debug, Clone)]
struct Space {
+ /// The index of this space in the list of spaces.
index: usize,
+ /// Whether to add the layout for this space even if it would be empty.
hard: bool,
+ /// The layouting actions accumulated from the subspaces.
actions: LayoutActionList,
+ /// The used size of this space from the top-left corner to
+ /// the bottomright-most point of used space (specialized).
combined_dimensions: Size2D,
}
+#[derive(Debug, Clone)]
+struct Subspace {
+ /// The axes along which contents in this subspace are laid out.
+ axes: LayoutAxes,
+ /// The beginning of this subspace in the parent space (specialized).
+ origin: Size2D,
+ /// The total usable space of this subspace (generalized).
+ usable: Size2D,
+ /// The used size of this subspace (generalized), with
+ /// - `x` being the maximum of the primary size of all boxes.
+ /// - `y` being the total extent of all boxes and space in the secondary
+ /// direction.
+ size: Size2D,
+ /// The so-far accumulated (offset, anchor, box) triples.
+ boxes: Vec<(Size, Size, Layout)>,
+ /// The last added spacing if the last was spacing.
+ last_spacing: LastSpacing,
+}
+
impl Space {
fn new(index: usize, hard: bool) -> Space {
Space {
@@ -29,20 +60,6 @@ impl Space {
}
}
-#[derive(Debug, Clone)]
-struct Subspace {
- origin: Size2D,
- anchor: Size2D,
- factor: i32,
-
- boxes: Vec<(Size, Size, Layout)>,
-
- usable: Size2D,
- dimensions: Size2D,
-
- space: SpaceState,
-}
-
impl Subspace {
fn new(origin: Size2D, usable: Size2D, axes: LayoutAxes) -> Subspace {
Subspace {
@@ -52,7 +69,7 @@ impl Subspace {
boxes: vec![],
usable: axes.generalize(usable),
dimensions: Size2D::zero(),
- space: SpaceState::Forbidden,
+ space: LastSpacing::Forbidden,
}
}
}
@@ -82,7 +99,7 @@ impl StackLayouter {
}
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
- if let SpaceState::Soft(space) = self.sub.space {
+ if let LastSpacing::Soft(space) = self.sub.space {
self.add_space(space, SpaceKind::Hard);
}
@@ -107,7 +124,7 @@ impl StackLayouter {
self.sub.boxes.push((offset, anchor, layout));
self.sub.dimensions = new_dimensions;
- self.sub.space = SpaceState::Allowed;
+ self.sub.space = LastSpacing::Allowed;
Ok(())
}
@@ -121,8 +138,8 @@ impl StackLayouter {
pub fn add_space(&mut self, space: Size, kind: SpaceKind) {
if kind == SpaceKind::Soft {
- if self.sub.space != SpaceState::Forbidden {
- self.sub.space = SpaceState::Soft(space);
+ if self.sub.space != LastSpacing::Forbidden {
+ self.sub.space = LastSpacing::Soft(space);
}
} else {
if self.sub.dimensions.y + space > self.sub.usable.y {
@@ -132,7 +149,7 @@ impl StackLayouter {
}
if kind == SpaceKind::Hard {
- self.sub.space = SpaceState::Forbidden;
+ self.sub.space = LastSpacing::Forbidden;
}
}
}
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 66ff75fd..3ca826ca 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -73,7 +73,6 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
Ok(Layout {
dimensions: Size2D::new(self.width, self.ctx.style.font_size),
actions: self.actions.to_vec(),
- debug_render: false,
})
}
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 56fb120c..9a818963 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -11,7 +11,7 @@ pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiL
struct TreeLayouter<'a, 'p> {
ctx: LayoutContext<'a, 'p>,
flex: FlexLayouter,
- style: TextStyle,
+ style: LayoutStyle,
}
impl<'a, 'p> TreeLayouter<'a, 'p> {
@@ -19,12 +19,12 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
TreeLayouter {
flex: FlexLayouter::new(FlexContext {
- flex_spacing: flex_spacing(&ctx.text_style),
+ flex_spacing: flex_spacing(&ctx.style.text),
spaces: ctx.spaces.clone(),
axes: ctx.axes,
expand: ctx.expand,
}),
- style: ctx.text_style.clone(),
+ style: ctx.style.clone(),
ctx,
}
}
@@ -37,9 +37,9 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Node::Space => self.layout_space(),
Node::Newline => self.layout_paragraph()?,
- Node::ToggleItalics => self.style.toggle_class(FontClass::Italic),
- Node::ToggleBold => self.style.toggle_class(FontClass::Bold),
- Node::ToggleMonospace => self.style.toggle_class(FontClass::Monospace),
+ Node::ToggleItalics => self.style.text.toggle_class(FontClass::Italic),
+ Node::ToggleBold => self.style.text.toggle_class(FontClass::Bold),
+ Node::ToggleMonospace => self.style.text.toggle_class(FontClass::Monospace),
Node::Func(func) => self.layout_func(func)?,
}
@@ -51,34 +51,35 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
fn layout_text(&mut self, text: &str) -> LayoutResult<()> {
let layout = layout_text(text, TextContext {
loader: &self.ctx.loader,
- style: &self.style,
+ style: &self.style.text,
})?;
Ok(self.flex.add(layout))
}
fn layout_space(&mut self) {
- self.flex.add_primary_space(word_spacing(&self.style), SpaceKind::Soft);
+ self.flex.add_primary_space(
+ word_spacing(&self.style.text),
+ SPACE_KIND,
+ );
}
fn layout_paragraph(&mut self) -> LayoutResult<()> {
- self.flex.add_secondary_space(paragraph_spacing(&self.style), SpaceKind::Soft)
+ self.flex.add_secondary_space(
+ paragraph_spacing(&self.style.text),
+ PARAGRAPH_KIND,
+ )
}
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
let spaces = self.flex.remaining();
- let mut axes = self.ctx.axes.expanding(false);
- axes.secondary.alignment = Alignment::Origin;
-
let commands = func.body.val.layout(LayoutContext {
loader: self.ctx.loader,
+ style: &self.style,
top_level: false,
- text_style: &self.style,
- page_style: self.ctx.page_style,
spaces,
- axes,
- expand: false,
+ .. self.ctx
})?;
for command in commands {
@@ -95,10 +96,8 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Command::Add(layout) => self.flex.add(layout),
Command::AddMultiple(layouts) => self.flex.add_multiple(layouts),
- Command::AddPrimarySpace(space)
- => self.flex.add_primary_space(space, SpaceKind::Hard),
- Command::AddSecondarySpace(space)
- => self.flex.add_secondary_space(space, SpaceKind::Hard)?,
+ Command::AddPrimarySpace(space) => self.flex.add_primary_space(space, SpacingKind::Hard),
+ Command::AddSecondarySpace(space) => self.flex.add_secondary_space(space, SpacingKind::Hard)?,
Command::FinishLine => self.flex.add_break(),
Command::FinishRun => { self.flex.finish_run()?; },
@@ -106,16 +105,17 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Command::BreakParagraph => self.layout_paragraph()?,
- Command::SetTextStyle(style) => self.style = style,
+ Command::SetTextStyle(style) => self.style.text = style,
Command::SetPageStyle(style) => {
if !self.ctx.top_level {
lerr!("page style cannot only be altered in the top-level context");
}
- self.ctx.page_style = style;
+ self.style.page = style;
self.flex.set_spaces(smallvec![
LayoutSpace {
dimensions: style.dimensions,
+ expand: (true, true),
padding: style.margins,
}
], true);
diff --git a/src/lib.rs b/src/lib.rs
index b1408f4a..7b6c4012 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,10 +22,10 @@ use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
use crate::func::Scope;
use crate::layout::{layout_tree, MultiLayout, LayoutContext};
-use crate::layout::{LayoutAxes, AlignedAxis, Axis, Alignment};
+use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment};
use crate::layout::{LayoutError, LayoutResult, LayoutSpace};
use crate::syntax::{SyntaxTree, parse, ParseContext, ParseError, ParseResult};
-use crate::style::{PageStyle, TextStyle};
+use crate::style::{LayoutStyle, PageStyle, TextStyle};
#[macro_use]
mod macros;
@@ -44,10 +44,8 @@ pub mod syntax;
pub struct Typesetter<'p> {
/// The font loader shared by all typesetting processes.
loader: SharedFontLoader<'p>,
- /// The base text style.
- text_style: TextStyle,
- /// The base page style.
- page_style: PageStyle,
+ /// The base layouting style.
+ style: LayoutStyle,
}
impl<'p> Typesetter<'p> {
@@ -56,21 +54,20 @@ impl<'p> Typesetter<'p> {
pub fn new() -> Typesetter<'p> {
Typesetter {
loader: RefCell::new(FontLoader::new()),
- text_style: TextStyle::default(),
- page_style: PageStyle::default(),
+ style: LayoutStyle::default(),
}
}
/// Set the base page style.
#[inline]
pub fn set_page_style(&mut self, style: PageStyle) {
- self.page_style = style;
+ self.style.page = style;
}
/// Set the base text style.
#[inline]
pub fn set_text_style(&mut self, style: TextStyle) {
- self.text_style = style;
+ self.style.text = style;
}
/// Add a font provider to the context of this typesetter.
@@ -99,17 +96,14 @@ impl<'p> Typesetter<'p> {
LayoutContext {
loader: &self.loader,
top_level: true,
- text_style: &self.text_style,
- page_style: self.page_style,
+ style: &self.style,
spaces: smallvec![LayoutSpace {
- dimensions: self.page_style.dimensions,
- padding: self.page_style.margins,
+ dimensions: self.style.page.dimensions,
+ expand: (true, true),
+ padding: self.style.page.margins,
}],
- axes: LayoutAxes {
- primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Origin, false),
- secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Origin, false),
- },
- expand: true,
+ axes: LayoutAxes::new(Axis::LeftToRight, Axis::TopToBottom),
+ alignment: LayoutAlignment::new(Alignment::Origin, Alignment::Origin),
},
)?)
}
diff --git a/src/library/align.rs b/src/library/align.rs
index 55063ebe..530120e7 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -69,7 +69,7 @@ function! {
layout(this, ctx) {
let mut axes = ctx.axes;
- let primary_horizontal = axes.primary.axis.is_horizontal();
+ let primary_horizontal = axes.primary.is_horizontal();
let mut primary = false;
let mut secondary = false;
@@ -87,7 +87,7 @@ function! {
*was_set = true;
- let horizontal = axis.axis.is_horizontal();
+ let horizontal = axis.is_horizontal();
let alignment = generic_alignment(spec, horizontal)?;
if axis.alignment == Alignment::End && alignment == Alignment::Origin {
@@ -116,13 +116,13 @@ function! {
set_axis(!primary_horizontal, this.vertical)?;
Ok(match &this.body {
- Some(body) => commands![AddMultiple(
+ Some(body) => vec![AddMultiple(
layout_tree(body, LayoutContext {
axes,
.. ctx.clone()
})?
)],
- None => commands![Command::SetAxes(axes)]
+ None => vec![Command::SetAxes(axes)]
})
}
}
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index 1cc4e020..8984417c 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -32,6 +32,6 @@ function! {
ctx.spaces[0].dimensions.y = height;
}
- Ok(commands![AddMultiple(layout_tree(&this.body, ctx)?)])
+ Ok(vec![AddMultiple(layout_tree(&this.body, ctx)?)])
}
}
diff --git a/src/library/page.rs b/src/library/page.rs
index 40872d63..e8a80870 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -7,7 +7,7 @@ pub struct PageBreak;
function! {
data: PageBreak,
parse: plain,
- layout(_, _) { Ok(commands![FinishSpace]) }
+ layout(_, _) { Ok(vec![FinishSpace]) }
}
/// `page.size`: Set the size of pages.
@@ -29,12 +29,12 @@ function! {
}
layout(this, ctx) {
- let mut style = ctx.page_style;
+ let mut style = ctx.style.page;
if let Some(width) = this.width { style.dimensions.x = width; }
if let Some(height) = this.height { style.dimensions.y = height; }
- Ok(commands![SetPageStyle(style)])
+ Ok(vec![SetPageStyle(style)])
}
}
@@ -67,13 +67,13 @@ function! {
}
layout(this, ctx) {
- let mut style = ctx.page_style;
+ let mut style = ctx.style.page;
if let Some(left) = this.left { style.margins.left = left; }
if let Some(top) = this.top { style.margins.top = top; }
if let Some(right) = this.right { style.margins.right = right; }
if let Some(bottom) = this.bottom { style.margins.bottom = bottom; }
- Ok(commands![SetPageStyle(style)])
+ Ok(vec![SetPageStyle(style)])
}
}
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index d52153b1..869a6227 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -7,7 +7,7 @@ pub struct LineBreak;
function! {
data: LineBreak,
parse: plain,
- layout(_, _) { Ok(commands![FinishLine]) }
+ layout(_, _) { Ok(vec![FinishLine]) }
}
/// `paragraph.break`: Ends the current paragraph.
@@ -19,7 +19,7 @@ pub struct ParagraphBreak;
function! {
data: ParagraphBreak,
parse: plain,
- layout(_, _) { Ok(commands![BreakParagraph]) }
+ layout(_, _) { Ok(vec![BreakParagraph]) }
}
macro_rules! space_func {
@@ -47,10 +47,10 @@ macro_rules! space_func {
layout(this, ctx) {
let $var = match this.0 {
Spacing::Absolute(s) => s,
- Spacing::Relative(f) => f * ctx.text_style.font_size,
+ Spacing::Relative(f) => f * ctx.style.text.font_size,
};
- Ok(commands![$command])
+ Ok(vec![$command])
}
}
);
diff --git a/src/library/style.rs b/src/library/style.rs
index a63166cf..b2de19bb 100644
--- a/src/library/style.rs
+++ b/src/library/style.rs
@@ -18,16 +18,16 @@ macro_rules! stylefunc {
}
layout(this, ctx) {
- let mut style = ctx.text_style.clone();
+ let mut style = ctx.style.text.clone();
style.toggle_class(FontClass::$ident);
Ok(match &this.body {
- Some(body) => commands![
+ Some(body) => vec![
SetTextStyle(style),
LayoutTree(body),
- SetTextStyle(ctx.text_style.clone()),
+ SetTextStyle(ctx.style.text.clone()),
],
- None => commands![SetTextStyle(style)]
+ None => vec![SetTextStyle(style)]
})
}
}
diff --git a/src/style.rs b/src/style.rs
index ae396852..3323e576 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -4,6 +4,13 @@ use toddle::query::FontClass;
use FontClass::*;
use crate::size::{Size, Size2D, SizeBox};
+/// Defines properties of pages and text.
+#[derive(Debug, Default, Clone)]
+pub struct LayoutStyle {
+ pub page: PageStyle,
+ pub text: TextStyle,
+}
+
/// Defines which fonts to use and how to space text.
#[derive(Debug, Clone)]
pub struct TextStyle {
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index b3fda734..77697161 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -472,7 +472,7 @@ mod tests {
data: TreeFn,
parse(_args, body, ctx) { Ok(TreeFn(parse!(required: body, ctx))) }
- layout(_, _) { Ok(commands![]) }
+ layout(_, _) { Ok(vec![]) }
}
impl PartialEq for TreeFn {
@@ -490,7 +490,7 @@ mod tests {
data: BodylessFn,
parse(_args, body, _ctx) { parse!(forbidden: body); Ok(BodylessFn) }
- layout(_, _) { Ok(commands![]) }
+ layout(_, _) { Ok(vec![]) }
}
impl PartialEq for BodylessFn {