Generalize layouting primitives 🛤

This commit is contained in:
Laurenz 2020-10-01 15:35:09 +02:00
parent e676ab53dd
commit f8770d2b2a
12 changed files with 372 additions and 352 deletions

View File

@ -43,18 +43,17 @@ impl Value {
/// A natural-language name of the type of this expression, e.g. /// A natural-language name of the type of this expression, e.g.
/// "identifier". /// "identifier".
pub fn name(&self) -> &'static str { pub fn name(&self) -> &'static str {
use Value::*;
match self { match self {
Ident(_) => "identifier", Self::Ident(_) => "identifier",
Str(_) => "string", Self::Str(_) => "string",
Bool(_) => "bool", Self::Bool(_) => "bool",
Number(_) => "number", Self::Number(_) => "number",
Length(_) => "length", Self::Length(_) => "length",
Color(_) => "color", Self::Color(_) => "color",
Dict(_) => "dict", Self::Dict(_) => "dict",
Tree(_) => "syntax tree", Self::Tree(_) => "syntax tree",
Func(_) => "function", Self::Func(_) => "function",
Commands(_) => "commands", Self::Commands(_) => "commands",
} }
} }
} }
@ -98,36 +97,34 @@ impl Spanned<Value> {
impl Debug for Value { impl Debug for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use Value::*;
match self { match self {
Ident(i) => i.fmt(f), Self::Ident(i) => i.fmt(f),
Str(s) => s.fmt(f), Self::Str(s) => s.fmt(f),
Bool(b) => b.fmt(f), Self::Bool(b) => b.fmt(f),
Number(n) => n.fmt(f), Self::Number(n) => n.fmt(f),
Length(s) => s.fmt(f), Self::Length(s) => s.fmt(f),
Color(c) => c.fmt(f), Self::Color(c) => c.fmt(f),
Dict(t) => t.fmt(f), Self::Dict(t) => t.fmt(f),
Tree(t) => t.fmt(f), Self::Tree(t) => t.fmt(f),
Func(_) => f.pad("<function>"), Self::Func(_) => f.pad("<function>"),
Commands(c) => c.fmt(f), Self::Commands(c) => c.fmt(f),
} }
} }
} }
impl PartialEq for Value { impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) { match (self, other) {
(Ident(a), Ident(b)) => a == b, (Self::Ident(a), Self::Ident(b)) => a == b,
(Str(a), Str(b)) => a == b, (Self::Str(a), Self::Str(b)) => a == b,
(Bool(a), Bool(b)) => a == b, (Self::Bool(a), Self::Bool(b)) => a == b,
(Number(a), Number(b)) => a == b, (Self::Number(a), Self::Number(b)) => a == b,
(Length(a), Length(b)) => a == b, (Self::Length(a), Self::Length(b)) => a == b,
(Color(a), Color(b)) => a == b, (Self::Color(a), Self::Color(b)) => a == b,
(Dict(a), Dict(b)) => a == b, (Self::Dict(a), Self::Dict(b)) => a == b,
(Tree(a), Tree(b)) => a == b, (Self::Tree(a), Self::Tree(b)) => a == b,
(Func(a), Func(b)) => Rc::ptr_eq(a, b), (Self::Func(a), Self::Func(b)) => Rc::ptr_eq(a, b),
(Commands(a), Commands(b)) => a == b, (Self::Commands(a), Self::Commands(b)) => a == b,
_ => false, _ => false,
} }
} }

View File

@ -3,7 +3,7 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::*; use std::ops::*;
use crate::layout::prelude::*; use crate::layout::primitive::*;
/// A value in two dimensions. /// A value in two dimensions.
#[derive(Default, Copy, Clone, Eq, PartialEq)] #[derive(Default, Copy, Clone, Eq, PartialEq)]
@ -44,22 +44,22 @@ impl<T: Clone> Value2<T> {
/// Get the specificed component. /// Get the specificed component.
pub fn get(self, axis: SpecAxis) -> T { pub fn get(self, axis: SpecAxis) -> T {
match axis { match axis {
Horizontal => self.x, SpecAxis::Horizontal => self.x,
Vertical => self.y, SpecAxis::Vertical => self.y,
} }
} }
/// Borrow the specificed component mutably. /// Borrow the specificed component mutably.
pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T { pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T {
match axis { match axis {
Horizontal => &mut self.x, SpecAxis::Horizontal => &mut self.x,
Vertical => &mut self.y, SpecAxis::Vertical => &mut self.y,
} }
} }
/// Return the primary value of this specialized 2D-value. /// Return the primary value of this specialized 2D-value.
pub fn primary(self, axes: LayoutAxes) -> T { pub fn primary(self, sys: LayoutSystem) -> T {
if axes.primary.axis() == Horizontal { if sys.primary.axis() == SpecAxis::Horizontal {
self.x self.x
} else { } else {
self.y self.y
@ -67,8 +67,8 @@ impl<T: Clone> Value2<T> {
} }
/// Borrow the primary value of this specialized 2D-value mutably. /// Borrow the primary value of this specialized 2D-value mutably.
pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut T { pub fn primary_mut(&mut self, sys: LayoutSystem) -> &mut T {
if axes.primary.axis() == Horizontal { if sys.primary.axis() == SpecAxis::Horizontal {
&mut self.x &mut self.x
} else { } else {
&mut self.y &mut self.y
@ -76,8 +76,8 @@ impl<T: Clone> Value2<T> {
} }
/// Return the secondary value of this specialized 2D-value. /// Return the secondary value of this specialized 2D-value.
pub fn secondary(self, axes: LayoutAxes) -> T { pub fn secondary(self, sys: LayoutSystem) -> T {
if axes.primary.axis() == Horizontal { if sys.primary.axis() == SpecAxis::Horizontal {
self.y self.y
} else { } else {
self.x self.x
@ -85,8 +85,8 @@ impl<T: Clone> Value2<T> {
} }
/// Borrow the secondary value of this specialized 2D-value mutably. /// Borrow the secondary value of this specialized 2D-value mutably.
pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut T { pub fn secondary_mut(&mut self, sys: LayoutSystem) -> &mut T {
if axes.primary.axis() == Horizontal { if sys.primary.axis() == SpecAxis::Horizontal {
&mut self.y &mut self.y
} else { } else {
&mut self.x &mut self.x
@ -94,22 +94,22 @@ impl<T: Clone> Value2<T> {
} }
/// Returns the generalized version of a `Size2D` dependent on the layouting /// Returns the generalized version of a `Size2D` dependent on the layouting
/// axes, that is: /// system, that is:
/// - `x` describes the primary axis instead of the horizontal one. /// - `x` describes the primary axis instead of the horizontal one.
/// - `y` describes the secondary axis instead of the vertical one. /// - `y` describes the secondary axis instead of the vertical one.
pub fn generalized(self, axes: LayoutAxes) -> Self { pub fn generalized(self, sys: LayoutSystem) -> Self {
match axes.primary.axis() { match sys.primary.axis() {
Horizontal => self, SpecAxis::Horizontal => self,
Vertical => Self { x: self.y, y: self.x }, SpecAxis::Vertical => Self { x: self.y, y: self.x },
} }
} }
/// Returns the specialized version of this generalized Size2D (inverse to /// Returns the specialized version of this generalized Size2D (inverse to
/// `generalized`). /// `generalized`).
pub fn specialized(self, axes: LayoutAxes) -> Self { pub fn specialized(self, sys: LayoutSystem) -> Self {
// In fact, generalized is its own inverse. For reasons of clarity // In fact, generalized is its own inverse. For reasons of clarity
// at the call site, we still have this second function. // at the call site, we still have this second function.
self.generalized(axes) self.generalized(sys)
} }
/// Swap the `x` and `y` values. /// Swap the `x` and `y` values.
@ -158,19 +158,19 @@ impl Size {
/// ///
/// This assumes the size to be generalized such that `x` corresponds to the /// This assumes the size to be generalized such that `x` corresponds to the
/// primary axis. /// primary axis.
pub fn anchor(self, align: LayoutAlign, axes: LayoutAxes) -> Self { pub fn anchor(self, align: LayoutAlign, sys: LayoutSystem) -> Self {
Size { Size {
x: anchor(self.x, align.primary, axes.primary), x: anchor(self.x, align.primary, sys.primary),
y: anchor(self.y, align.secondary, axes.secondary), y: anchor(self.y, align.secondary, sys.secondary),
} }
} }
} }
fn anchor(length: f64, align: GenAlign, dir: Dir) -> f64 { fn anchor(length: f64, align: GenAlign, dir: Dir) -> f64 {
match (dir.is_positive(), align) { match (dir.is_positive(), align) {
(true, Start) | (false, End) => 0.0, (true, GenAlign::Start) | (false, GenAlign::End) => 0.0,
(_, Center) => length / 2.0, (_, GenAlign::Center) => length / 2.0,
(true, End) | (false, Start) => length, (true, GenAlign::End) | (false, GenAlign::Start) => length,
} }
} }
@ -216,15 +216,15 @@ impl<T: Clone> Value4<T> {
/// ///
/// Center alignment is treated the same as origin alignment. /// Center alignment is treated the same as origin alignment.
pub fn get_mut(&mut self, mut dir: Dir, align: GenAlign) -> &mut T { pub fn get_mut(&mut self, mut dir: Dir, align: GenAlign) -> &mut T {
if align == End { if align == GenAlign::End {
dir = dir.inv(); dir = dir.inv();
} }
match dir { match dir {
LTR => &mut self.left, Dir::LTR => &mut self.left,
RTL => &mut self.right, Dir::RTL => &mut self.right,
TTB => &mut self.top, Dir::TTB => &mut self.top,
BTT => &mut self.bottom, Dir::BTT => &mut self.bottom,
} }
} }

View File

@ -24,8 +24,8 @@ pub struct LineLayouter {
pub struct LineContext { pub struct LineContext {
/// The spaces to layout into. /// The spaces to layout into.
pub spaces: LayoutSpaces, pub spaces: LayoutSpaces,
/// The initial layouting axes, which can be updated through `set_axes`. /// The initial layouting system, which can be updated through `set_sys`.
pub axes: LayoutAxes, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The alignment of the _resulting_ layout. This does not effect the line
/// layouting itself, but rather how the finished layout will be positioned /// layouting itself, but rather how the finished layout will be positioned
/// in a parent layout. /// in a parent layout.
@ -64,7 +64,7 @@ impl LineLayouter {
Self { Self {
stack: StackLayouter::new(StackContext { stack: StackLayouter::new(StackContext {
spaces: ctx.spaces.clone(), spaces: ctx.spaces.clone(),
axes: ctx.axes, sys: ctx.sys,
align: ctx.align, align: ctx.align,
repeat: ctx.repeat, repeat: ctx.repeat,
}), }),
@ -75,7 +75,7 @@ impl LineLayouter {
/// Add a layout. /// Add a layout.
pub fn add(&mut self, layout: BoxLayout) { pub fn add(&mut self, layout: BoxLayout) {
let axes = self.ctx.axes; let sys = self.ctx.sys;
if let Some(align) = self.run.align { if let Some(align) = self.run.align {
if layout.align.secondary != align.secondary { if layout.align.secondary != align.secondary {
@ -92,7 +92,7 @@ impl LineLayouter {
} else if layout.align.primary > align.primary { } else if layout.align.primary > align.primary {
let mut rest_run = LineRun::new(); let mut rest_run = LineRun::new();
let usable = self.stack.usable().primary(axes); let usable = self.stack.usable().primary(sys);
rest_run.usable = Some(match layout.align.primary { rest_run.usable = Some(match layout.align.primary {
GenAlign::Start => unreachable!("start > x"), GenAlign::Start => unreachable!("start > x"),
GenAlign::Center => usable - 2.0 * self.run.size.x, GenAlign::Center => usable - 2.0 * self.run.size.x,
@ -112,7 +112,7 @@ impl LineLayouter {
self.add_primary_spacing(spacing, SpacingKind::Hard); self.add_primary_spacing(spacing, SpacingKind::Hard);
} }
let size = layout.size.generalized(axes); let size = layout.size.generalized(sys);
if !self.usable().fits(size) { if !self.usable().fits(size) {
if !self.line_is_empty() { if !self.line_is_empty() {
@ -148,7 +148,7 @@ impl LineLayouter {
/// needed. /// needed.
fn usable(&self) -> Size { fn usable(&self) -> Size {
// The base is the usable space of the stack layouter. // The base is the usable space of the stack layouter.
let mut usable = self.stack.usable().generalized(self.ctx.axes); let mut usable = self.stack.usable().generalized(self.ctx.sys);
// If there was another run already, override the stack's size. // If there was another run already, override the stack's size.
if let Some(primary) = self.run.usable { if let Some(primary) = self.run.usable {
@ -190,11 +190,11 @@ impl LineLayouter {
self.stack.add_spacing(spacing, kind) self.stack.add_spacing(spacing, kind)
} }
/// Update the layouting axes. /// Update the layouting system.
pub fn set_axes(&mut self, axes: LayoutAxes) { pub fn set_sys(&mut self, sys: LayoutSystem) {
self.finish_line_if_not_empty(); self.finish_line_if_not_empty();
self.ctx.axes = axes; self.ctx.sys = sys;
self.stack.set_axes(axes) self.stack.set_sys(sys)
} }
/// Update the layouting spaces. /// Update the layouting spaces.
@ -215,7 +215,7 @@ impl LineLayouter {
/// it will fit into this layouter's underlying stack. /// it will fit into this layouter's underlying stack.
pub fn remaining(&self) -> LayoutSpaces { pub fn remaining(&self) -> LayoutSpaces {
let mut spaces = self.stack.remaining(); let mut spaces = self.stack.remaining();
*spaces[0].size.secondary_mut(self.ctx.axes) -= self.run.size.y; *spaces[0].size.secondary_mut(self.ctx.sys) -= self.run.size.y;
spaces spaces
} }
@ -244,9 +244,9 @@ impl LineLayouter {
let layouts = std::mem::take(&mut self.run.layouts); let layouts = std::mem::take(&mut self.run.layouts);
for (offset, layout) in layouts { for (offset, layout) in layouts {
let x = match self.ctx.axes.primary.is_positive() { let x = match self.ctx.sys.primary.is_positive() {
true => offset, true => offset,
false => self.run.size.x - offset - layout.size.primary(self.ctx.axes), false => self.run.size.x - offset - layout.size.primary(self.ctx.sys),
}; };
let pos = Size::with_x(x); let pos = Size::with_x(x);
@ -254,8 +254,8 @@ impl LineLayouter {
} }
self.stack.add(BoxLayout { self.stack.add(BoxLayout {
size: self.run.size.specialized(self.ctx.axes), size: self.run.size.specialized(self.ctx.sys),
align: self.run.align.unwrap_or(LayoutAlign::new(Start, Start)), align: self.run.align.unwrap_or(LayoutAlign::START),
elements, elements,
}); });

View File

@ -7,17 +7,6 @@ pub mod stack;
pub mod text; pub mod text;
mod tree; mod tree;
/// Basic types used across the layouting engine.
pub mod prelude {
pub use super::primitive::*;
pub use super::{layout, BoxLayout, LayoutContext, LayoutSpace, MultiLayout};
pub use Dir::*;
pub use GenAlign::*;
pub use GenAxis::*;
pub use SpecAlign::*;
pub use SpecAxis::*;
}
pub use primitive::*; pub use primitive::*;
pub use tree::layout_tree as layout; pub use tree::layout_tree as layout;
@ -28,7 +17,6 @@ use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::SynTree; use crate::syntax::SynTree;
use elements::LayoutElements; use elements::LayoutElements;
use prelude::*;
/// A collection of layouts. /// A collection of layouts.
pub type MultiLayout = Vec<BoxLayout>; pub type MultiLayout = Vec<BoxLayout>;
@ -60,8 +48,8 @@ pub struct LayoutContext<'a> {
/// Whether to spill over into copies of the last space or finish layouting /// Whether to spill over into copies of the last space or finish layouting
/// when the last space is used up. /// when the last space is used up.
pub repeat: bool, pub repeat: bool,
/// The axes along which content is laid out. /// The system into which content is laid out.
pub axes: LayoutAxes, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The alignment of the _resulting_ layout. This does not effect the line
/// layouting itself, but rather how the finished layout will be positioned /// layouting itself, but rather how the finished layout will be positioned
/// in a parent layout. /// in a parent layout.
@ -146,7 +134,57 @@ pub enum Command {
/// Update the alignment for future boxes added to this layouting process. /// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlign), SetAlignment(LayoutAlign),
/// Update the layouting axes along which future boxes will be laid /// Update the layouting system along which future boxes will be laid
/// out. This ends the current line. /// out. This ends the current line.
SetAxes(LayoutAxes), SetSystem(LayoutSystem),
}
/// Defines how spacing interacts with surrounding spacing.
///
/// There are two options for interaction: Hard and soft spacing. Typically,
/// hard spacing is used when a fixed amount of space needs to be inserted no
/// matter what. In contrast, soft spacing can be used to insert a default
/// spacing between e.g. two words or paragraphs that can still be overridden by
/// a hard space.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SpacingKind {
/// Hard spaces are always laid out and consume surrounding soft space.
Hard,
/// Soft spaces are not laid out if they are touching a hard space and
/// consume neighbouring soft spaces with higher levels.
Soft(u32),
}
impl SpacingKind {
/// The standard spacing kind used for paragraph spacing.
pub const PARAGRAPH: Self = Self::Soft(1);
/// The standard spacing kind used for line spacing.
pub const LINE: Self = Self::Soft(2);
/// The standard spacing kind used for word spacing.
pub const WORD: Self = Self::Soft(1);
}
/// The spacing kind of the most recently inserted item in a layouting process.
///
/// Since the last inserted item may not be spacing at all, this can be `None`.
#[derive(Debug, Copy, Clone, PartialEq)]
enum LastSpacing {
/// The last item was hard spacing.
Hard,
/// The last item was soft spacing with the given width and level.
Soft(f64, u32),
/// The last item wasn't spacing.
None,
}
impl LastSpacing {
/// The width of the soft space if this is a soft space or zero otherwise.
fn soft_or_zero(self) -> f64 {
match self {
LastSpacing::Soft(space, _) => space,
_ => 0.0,
}
}
} }

View File

@ -2,46 +2,33 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::prelude::*; /// Specifies the directions into which content is laid out.
/// Specifies the axes along content is laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LayoutAxes {
pub primary: Dir,
pub secondary: Dir,
}
impl LayoutAxes {
/// Create a new instance from the two directions.
/// ///
/// # Panics /// The primary component defines into which direction text and lines flow and the
/// This function panics if the directions are aligned, i.e. if they are /// secondary into which paragraphs and pages grow.
/// on the same axis. pub type LayoutSystem = Gen2<Dir>;
pub fn new(primary: Dir, secondary: Dir) -> Self {
if primary.axis() == secondary.axis() {
panic!("directions {} and {} are aligned", primary, secondary);
}
Self { primary, secondary }
}
/// Return the direction of the specified generic axis. impl Default for LayoutSystem {
pub fn get(self, axis: GenAxis) -> Dir { fn default() -> Self {
match axis { Self::new(Dir::LTR, Dir::TTB)
Primary => self.primary,
Secondary => self.secondary,
} }
} }
/// Borrow the direction of the specified generic axis mutably. /// Specifies where to align a layout in a parent container.
pub fn get_mut(&mut self, axis: GenAxis) -> &mut Dir { pub type LayoutAlign = Gen2<GenAlign>;
match axis {
Primary => &mut self.primary, impl LayoutAlign {
Secondary => &mut self.secondary, /// The layout alignment that has both components set to `Start`.
} pub const START: Self = Self {
} primary: GenAlign::Start,
secondary: GenAlign::Start,
};
} }
/// Directions along which content is laid out. /// Whether to expand a layout to an area's full size or shrink it to fit its content.
pub type LayoutExpansion = Spec2<bool>;
/// The four directions into which content can be laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dir { pub enum Dir {
/// Left to right. /// Left to right.
@ -55,11 +42,31 @@ pub enum Dir {
} }
impl Dir { impl Dir {
/// The side this direction starts at.
pub fn start(self) -> Side {
match self {
Self::LTR => Side::Left,
Self::RTL => Side::Right,
Self::TTB => Side::Top,
Self::BTT => Side::Bottom,
}
}
/// The side this direction ends at.
pub fn end(self) -> Side {
match self {
Self::LTR => Side::Right,
Self::RTL => Side::Left,
Self::TTB => Side::Bottom,
Self::BTT => Side::Top,
}
}
/// The specific axis this direction belongs to. /// The specific axis this direction belongs to.
pub fn axis(self) -> SpecAxis { pub fn axis(self) -> SpecAxis {
match self { match self {
LTR | RTL => Horizontal, Self::LTR | Self::RTL => SpecAxis::Horizontal,
TTB | BTT => Vertical, Self::TTB | Self::BTT => SpecAxis::Vertical,
} }
} }
@ -68,8 +75,8 @@ impl Dir {
/// The positive directions are left-to-right and top-to-bottom. /// The positive directions are left-to-right and top-to-bottom.
pub fn is_positive(self) -> bool { pub fn is_positive(self) -> bool {
match self { match self {
LTR | TTB => true, Self::LTR | Self::TTB => true,
RTL | BTT => false, Self::RTL | Self::BTT => false,
} }
} }
@ -84,10 +91,10 @@ impl Dir {
/// The inverse direction. /// The inverse direction.
pub fn inv(self) -> Self { pub fn inv(self) -> Self {
match self { match self {
LTR => RTL, Self::LTR => Self::RTL,
RTL => LTR, Self::RTL => Self::LTR,
TTB => BTT, Self::TTB => Self::BTT,
BTT => TTB, Self::BTT => Self::TTB,
} }
} }
} }
@ -95,10 +102,10 @@ impl Dir {
impl Display for Dir { impl Display for Dir {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self { f.pad(match self {
LTR => "ltr", Self::LTR => "ltr",
RTL => "rtl", Self::RTL => "rtl",
TTB => "ttb", Self::TTB => "ttb",
BTT => "btt", Self::BTT => "btt",
}) })
} }
} }
@ -106,24 +113,24 @@ impl Display for Dir {
/// The two generic layouting axes. /// The two generic layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GenAxis { pub enum GenAxis {
/// The primary layouting direction along which text and lines flow. /// The primary layouting direction into which text and lines flow.
Primary, Primary,
/// The secondary layouting direction along which paragraphs grow. /// The secondary layouting direction into which paragraphs grow.
Secondary, Secondary,
} }
impl GenAxis { impl GenAxis {
/// The specific version of this axis in the given system of axes. /// The specific version of this axis in the given layout system.
pub fn to_specific(self, axes: LayoutAxes) -> SpecAxis { pub fn to_spec(self, sys: LayoutSystem) -> SpecAxis {
axes.get(self).axis() sys.get(self).axis()
} }
} }
impl Display for GenAxis { impl Display for GenAxis {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self { f.pad(match self {
Primary => "primary", Self::Primary => "primary",
Secondary => "secondary", Self::Secondary => "secondary",
}) })
} }
} }
@ -138,12 +145,12 @@ pub enum SpecAxis {
} }
impl SpecAxis { impl SpecAxis {
/// The generic version of this axis in the given system of axes. /// The generic version of this axis in the given layout system.
pub fn to_generic(self, axes: LayoutAxes) -> GenAxis { pub fn to_gen(self, sys: LayoutSystem) -> GenAxis {
if self == axes.primary.axis() { if self == sys.primary.axis() {
Primary GenAxis::Primary
} else { } else {
Secondary GenAxis::Secondary
} }
} }
} }
@ -151,40 +158,19 @@ impl SpecAxis {
impl Display for SpecAxis { impl Display for SpecAxis {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self { f.pad(match self {
Horizontal => "horizontal", Self::Horizontal => "horizontal",
Vertical => "vertical", Self::Vertical => "vertical",
}) })
} }
} }
/// Specifies where to align a layout in a parent container. /// A side of a container.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LayoutAlign { pub enum Side {
pub primary: GenAlign, Left,
pub secondary: GenAlign, Top,
} Right,
Bottom,
impl LayoutAlign {
/// Create a new instance from the two alignments.
pub fn new(primary: GenAlign, secondary: GenAlign) -> Self {
Self { primary, secondary }
}
/// Return the alignment for the specified generic axis.
pub fn get(self, axis: GenAxis) -> GenAlign {
match axis {
Primary => self.primary,
Secondary => self.secondary,
}
}
/// Borrow the alignment for the specified generic axis mutably.
pub fn get_mut(&mut self, axis: GenAxis) -> &mut GenAlign {
match axis {
Primary => &mut self.primary,
Secondary => &mut self.secondary,
}
}
} }
/// Where to align content along an axis in a generic context. /// Where to align content along an axis in a generic context.
@ -199,9 +185,9 @@ impl GenAlign {
/// The inverse alignment. /// The inverse alignment.
pub fn inv(self) -> Self { pub fn inv(self) -> Self {
match self { match self {
Start => End, Self::Start => Self::End,
Center => Center, Self::Center => Self::Center,
End => Start, Self::End => Self::Start,
} }
} }
} }
@ -209,9 +195,9 @@ impl GenAlign {
impl Display for GenAlign { impl Display for GenAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self { f.pad(match self {
Start => "start", Self::Start => "start",
Center => "center", Self::Center => "center",
End => "end", Self::End => "end",
}) })
} }
} }
@ -232,30 +218,29 @@ impl SpecAlign {
/// Returns `None` if this is `Center` since the axis is unknown. /// Returns `None` if this is `Center` since the axis is unknown.
pub fn axis(self) -> Option<SpecAxis> { pub fn axis(self) -> Option<SpecAxis> {
match self { match self {
Self::Left => Some(Horizontal), Self::Left => Some(SpecAxis::Horizontal),
Self::Right => Some(Horizontal), Self::Right => Some(SpecAxis::Horizontal),
Self::Top => Some(Vertical), Self::Top => Some(SpecAxis::Vertical),
Self::Bottom => Some(Vertical), Self::Bottom => Some(SpecAxis::Vertical),
Self::Center => None, Self::Center => None,
} }
} }
/// The generic version of this alignment in the given system of axes. /// The generic version of this alignment in the given layout system.
pub fn to_generic(self, axes: LayoutAxes) -> GenAlign { pub fn to_gen(self, sys: LayoutSystem) -> GenAlign {
let get = |spec: SpecAxis, align: GenAlign| { let get = |spec: SpecAxis, positive: GenAlign| {
let axis = spec.to_generic(axes); if sys.get(spec.to_gen(sys)).is_positive() {
if axes.get(axis).is_positive() { positive
align
} else { } else {
align.inv() positive.inv()
} }
}; };
match self { match self {
Self::Left => get(Horizontal, Start), Self::Left => get(SpecAxis::Horizontal, GenAlign::Start),
Self::Right => get(Horizontal, End), Self::Right => get(SpecAxis::Horizontal, GenAlign::End),
Self::Top => get(Vertical, Start), Self::Top => get(SpecAxis::Vertical, GenAlign::Start),
Self::Bottom => get(Vertical, End), Self::Bottom => get(SpecAxis::Vertical, GenAlign::End),
Self::Center => GenAlign::Center, Self::Center => GenAlign::Center,
} }
} }
@ -273,85 +258,82 @@ impl Display for SpecAlign {
} }
} }
/// Specifies whether to expand a layout to the full size of the space it is /// A generic container with two components for the two generic axes.
/// laid out in or to shrink it to fit the content. #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct Gen2<T> {
pub struct LayoutExpansion { /// The primary component.
/// Whether to expand on the horizontal axis. pub primary: T,
pub horizontal: bool, /// The secondary component.
/// Whether to expand on the vertical axis. pub secondary: T,
pub vertical: bool,
} }
impl LayoutExpansion { impl<T> Gen2<T> {
/// Create a new instance from the two values. /// Create a new instance from the two components.
pub fn new(horizontal: bool, vertical: bool) -> Self { pub fn new(primary: T, secondary: T) -> Self {
Self { primary, secondary }
}
/// Return the component for the specified generic axis.
pub fn get(self, axis: GenAxis) -> T {
match axis {
GenAxis::Primary => self.primary,
GenAxis::Secondary => self.secondary,
}
}
/// Borrow the component for the specified generic axis.
pub fn get_ref(&mut self, axis: GenAxis) -> &T {
match axis {
GenAxis::Primary => &mut self.primary,
GenAxis::Secondary => &mut self.secondary,
}
}
/// Borrow the component for the specified generic axis mutably.
pub fn get_mut(&mut self, axis: GenAxis) -> &mut T {
match axis {
GenAxis::Primary => &mut self.primary,
GenAxis::Secondary => &mut self.secondary,
}
}
}
/// A generic container with two components for the two specific axes.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Spec2<T> {
/// The horizontal component.
pub horizontal: T,
/// The vertical component.
pub vertical: T,
}
impl<T> Spec2<T> {
/// Create a new instance from the two components.
pub fn new(horizontal: T, vertical: T) -> Self {
Self { horizontal, vertical } Self { horizontal, vertical }
} }
/// Return the expansion value for the given specific axis. /// Return the component for the given specific axis.
pub fn get(self, axis: SpecAxis) -> bool { pub fn get(self, axis: SpecAxis) -> T {
match axis { match axis {
Horizontal => self.horizontal, SpecAxis::Horizontal => self.horizontal,
Vertical => self.vertical, SpecAxis::Vertical => self.vertical,
} }
} }
/// Borrow the expansion value for the given specific axis mutably. /// Borrow the component for the given specific axis.
pub fn get_mut(&mut self, axis: SpecAxis) -> &mut bool { pub fn get_ref(&mut self, axis: SpecAxis) -> &T {
match axis { match axis {
Horizontal => &mut self.horizontal, SpecAxis::Horizontal => &mut self.horizontal,
Vertical => &mut self.vertical, SpecAxis::Vertical => &mut self.vertical,
}
} }
} }
/// Defines how spacing interacts with surrounding spacing. /// Borrow the component for the given specific axis mutably.
/// pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T {
/// There are two options for interaction: Hard and soft spacing. Typically, match axis {
/// hard spacing is used when a fixed amount of space needs to be inserted no SpecAxis::Horizontal => &mut self.horizontal,
/// matter what. In contrast, soft spacing can be used to insert a default SpecAxis::Vertical => &mut self.vertical,
/// spacing between e.g. two words or paragraphs that can still be overridden by
/// a hard space.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SpacingKind {
/// Hard spaces are always laid out and consume surrounding soft space.
Hard,
/// Soft spaces are not laid out if they are touching a hard space and
/// consume neighbouring soft spaces with higher levels.
Soft(u32),
}
impl SpacingKind {
/// The standard spacing kind used for paragraph spacing.
pub const PARAGRAPH: Self = Self::Soft(1);
/// The standard spacing kind used for line spacing.
pub const LINE: Self = Self::Soft(2);
/// The standard spacing kind used for word spacing.
pub const WORD: Self = Self::Soft(1);
}
/// The spacing kind of the most recently inserted item in a layouting process.
///
/// Since the last inserted item may not be spacing at all, this can be `None`.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum LastSpacing {
/// The last item was hard spacing.
Hard,
/// The last item was soft spacing with the given width and level.
Soft(f64, u32),
/// The last item wasn't spacing.
None,
}
impl LastSpacing {
/// The width of the soft space if this is a soft space or zero otherwise.
pub fn soft_or_zero(self) -> f64 {
match self {
LastSpacing::Soft(space, _) => space,
_ => 0.0,
} }
} }
} }

View File

@ -35,8 +35,8 @@ pub struct StackLayouter {
pub struct StackContext { pub struct StackContext {
/// The spaces to layout into. /// The spaces to layout into.
pub spaces: LayoutSpaces, pub spaces: LayoutSpaces,
/// The initial layouting axes, which can be updated through `set_axes`. /// The initial layouting system, which can be updated through `set_sys`.
pub axes: LayoutAxes, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The alignment of the _resulting_ layout. This does not effect the line
/// layouting itself, but rather how the finished layout will be positioned /// layouting itself, but rather how the finished layout will be positioned
/// in a parent layout. /// in a parent layout.
@ -46,7 +46,7 @@ pub struct StackContext {
pub repeat: bool, pub repeat: bool,
} }
/// A layout space composed of subspaces which can have different axes and /// A layout space composed of subspaces which can have different systems and
/// alignments. /// alignments.
struct Space { struct Space {
/// The index of this space in `ctx.spaces`. /// The index of this space in `ctx.spaces`.
@ -54,7 +54,7 @@ struct Space {
/// Whether to include a layout for this space even if it would be empty. /// Whether to include a layout for this space even if it would be empty.
hard: bool, hard: bool,
/// The so-far accumulated layouts. /// The so-far accumulated layouts.
layouts: Vec<(LayoutAxes, BoxLayout)>, layouts: Vec<(LayoutSystem, BoxLayout)>,
/// The specialized size of this space. /// The specialized size of this space.
size: Size, size: Size,
/// The specialized remaining space. /// The specialized remaining space.
@ -103,11 +103,11 @@ impl StackLayouter {
} }
// Change the usable space and size of the space. // Change the usable space and size of the space.
self.update_metrics(layout.size.generalized(self.ctx.axes)); self.update_metrics(layout.size.generalized(self.ctx.sys));
// Add the box to the vector and remember that spacings are allowed // Add the box to the vector and remember that spacings are allowed
// again. // again.
self.space.layouts.push((self.ctx.axes, layout)); self.space.layouts.push((self.ctx.sys, layout));
self.space.last_spacing = LastSpacing::None; self.space.last_spacing = LastSpacing::None;
} }
@ -126,13 +126,13 @@ impl StackLayouter {
// A hard space is simply an empty box. // A hard space is simply an empty box.
SpacingKind::Hard => { SpacingKind::Hard => {
// Reduce the spacing such that it definitely fits. // Reduce the spacing such that it definitely fits.
spacing = spacing.min(self.space.usable.secondary(self.ctx.axes)); spacing = spacing.min(self.space.usable.secondary(self.ctx.sys));
let size = Size::with_y(spacing); let size = Size::with_y(spacing);
self.update_metrics(size); self.update_metrics(size);
self.space.layouts.push((self.ctx.axes, BoxLayout { self.space.layouts.push((self.ctx.sys, BoxLayout {
size: size.specialized(self.ctx.axes), size: size.specialized(self.ctx.sys),
align: LayoutAlign::new(Start, Start), align: LayoutAlign::START,
elements: LayoutElements::new(), elements: LayoutElements::new(),
})); }));
@ -156,10 +156,10 @@ impl StackLayouter {
} }
fn update_metrics(&mut self, added: Size) { fn update_metrics(&mut self, added: Size) {
let axes = self.ctx.axes; let sys = self.ctx.sys;
let mut size = self.space.size.generalized(axes); let mut size = self.space.size.generalized(sys);
let mut extra = self.space.extra.generalized(axes); let mut extra = self.space.extra.generalized(sys);
size.x += (added.x - extra.x).max(0.0); size.x += (added.x - extra.x).max(0.0);
size.y += (added.y - extra.y).max(0.0); size.y += (added.y - extra.y).max(0.0);
@ -167,16 +167,17 @@ impl StackLayouter {
extra.x = extra.x.max(added.x); extra.x = extra.x.max(added.x);
extra.y = (extra.y - added.y).max(0.0); extra.y = (extra.y - added.y).max(0.0);
self.space.size = size.specialized(axes); self.space.size = size.specialized(sys);
self.space.extra = extra.specialized(axes); self.space.extra = extra.specialized(sys);
*self.space.usable.secondary_mut(axes) -= added.y; *self.space.usable.secondary_mut(sys) -= added.y;
} }
/// Returns true if a space break is necessary. /// Returns true if a space break is necessary.
fn update_rulers(&mut self, align: LayoutAlign) -> bool { fn update_rulers(&mut self, align: LayoutAlign) -> bool {
let allowed = self.is_fitting_alignment(align); let allowed = self.is_fitting_alignment(align);
if allowed { if allowed {
*self.space.rulers.get_mut(self.ctx.axes.secondary, Start) = align.secondary; *self.space.rulers.get_mut(self.ctx.sys.secondary, GenAlign::Start) =
align.secondary;
} }
allowed allowed
} }
@ -184,23 +185,23 @@ impl StackLayouter {
/// Whether a layout with the given alignment can still be layouted into the /// Whether a layout with the given alignment can still be layouted into the
/// active space or a space break is necessary. /// active space or a space break is necessary.
pub(crate) fn is_fitting_alignment(&mut self, align: LayoutAlign) -> bool { pub(crate) fn is_fitting_alignment(&mut self, align: LayoutAlign) -> bool {
self.is_fitting_axis(self.ctx.axes.primary, align.primary) self.is_fitting_axis(self.ctx.sys.primary, align.primary)
&& self.is_fitting_axis(self.ctx.axes.secondary, align.secondary) && self.is_fitting_axis(self.ctx.sys.secondary, align.secondary)
} }
fn is_fitting_axis(&mut self, dir: Dir, align: GenAlign) -> bool { fn is_fitting_axis(&mut self, dir: Dir, align: GenAlign) -> bool {
align >= *self.space.rulers.get_mut(dir, Start) align >= *self.space.rulers.get_mut(dir, GenAlign::Start)
&& align <= self.space.rulers.get_mut(dir, End).inv() && align <= self.space.rulers.get_mut(dir, GenAlign::End).inv()
} }
/// Update the layouting axes. /// Update the layouting system.
pub fn set_axes(&mut self, axes: LayoutAxes) { pub fn set_sys(&mut self, sys: LayoutSystem) {
// Forget the spacing because it is not relevant anymore. // Forget the spacing because it is not relevant anymore.
if axes.secondary != self.ctx.axes.secondary { if sys.secondary != self.ctx.sys.secondary {
self.space.last_spacing = LastSpacing::Hard; self.space.last_spacing = LastSpacing::Hard;
} }
self.ctx.axes = axes; self.ctx.sys = sys;
} }
/// Update the layouting spaces. /// Update the layouting spaces.
@ -253,7 +254,7 @@ impl StackLayouter {
pub fn usable(&self) -> Size { pub fn usable(&self) -> Size {
self.space.usable self.space.usable
- Size::with_y(self.space.last_spacing.soft_or_zero()) - Size::with_y(self.space.last_spacing.soft_or_zero())
.specialized(self.ctx.axes) .specialized(self.ctx.sys)
} }
/// Whether the current layout space is empty. /// Whether the current layout space is empty.
@ -308,7 +309,7 @@ impl StackLayouter {
bottom: start.y + self.space.size.y, bottom: start.y + self.space.size.y,
}; };
for (axes, layout) in &self.space.layouts { for (sys, layout) in &self.space.layouts {
// First, we store the bounds calculated so far (which were reduced // First, we store the bounds calculated so far (which were reduced
// by the predecessors of this layout) as the initial bounding box // by the predecessors of this layout) as the initial bounding box
// of this layout. // of this layout.
@ -318,8 +319,8 @@ impl StackLayouter {
// layout uses up space from the origin to the end. Thus, it reduces // 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 // the usable space for following layouts at it's origin by its
// extent along the secondary axis. // extent along the secondary axis.
*bound.get_mut(axes.secondary, Start) += *bound.get_mut(sys.secondary, GenAlign::Start) +=
axes.secondary.factor() * layout.size.secondary(*axes); sys.secondary.factor() * layout.size.secondary(*sys);
} }
// ------------------------------------------------------------------ // // ------------------------------------------------------------------ //
@ -329,28 +330,29 @@ impl StackLayouter {
// The `x` field stores the maximal primary extent in one axis-aligned // The `x` field stores the maximal primary extent in one axis-aligned
// run, while the `y` fields stores the accumulated secondary extent. // run, while the `y` fields stores the accumulated secondary extent.
let mut extent = Size::ZERO; let mut extent = Size::ZERO;
let mut rotation = Vertical; let mut rotation = SpecAxis::Vertical;
for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() { for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() {
let (axes, layout) = entry; let (sys, layout) = entry;
// When the axes are rotated, the the maximal primary size // When the axes are rotated, the maximal primary size (`extent.x`)
// (`extent.x`) dictates how much secondary extent the whole run // dictates how much secondary extent the whole run had. This value
// had. This value is thus stored in `extent.y`. The primary extent // is thus stored in `extent.y`. The primary extent is reset for
// is reset for this new axis-aligned run. // this new axis-aligned run.
if rotation != axes.secondary.axis() { if rotation != sys.secondary.axis() {
extent.y = extent.x; extent.y = extent.x;
extent.x = 0.0; extent.x = 0.0;
rotation = axes.secondary.axis(); rotation = sys.secondary.axis();
} }
// We reduce the bounding box of this layout at it's end by the // 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, // accumulated secondary extent of all layouts we have seen so far,
// which are the layouts after this one since we iterate reversed. // which are the layouts after this one since we iterate reversed.
*bound.get_mut(axes.secondary, End) -= axes.secondary.factor() * extent.y; *bound.get_mut(sys.secondary, GenAlign::End) -=
sys.secondary.factor() * extent.y;
// Then, we add this layout's secondary extent to the accumulator. // Then, we add this layout's secondary extent to the accumulator.
let size = layout.size.generalized(*axes); let size = layout.size.generalized(*sys);
extent.x = extent.x.max(size.x); extent.x = extent.x.max(size.x);
extent.y += size.y; extent.y += size.y;
} }
@ -362,17 +364,17 @@ impl StackLayouter {
let mut elements = LayoutElements::new(); let mut elements = LayoutElements::new();
let layouts = std::mem::take(&mut self.space.layouts); let layouts = std::mem::take(&mut self.space.layouts);
for ((axes, layout), bound) in layouts.into_iter().zip(bounds) { for ((sys, layout), bound) in layouts.into_iter().zip(bounds) {
let size = layout.size.specialized(axes); let size = layout.size.specialized(sys);
let align = layout.align; let align = layout.align;
// The space in which this layout is aligned is given by the // The space in which this layout is aligned is given by the
// distances between the borders of it's bounding box. // distances between the borders of it's bounding box.
let usable = Size::new(bound.right - bound.left, bound.bottom - bound.top) let usable = Size::new(bound.right - bound.left, bound.bottom - bound.top)
.generalized(axes); .generalized(sys);
let local = usable.anchor(align, axes) - size.anchor(align, axes); let local = usable.anchor(align, sys) - size.anchor(align, sys);
let pos = Size::new(bound.left, bound.top) + local.specialized(axes); let pos = Size::new(bound.left, bound.top) + local.specialized(sys);
elements.extend_offset(pos, layout.elements); elements.extend_offset(pos, layout.elements);
} }
@ -404,7 +406,7 @@ impl Space {
size: Size::ZERO, size: Size::ZERO,
usable, usable,
extra: Size::ZERO, extra: Size::ZERO,
rulers: Value4::with_all(Start), rulers: Value4::with_all(GenAlign::Start),
last_spacing: LastSpacing::Hard, last_spacing: LastSpacing::Hard,
} }
} }

View File

@ -29,7 +29,7 @@ impl<'a> TreeLayouter<'a> {
Self { Self {
layouter: LineLayouter::new(LineContext { layouter: LineLayouter::new(LineContext {
spaces: ctx.spaces.clone(), spaces: ctx.spaces.clone(),
axes: ctx.axes, sys: ctx.sys,
align: ctx.align, align: ctx.align,
repeat: ctx.repeat, repeat: ctx.repeat,
line_spacing: ctx.style.text.line_spacing(), line_spacing: ctx.style.text.line_spacing(),
@ -107,7 +107,7 @@ impl<'a> TreeLayouter<'a> {
layout_text(text, TextContext { layout_text(text, TextContext {
loader: &mut self.ctx.loader.borrow_mut(), loader: &mut self.ctx.loader.borrow_mut(),
style: &self.style.text, style: &self.style.text,
dir: self.ctx.axes.primary, dir: self.ctx.sys.primary,
align: self.ctx.align, align: self.ctx.align,
}) })
.await, .await,
@ -170,15 +170,14 @@ impl<'a> TreeLayouter<'a> {
async fn execute_command(&mut self, command: Command, span: Span) { async fn execute_command(&mut self, command: Command, span: Span) {
use Command::*; use Command::*;
match command { match command {
LayoutSyntaxTree(tree) => self.layout_tree(&tree).await, LayoutSyntaxTree(tree) => self.layout_tree(&tree).await,
Add(layout) => self.layouter.add(layout), Add(layout) => self.layouter.add(layout),
AddMultiple(layouts) => self.layouter.add_multiple(layouts), AddMultiple(layouts) => self.layouter.add_multiple(layouts),
AddSpacing(space, kind, axis) => match axis { AddSpacing(space, kind, axis) => match axis {
Primary => self.layouter.add_primary_spacing(space, kind), GenAxis::Primary => self.layouter.add_primary_spacing(space, kind),
Secondary => self.layouter.add_secondary_spacing(space, kind), GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind),
}, },
BreakLine => self.layouter.finish_line(), BreakLine => self.layouter.finish_line(),
@ -223,9 +222,9 @@ impl<'a> TreeLayouter<'a> {
} }
SetAlignment(align) => self.ctx.align = align, SetAlignment(align) => self.ctx.align = align,
SetAxes(axes) => { SetSystem(sys) => {
self.layouter.set_axes(axes); self.layouter.set_sys(sys);
self.ctx.axes = axes; self.ctx.sys = sys;
} }
} }
} }

View File

@ -47,7 +47,10 @@ use std::pin::Pin;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::eval::{Scope, Value}; use crate::eval::{Scope, Value};
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::layout::{Commands, MultiLayout}; use crate::layout::{
layout, Commands, Dir, GenAlign, LayoutAlign, LayoutContext, LayoutExpansion,
LayoutSpace, LayoutSystem, MultiLayout,
};
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::{Decoration, Offset, Pos, SpanVec, SynTree}; use crate::syntax::{Decoration, Offset, Pos, SpanVec, SynTree};
@ -90,8 +93,6 @@ impl Typesetter {
/// Layout a syntax tree and return the produced layout. /// Layout a syntax tree and return the produced layout.
pub async fn layout(&self, tree: &SynTree) -> Pass<MultiLayout> { pub async fn layout(&self, tree: &SynTree) -> Pass<MultiLayout> {
use crate::layout::prelude::*;
let margins = self.style.page.margins(); let margins = self.style.page.margins();
layout(&tree, LayoutContext { layout(&tree, LayoutContext {
loader: &self.loader, loader: &self.loader,
@ -104,8 +105,8 @@ impl Typesetter {
expansion: LayoutExpansion::new(true, true), expansion: LayoutExpansion::new(true, true),
}], }],
repeat: true, repeat: true,
axes: LayoutAxes::new(LTR, TTB), sys: LayoutSystem::new(Dir::LTR, Dir::TTB),
align: LayoutAlign::new(Start, Start), align: LayoutAlign::new(GenAlign::Start, GenAlign::Start),
root: true, root: true,
}) })
.await .await

View File

@ -23,8 +23,8 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
let all = args let all = args
.take_all_num_vals::<Spanned<SpecAlign>>() .take_all_num_vals::<Spanned<SpecAlign>>()
.map(|align| (align.v.axis(), align)) .map(|align| (align.v.axis(), align))
.chain(h.into_iter().map(|align| (Some(Horizontal), align))) .chain(h.into_iter().map(|align| (Some(SpecAxis::Horizontal), align)))
.chain(v.into_iter().map(|align| (Some(Vertical), align))); .chain(v.into_iter().map(|align| (Some(SpecAxis::Vertical), align)));
let mut aligns = ctx.align; let mut aligns = ctx.align;
let mut had = [false; 2]; let mut had = [false; 2];
@ -43,8 +43,8 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
} else if had[axis as usize] { } else if had[axis as usize] {
error!(@f, align.span, "duplicate alignment for {} axis", axis); error!(@f, align.span, "duplicate alignment for {} axis", axis);
} else { } else {
let gen_align = align.v.to_generic(ctx.axes); let gen_align = align.v.to_gen(ctx.sys);
*aligns.get_mut(axis.to_generic(ctx.axes)) = gen_align; *aligns.get_mut(axis.to_gen(ctx.sys)) = gen_align;
had[axis as usize] = true; had[axis as usize] = true;
} }
} else { } else {
@ -54,7 +54,7 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
// We have two unflushed centers, meaning we know that both axes // We have two unflushed centers, meaning we know that both axes
// are to be centered. // are to be centered.
had = [true, true]; had = [true, true];
aligns = LayoutAlign::new(Center, Center); aligns = LayoutAlign::new(GenAlign::Center, GenAlign::Center);
} else { } else {
deferred_center = true; deferred_center = true;
} }
@ -63,13 +63,13 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
// Flush a deferred center alignment if we know have had at least one // Flush a deferred center alignment if we know have had at least one
// known alignment. // known alignment.
if deferred_center && had != [false, false] { if deferred_center && had != [false, false] {
let axis = if !had[Horizontal as usize] { let axis = if !had[SpecAxis::Horizontal as usize] {
Horizontal SpecAxis::Horizontal
} else { } else {
Vertical SpecAxis::Vertical
}; };
*aligns.get_mut(axis.to_generic(ctx.axes)) = Center; *aligns.get_mut(axis.to_gen(ctx.sys)) = GenAlign::Center;
had[axis as usize] = true; had[axis as usize] = true;
deferred_center = false; deferred_center = false;
@ -79,7 +79,7 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
// If center has not been flushed by known, it is the only argument and then // If center has not been flushed by known, it is the only argument and then
// we default to applying it to the primary axis. // we default to applying it to the primary axis.
if deferred_center { if deferred_center {
aligns.primary = Center; aligns.primary = GenAlign::Center;
} }
let commands = match content { let commands = match content {

View File

@ -1,6 +1,7 @@
use fontdock::{FontStretch, FontStyle, FontWeight}; use fontdock::{FontStretch, FontStyle, FontWeight};
use super::*; use super::*;
use crate::eval::StringLike;
use crate::length::ScaleLength; use crate::length::ScaleLength;
/// `font`: Configure the font. /// `font`: Configure the font.

View File

@ -7,7 +7,7 @@ use crate::length::ScaleLength;
/// # Positional arguments /// # Positional arguments
/// - The spacing (length or relative to font size). /// - The spacing (length or relative to font size).
pub async fn h(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn h(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
spacing(name, args, ctx, Horizontal) spacing(name, args, ctx, SpecAxis::Horizontal)
} }
/// `v`: Add vertical spacing. /// `v`: Add vertical spacing.
@ -15,7 +15,7 @@ pub async fn h(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Valu
/// # Positional arguments /// # Positional arguments
/// - The spacing (length or relative to font size). /// - The spacing (length or relative to font size).
pub async fn v(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn v(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
spacing(name, args, ctx, Vertical) spacing(name, args, ctx, SpecAxis::Vertical)
} }
fn spacing( fn spacing(
@ -28,7 +28,7 @@ fn spacing(
let spacing = args.expect::<ScaleLength>("spacing", name, &mut f); let spacing = args.expect::<ScaleLength>("spacing", name, &mut f);
let commands = if let Some(spacing) = spacing { let commands = if let Some(spacing) = spacing {
let axis = axis.to_generic(ctx.axes); let axis = axis.to_gen(ctx.sys);
let spacing = spacing.raw_scaled(ctx.style.text.font_size()); let spacing = spacing.raw_scaled(ctx.style.text.font_size());
vec![AddSpacing(spacing, SpacingKind::Hard, axis)] vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
} else { } else {

View File

@ -1,10 +1,10 @@
//! A prelude for building custom functions. //! A prelude for building custom functions.
pub use super::*; pub use crate::eval::{Dict, DictValue, Value};
pub use crate::eval::*; pub use crate::layout::primitive::*;
pub use crate::layout::prelude::*; pub use crate::layout::{layout, Command, Commands, LayoutContext};
pub use crate::layout::Command::{self, *};
pub use crate::layout::Commands;
pub use crate::style::*; pub use crate::style::*;
pub use crate::syntax::*; pub use crate::syntax::{Span, Spanned, SynTree};
pub use crate::{Feedback, Pass}; pub use crate::{Feedback, Pass};
pub use Command::*;