mirror of
https://github.com/typst/typst
synced 2025-05-21 20:45:27 +08:00
Generalize layouting primitives 🛤
This commit is contained in:
parent
e676ab53dd
commit
f8770d2b2a
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
62
src/geom.rs
62
src/geom.rs
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
///
|
||||||
|
/// The primary component defines into which direction text and lines flow and the
|
||||||
|
/// secondary into which paragraphs and pages grow.
|
||||||
|
pub type LayoutSystem = Gen2<Dir>;
|
||||||
|
|
||||||
/// Specifies the axes along content is laid out.
|
impl Default for LayoutSystem {
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
fn default() -> Self {
|
||||||
pub struct LayoutAxes {
|
Self::new(Dir::LTR, Dir::TTB)
|
||||||
pub primary: Dir,
|
|
||||||
pub secondary: Dir,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LayoutAxes {
|
|
||||||
/// Create a new instance from the two directions.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// This function panics if the directions are aligned, i.e. if they are
|
|
||||||
/// on the same axis.
|
|
||||||
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.
|
|
||||||
pub fn get(self, axis: GenAxis) -> Dir {
|
|
||||||
match axis {
|
|
||||||
Primary => self.primary,
|
|
||||||
Secondary => self.secondary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrow the direction of the specified generic axis mutably.
|
|
||||||
pub fn get_mut(&mut self, axis: GenAxis) -> &mut Dir {
|
|
||||||
match axis {
|
|
||||||
Primary => &mut self.primary,
|
|
||||||
Secondary => &mut self.secondary,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Directions along which content is laid out.
|
/// Specifies where to align a layout in a parent container.
|
||||||
|
pub type LayoutAlign = Gen2<GenAlign>;
|
||||||
|
|
||||||
|
impl LayoutAlign {
|
||||||
|
/// The layout alignment that has both components set to `Start`.
|
||||||
|
pub const START: Self = Self {
|
||||||
|
primary: GenAlign::Start,
|
||||||
|
secondary: GenAlign::Start,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// Borrow the component for the given specific axis mutably.
|
||||||
/// Defines how spacing interacts with surrounding spacing.
|
pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T {
|
||||||
///
|
match axis {
|
||||||
/// There are two options for interaction: Hard and soft spacing. Typically,
|
SpecAxis::Horizontal => &mut self.horizontal,
|
||||||
/// hard spacing is used when a fixed amount of space needs to be inserted no
|
SpecAxis::Vertical => &mut self.vertical,
|
||||||
/// 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)]
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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::*;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user