mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Generalize flex layouter 🎯
This commit is contained in:
parent
9473ae61e9
commit
0917d89bb8
@ -21,58 +21,31 @@ use super::*;
|
|||||||
pub struct FlexLayouter {
|
pub struct FlexLayouter {
|
||||||
ctx: FlexContext,
|
ctx: FlexContext,
|
||||||
units: Vec<FlexUnit>,
|
units: Vec<FlexUnit>,
|
||||||
|
|
||||||
stack: StackLayouter,
|
stack: StackLayouter,
|
||||||
usable_width: Size,
|
|
||||||
|
usable: Size,
|
||||||
run: FlexRun,
|
run: FlexRun,
|
||||||
cached_glue: Option<Size2D>,
|
space: Option<Size>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for flex layouting.
|
/// The context for flex layouting.
|
||||||
///
|
///
|
||||||
/// See [`LayoutContext`] for details about the fields.
|
/// See [`LayoutContext`] for details about the fields.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FlexContext {
|
pub struct FlexContext {
|
||||||
|
pub spaces: LayoutSpaces,
|
||||||
|
pub axes: LayoutAxes,
|
||||||
/// The spacing between two lines of boxes.
|
/// The spacing between two lines of boxes.
|
||||||
pub flex_spacing: Size,
|
pub flex_spacing: Size,
|
||||||
pub alignment: Alignment,
|
|
||||||
pub space: LayoutSpace,
|
|
||||||
pub followup_spaces: Option<LayoutSpace>,
|
|
||||||
pub shrink_to_fit: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! reuse {
|
|
||||||
($ctx:expr, $flex_spacing:expr) => (
|
|
||||||
FlexContext {
|
|
||||||
flex_spacing: $flex_spacing,
|
|
||||||
alignment: $ctx.alignment,
|
|
||||||
space: $ctx.space,
|
|
||||||
followup_spaces: $ctx.followup_spaces,
|
|
||||||
shrink_to_fit: $ctx.shrink_to_fit,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlexContext {
|
|
||||||
/// Create a flex context from a generic layout context.
|
|
||||||
pub fn from_layout_ctx(ctx: LayoutContext, flex_spacing: Size) -> FlexContext {
|
|
||||||
reuse!(ctx, flex_spacing)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a flex context from a stack context.
|
|
||||||
pub fn from_stack_ctx(ctx: StackContext, flex_spacing: Size) -> FlexContext {
|
|
||||||
reuse!(ctx, flex_spacing)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum FlexUnit {
|
enum FlexUnit {
|
||||||
/// A content unit to be arranged flexibly.
|
/// A content unit to be arranged flexibly.
|
||||||
Boxed(Layout),
|
Boxed(Layout),
|
||||||
/// A unit which acts as glue between two [`FlexUnit::Boxed`] units and
|
/// Space between two box units which is only present if there
|
||||||
/// is only present if there was no flow break in between the two
|
/// was no flow break in between the two surrounding units.
|
||||||
/// surrounding boxes.
|
Space(Size),
|
||||||
Glue(Size2D),
|
|
||||||
/// A forced break of the current flex run.
|
/// A forced break of the current flex run.
|
||||||
Break,
|
Break,
|
||||||
}
|
}
|
||||||
@ -86,18 +59,19 @@ struct FlexRun {
|
|||||||
impl FlexLayouter {
|
impl FlexLayouter {
|
||||||
/// Create a new flex layouter.
|
/// Create a new flex layouter.
|
||||||
pub fn new(ctx: FlexContext) -> FlexLayouter {
|
pub fn new(ctx: FlexContext) -> FlexLayouter {
|
||||||
|
let stack = StackLayouter::new(StackContext {
|
||||||
|
spaces: ctx.spaces,
|
||||||
|
axes: ctx.axes,
|
||||||
|
});
|
||||||
|
|
||||||
FlexLayouter {
|
FlexLayouter {
|
||||||
ctx,
|
ctx,
|
||||||
units: vec![],
|
units: vec![],
|
||||||
|
stack,
|
||||||
|
|
||||||
stack: StackLayouter::new(StackContext::from_flex_ctx(ctx, Flow::Vertical)),
|
usable: stack.usable().x,
|
||||||
|
run: FlexRun { content: vec![], size: Size2D::zero() },
|
||||||
usable_width: ctx.space.usable().x,
|
space: None,
|
||||||
run: FlexRun {
|
|
||||||
content: vec![],
|
|
||||||
size: Size2D::zero()
|
|
||||||
},
|
|
||||||
cached_glue: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,12 +85,12 @@ impl FlexLayouter {
|
|||||||
self.units.push(FlexUnit::Boxed(layout));
|
self.units.push(FlexUnit::Boxed(layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a glue box which can be replaced by a line break.
|
/// Add a space box which can be replaced by a run break.
|
||||||
pub fn add_glue(&mut self, glue: Size2D) {
|
pub fn add_space(&mut self, space: Size) {
|
||||||
self.units.push(FlexUnit::Glue(glue));
|
self.units.push(FlexUnit::Space(space));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a forced line break.
|
/// Add a forced run break.
|
||||||
pub fn add_break(&mut self) {
|
pub fn add_break(&mut self) {
|
||||||
self.units.push(FlexUnit::Break);
|
self.units.push(FlexUnit::Break);
|
||||||
}
|
}
|
||||||
@ -133,70 +107,71 @@ impl FlexLayouter {
|
|||||||
for unit in units {
|
for unit in units {
|
||||||
match unit {
|
match unit {
|
||||||
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
||||||
FlexUnit::Glue(glue) => self.layout_glue(glue),
|
FlexUnit::Space(space) => {
|
||||||
FlexUnit::Break => self.layout_break()?,
|
self.space = Some(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexUnit::Break => {
|
||||||
|
self.space = None;
|
||||||
|
self.finish_run()?;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish the last flex run.
|
// Finish the last flex run.
|
||||||
self.finish_run()?;
|
self.finish_run()?;
|
||||||
|
|
||||||
self.stack.finish()
|
Ok(self.stack.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a content box into the current flex run or start a new run if
|
/// Layout a content box into the current flex run or start a new run if
|
||||||
/// it does not fit.
|
/// it does not fit.
|
||||||
fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> {
|
fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> {
|
||||||
let glue_width = self.cached_glue.unwrap_or(Size2D::zero()).x;
|
let size = boxed.dimensions.generalized(self.ctx.axes);
|
||||||
let new_line_width = self.run.size.x + glue_width + boxed.dimensions.x;
|
|
||||||
|
|
||||||
if self.overflows_line(new_line_width) {
|
let space = self.space.unwrap_or(Size::zero());
|
||||||
self.cached_glue = None;
|
let new_run_size = self.run.size.x + space + size.x;
|
||||||
|
|
||||||
// If the box does not even fit on its own line, then we try
|
if new_run_size > self.usable {
|
||||||
// it in the next space, or we have to give up if there is none.
|
self.space = None;
|
||||||
if self.overflows_line(boxed.dimensions.x) {
|
|
||||||
if self.ctx.followup_spaces.is_some() {
|
while size.x > self.usable {
|
||||||
self.stack.finish_layout(true)?;
|
if self.stack.in_last_space() {
|
||||||
return self.layout_box(boxed);
|
Err(LayoutError::NotEnoughSpace("cannot fix box into flex run"))?;
|
||||||
} else {
|
|
||||||
return Err(LayoutError::NotEnoughSpace("cannot fit box into flex run"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.stack.finish_layout(true);
|
||||||
|
self.usable = self.stack.usable().x;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_run()?;
|
self.finish_run()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flush_glue();
|
if let Some(space) = self.space.take() {
|
||||||
|
if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable {
|
||||||
|
self.run.size.x += space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let dimensions = boxed.dimensions;
|
|
||||||
self.run.content.push((self.run.size.x, boxed));
|
self.run.content.push((self.run.size.x, boxed));
|
||||||
self.grow_run(dimensions);
|
self.run.size.x += size.x;
|
||||||
|
self.run.size.y = crate::size::max(self.run.size.y, size.y);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_glue(&mut self, glue: Size2D) {
|
|
||||||
self.cached_glue = Some(glue);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout_break(&mut self) -> LayoutResult<()> {
|
|
||||||
self.cached_glue = None;
|
|
||||||
self.finish_run()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finish the current flex run.
|
/// Finish the current flex run.
|
||||||
fn finish_run(&mut self) -> LayoutResult<()> {
|
fn finish_run(&mut self) -> LayoutResult<()> {
|
||||||
self.run.size.y += self.ctx.flex_spacing;
|
|
||||||
|
|
||||||
let mut actions = LayoutActionList::new();
|
let mut actions = LayoutActionList::new();
|
||||||
for (x, layout) in self.run.content.drain(..) {
|
for (x, layout) in self.run.content.drain(..) {
|
||||||
let position = Size2D::with_x(x);
|
let position = Size2D::with_x(x).specialized(self.ctx.axes);
|
||||||
actions.add_layout(position, layout);
|
actions.add_layout(position, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.run.size.y += self.ctx.flex_spacing;
|
||||||
|
|
||||||
self.stack.add(Layout {
|
self.stack.add(Layout {
|
||||||
dimensions: self.run.size,
|
dimensions: self.run.size.specialized(self.ctx.axes),
|
||||||
actions: actions.into_vec(),
|
actions: actions.into_vec(),
|
||||||
debug_render: false,
|
debug_render: false,
|
||||||
})?;
|
})?;
|
||||||
@ -206,25 +181,8 @@ impl FlexLayouter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_glue(&mut self) {
|
|
||||||
if let Some(glue) = self.cached_glue.take() {
|
|
||||||
if self.run.size.x > Size::zero() && !self.overflows_line(self.run.size.x + glue.x) {
|
|
||||||
self.grow_run(glue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grow_run(&mut self, dimensions: Size2D) {
|
|
||||||
self.run.size.x += dimensions.x;
|
|
||||||
self.run.size.y = crate::size::max(self.run.size.y, dimensions.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this layouter contains any items.
|
/// Whether this layouter contains any items.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.units.is_empty()
|
self.units.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overflows_line(&self, line: Size) -> bool {
|
|
||||||
line > self.usable_width
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,6 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This layouter's context.
|
|
||||||
pub fn ctx(&self) -> StackContext {
|
|
||||||
self.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a sublayout.
|
/// Add a sublayout.
|
||||||
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
||||||
let size = layout.dimensions.generalized(self.ctx.axes);
|
let size = layout.dimensions.generalized(self.ctx.axes);
|
||||||
@ -53,12 +48,11 @@ impl StackLayouter {
|
|||||||
|
|
||||||
// Search for a suitable space to insert the box.
|
// Search for a suitable space to insert the box.
|
||||||
while !self.usable.fits(new_dimensions) {
|
while !self.usable.fits(new_dimensions) {
|
||||||
if self.active_space == self.ctx.spaces.len() - 1 {
|
if self.in_last_space() {
|
||||||
return Err(LayoutError::NotEnoughSpace("box is to large for stack spaces"));
|
Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_layout()?;
|
self.finish_layout(true);
|
||||||
self.start_new_space(true);
|
|
||||||
new_dimensions = self.size_with(size);
|
new_dimensions = self.size_with(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,33 +74,36 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add space after the last layout.
|
/// Add space after the last layout.
|
||||||
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
|
pub fn add_space(&mut self, space: Size) {
|
||||||
if self.dimensions.y + space > self.usable.y {
|
if self.dimensions.y + space > self.usable.y {
|
||||||
self.finish_layout()?;
|
self.finish_layout(false);
|
||||||
self.start_new_space(false);
|
|
||||||
} else {
|
} else {
|
||||||
self.dimensions.y += space;
|
self.dimensions.y += space;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the layouting.
|
/// Finish the layouting.
|
||||||
///
|
///
|
||||||
/// The layouter is not consumed by this to prevent ownership problems.
|
/// The layouter is not consumed by this to prevent ownership problems.
|
||||||
/// Nevertheless, it should not be used further.
|
/// Nevertheless, it should not be used further.
|
||||||
pub fn finish(&mut self) -> LayoutResult<MultiLayout> {
|
pub fn finish(&mut self) -> MultiLayout {
|
||||||
if self.include_empty || !self.boxes.is_empty() {
|
if self.include_empty || !self.boxes.is_empty() {
|
||||||
self.finish_layout()?;
|
self.finish_boxes();
|
||||||
}
|
}
|
||||||
Ok(std::mem::replace(&mut self.layouts, MultiLayout::new()))
|
std::mem::replace(&mut self.layouts, MultiLayout::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the current layout and start a new one in a new space.
|
/// Finish the current layout and start a new one in a new space.
|
||||||
///
|
///
|
||||||
/// If `start_new_empty` is true, a new empty layout will be started. Otherwise,
|
/// If `include_empty` is true, the followup layout will even be
|
||||||
/// the new layout only appears once new content is added.
|
/// part of the finished multi-layout if it would be empty.
|
||||||
pub fn finish_layout(&mut self) -> LayoutResult<()> {
|
pub fn finish_layout(&mut self, include_empty: bool) {
|
||||||
|
self.finish_boxes();
|
||||||
|
self.start_new_space(include_empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compose all cached boxes into a layout.
|
||||||
|
fn finish_boxes(&mut self) {
|
||||||
let mut actions = LayoutActionList::new();
|
let mut actions = LayoutActionList::new();
|
||||||
|
|
||||||
let space = self.ctx.spaces[self.active_space];
|
let space = self.ctx.spaces[self.active_space];
|
||||||
@ -116,7 +113,7 @@ impl StackLayouter {
|
|||||||
|
|
||||||
for (offset, layout_anchor, layout) in self.boxes.drain(..) {
|
for (offset, layout_anchor, layout) in self.boxes.drain(..) {
|
||||||
let general_position = anchor - layout_anchor + Size2D::with_y(offset * factor);
|
let general_position = anchor - layout_anchor + Size2D::with_y(offset * factor);
|
||||||
let position = general_position.specialized(self.ctx.axes) + start;
|
let position = start + general_position.specialized(self.ctx.axes);
|
||||||
|
|
||||||
actions.add_layout(position, layout);
|
actions.add_layout(position, layout);
|
||||||
}
|
}
|
||||||
@ -130,8 +127,6 @@ impl StackLayouter {
|
|||||||
actions: actions.into_vec(),
|
actions: actions.into_vec(),
|
||||||
debug_render: true,
|
debug_render: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up layouting in the next space. Should be preceded by `finish_layout`.
|
/// Set up layouting in the next space. Should be preceded by `finish_layout`.
|
||||||
@ -139,19 +134,34 @@ impl StackLayouter {
|
|||||||
/// If `include_empty` is true, the new empty layout will always be added when
|
/// If `include_empty` is true, the new empty layout will always be added when
|
||||||
/// finishing this stack. Otherwise, the new layout only appears if new
|
/// finishing this stack. Otherwise, the new layout only appears if new
|
||||||
/// content is added to it.
|
/// content is added to it.
|
||||||
pub fn start_new_space(&mut self, include_empty: bool) {
|
fn start_new_space(&mut self, include_empty: bool) {
|
||||||
self.active_space = (self.active_space + 1).min(self.ctx.spaces.len() - 1);
|
self.active_space = (self.active_space + 1).min(self.ctx.spaces.len() - 1);
|
||||||
self.usable = self.ctx.spaces[self.active_space].usable().generalized(self.ctx.axes);
|
self.usable = self.ctx.spaces[self.active_space].usable().generalized(self.ctx.axes);
|
||||||
self.dimensions = start_dimensions(self.usable, self.ctx.axes);
|
self.dimensions = start_dimensions(self.usable, self.ctx.axes);
|
||||||
self.include_empty = include_empty;
|
self.include_empty = include_empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The remaining space for new layouts.
|
/// This layouter's context.
|
||||||
|
pub fn ctx(&self) -> StackContext {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The (generalized) usable area of the current space.
|
||||||
|
pub fn usable(&self) -> Size2D {
|
||||||
|
self.usable
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The (specialized) remaining area for new layouts in the current space.
|
||||||
pub fn remaining(&self) -> Size2D {
|
pub fn remaining(&self) -> Size2D {
|
||||||
Size2D::new(self.usable.x, self.usable.y - self.dimensions.y)
|
Size2D::new(self.usable.x, self.usable.y - self.dimensions.y)
|
||||||
.specialized(self.ctx.axes)
|
.specialized(self.ctx.axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this layouter is in its last space.
|
||||||
|
pub fn in_last_space(&self) -> bool {
|
||||||
|
self.active_space == self.ctx.spaces.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
/// The combined size of the so-far included boxes with the other size.
|
/// The combined size of the so-far included boxes with the other size.
|
||||||
fn size_with(&self, other: Size2D) -> Size2D {
|
fn size_with(&self, other: Size2D) -> Size2D {
|
||||||
Size2D {
|
Size2D {
|
||||||
|
@ -59,7 +59,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
|
|
||||||
if self.set_newline {
|
if self.set_newline {
|
||||||
let space = paragraph_spacing(&self.style);
|
let space = paragraph_spacing(&self.style);
|
||||||
self.stack.add_space(space)?;
|
self.stack.add_space(space);
|
||||||
self.set_newline = false;
|
self.set_newline = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
/// Finish the layout.
|
/// Finish the layout.
|
||||||
fn finish(mut self) -> LayoutResult<MultiLayout> {
|
fn finish(mut self) -> LayoutResult<MultiLayout> {
|
||||||
self.finish_flex()?;
|
self.finish_flex()?;
|
||||||
self.stack.finish()
|
Ok(self.stack.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a function.
|
/// Layout a function.
|
||||||
@ -136,8 +136,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
|
|
||||||
Command::FinishLayout => {
|
Command::FinishLayout => {
|
||||||
self.finish_flex()?;
|
self.finish_flex()?;
|
||||||
self.stack.finish_layout()?;
|
self.stack.finish_layout(true);
|
||||||
self.stack.start_new_space(true);
|
|
||||||
self.start_new_flex();
|
self.start_new_flex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
src/lib.rs
22
src/lib.rs
@ -17,6 +17,7 @@
|
|||||||
pub extern crate toddle;
|
pub extern crate toddle;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use smallvec::smallvec;
|
||||||
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
||||||
|
|
||||||
use crate::func::Scope;
|
use crate::func::Scope;
|
||||||
@ -93,27 +94,22 @@ impl<'p> Typesetter<'p> {
|
|||||||
|
|
||||||
/// Layout a syntax tree and return the produced layout.
|
/// Layout a syntax tree and return the produced layout.
|
||||||
pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult<MultiLayout> {
|
pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult<MultiLayout> {
|
||||||
let space = LayoutSpace {
|
Ok(layout_tree(
|
||||||
dimensions: self.page_style.dimensions,
|
|
||||||
padding: self.page_style.margins,
|
|
||||||
};
|
|
||||||
|
|
||||||
let pages = layout_tree(
|
|
||||||
&tree,
|
&tree,
|
||||||
LayoutContext {
|
LayoutContext {
|
||||||
loader: &self.loader,
|
loader: &self.loader,
|
||||||
style: &self.text_style,
|
style: &self.text_style,
|
||||||
space,
|
spaces: smallvec![LayoutSpace {
|
||||||
followup_spaces: Some(space),
|
dimensions: self.page_style.dimensions,
|
||||||
|
padding: self.page_style.margins,
|
||||||
shrink_to_fit: false,
|
shrink_to_fit: false,
|
||||||
|
}],
|
||||||
axes: LayoutAxes {
|
axes: LayoutAxes {
|
||||||
primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Left).unwrap(),
|
primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Origin),
|
||||||
secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Top).unwrap(),
|
secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Origin),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)?;
|
)?)
|
||||||
|
|
||||||
Ok(pages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process source code directly into a layout.
|
/// Process source code directly into a layout.
|
||||||
|
@ -40,16 +40,6 @@ macro_rules! error_type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether an expression matches a pattern.
|
|
||||||
macro_rules! matches {
|
|
||||||
($val:expr, $($pattern:tt)*) => (
|
|
||||||
match $val {
|
|
||||||
$($pattern)* => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `Debug` implementation from a `Display` implementation.
|
/// Create a `Debug` implementation from a `Display` implementation.
|
||||||
macro_rules! debug_display {
|
macro_rules! debug_display {
|
||||||
($type:ident) => (
|
($type:ident) => (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user