summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/layout/flex.rs39
-rw-r--r--src/layout/mod.rs27
-rw-r--r--src/layout/stacked.rs55
-rw-r--r--src/layout/text.rs14
-rw-r--r--src/layout/tree.rs29
-rw-r--r--src/lib.rs6
-rw-r--r--src/library/align.rs5
7 files changed, 118 insertions, 57 deletions
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
index 80cc2074..877e0bf3 100644
--- a/src/layout/flex.rs
+++ b/src/layout/flex.rs
@@ -28,12 +28,40 @@ pub struct FlexLayouter {
}
/// The context for flex layouting.
+///
+/// See [`LayoutContext`] for details about the fields.
#[derive(Debug, Copy, Clone)]
pub struct FlexContext {
- pub space: LayoutSpace,
/// The spacing between two lines of boxes.
pub flex_spacing: Size,
- pub extra_space: Option<LayoutSpace>,
+ 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)
+ }
}
enum FlexUnit {
@@ -57,10 +85,7 @@ impl FlexLayouter {
ctx,
units: vec![],
- stack: StackLayouter::new(StackContext {
- space: ctx.space,
- extra_space: ctx.extra_space,
- }),
+ stack: StackLayouter::new(StackContext::from_flex_ctx(ctx)),
usable_width: ctx.space.usable().x,
run: FlexRun {
@@ -125,7 +150,7 @@ impl FlexLayouter {
// If the box does not even fit on its own line, then we try
// it in the next space, or we have to give up if there is none.
if self.overflows_line(boxed.dimensions.x) {
- if self.ctx.extra_space.is_some() {
+ if self.ctx.followup_spaces.is_some() {
self.stack.finish_layout(true)?;
return self.layout_box(boxed);
} else {
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index a5dfa0ad..fccbe8c8 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -126,10 +126,22 @@ impl<'a> IntoIterator for &'a MultiLayout {
/// The general context for layouting.
#[derive(Debug, Copy, Clone)]
pub struct LayoutContext<'a, 'p> {
+ /// The font loader to retrieve fonts from when typesetting text
+ /// using [`layout_text`].
pub loader: &'a SharedFontLoader<'p>,
+ /// The style to set text with. This includes sizes and font classes
+ /// which determine which font from the loaders selection is used.
pub style: &'a TextStyle,
+ /// The alignment to use for the content.
+ pub alignment: Alignment,
+ /// The primary space to layout in.
pub space: LayoutSpace,
- pub extra_space: Option<LayoutSpace>,
+ /// The additional spaces which are used when the primary space
+ /// cannot fit the whole content.
+ pub followup_spaces: Option<LayoutSpace>,
+ /// Whether to shrink the dimensions to fit the content or the keep the
+ /// dimensions from the layout spaces.
+ pub shrink_to_fit: bool,
}
/// Spacial layouting constraints.
@@ -139,11 +151,6 @@ pub struct LayoutSpace {
pub dimensions: Size2D,
/// Padding that should be respected on each side.
pub padding: SizeBox,
- /// The alignment to use for the content.
- pub alignment: Alignment,
- /// Whether to shrink the dimensions to fit the content or the keep the
- /// dimensions from the layout space.
- pub shrink_to_fit: bool,
}
impl LayoutSpace {
@@ -151,6 +158,14 @@ impl LayoutSpace {
pub fn usable(&self) -> Size2D {
self.dimensions.unpadded(self.padding)
}
+
+ /// A layout without padding and dimensions reduced by the padding.
+ pub fn usable_space(&self) -> LayoutSpace {
+ LayoutSpace {
+ dimensions: self.usable(),
+ padding: SizeBox::zero(),
+ }
+ }
}
/// Where to align content.
diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs
index 3a29f722..0b7bfd4f 100644
--- a/src/layout/stacked.rs
+++ b/src/layout/stacked.rs
@@ -17,10 +17,37 @@ pub struct StackLayouter {
}
/// The context for stack layouting.
+///
+/// See [`LayoutContext`] for details about the fields.
#[derive(Debug, Copy, Clone)]
pub struct StackContext {
+ pub alignment: Alignment,
pub space: LayoutSpace,
- pub extra_space: Option<LayoutSpace>,
+ pub followup_spaces: Option<LayoutSpace>,
+ pub shrink_to_fit: bool,
+}
+
+macro_rules! reuse {
+ ($ctx:expr) => {
+ StackContext {
+ alignment: $ctx.alignment,
+ space: $ctx.space,
+ followup_spaces: $ctx.followup_spaces,
+ shrink_to_fit: $ctx.shrink_to_fit
+ }
+ };
+}
+
+impl StackContext {
+ /// Create a stack context from a generic layout context.
+ pub fn from_layout_ctx(ctx: LayoutContext) -> StackContext {
+ reuse!(ctx)
+ }
+
+ /// Create a stack context from a flex context.
+ pub fn from_flex_ctx(ctx: FlexContext) -> StackContext {
+ reuse!(ctx)
+ }
}
impl StackLayouter {
@@ -33,8 +60,8 @@ impl StackLayouter {
space: ctx.space,
usable: ctx.space.usable(),
- dimensions: start_dimensions(ctx.space),
- cursor: start_cursor(ctx.space),
+ dimensions: start_dimensions(ctx.alignment, ctx.space),
+ cursor: start_cursor(ctx.alignment, ctx.space),
in_extra_space: false,
started: true,
}
@@ -57,7 +84,7 @@ impl StackLayouter {
};
if self.overflows(new_dimensions) {
- if self.ctx.extra_space.is_some() &&
+ if self.ctx.followup_spaces.is_some() &&
!(self.in_extra_space && self.overflows(layout.dimensions))
{
self.finish_layout(true)?;
@@ -70,7 +97,7 @@ impl StackLayouter {
// Determine where to put the box. When we right-align it, we want the
// cursor to point to the top-right corner of the box. Therefore, the
// position has to be moved to the left by the width of the box.
- let position = match self.space.alignment {
+ let position = match self.ctx.alignment {
Alignment::Left => self.cursor,
Alignment::Right => self.cursor - Size2D::with_x(layout.dimensions.x),
Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2),
@@ -101,7 +128,7 @@ impl StackLayouter {
let new_dimensions = self.dimensions + Size2D::with_y(space);
if self.overflows(new_dimensions) {
- if self.ctx.extra_space.is_some() {
+ if self.ctx.followup_spaces.is_some() {
self.finish_layout(false)?;
} else {
return Err(LayoutError::NotEnoughSpace("cannot fit space into stack"));
@@ -133,7 +160,7 @@ impl StackLayouter {
pub fn finish_layout(&mut self, start_new_empty: bool) -> LayoutResult<()> {
let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
self.layouts.add(Layout {
- dimensions: if self.space.shrink_to_fit {
+ dimensions: if self.ctx.shrink_to_fit {
self.dimensions.padded(self.space.padding)
} else {
self.space.dimensions
@@ -152,12 +179,12 @@ impl StackLayouter {
}
pub fn start_new_space(&mut self) -> LayoutResult<()> {
- if let Some(space) = self.ctx.extra_space {
+ if let Some(space) = self.ctx.followup_spaces {
self.started = true;
self.space = space;
self.usable = space.usable();
- self.dimensions = start_dimensions(space);
- self.cursor = start_cursor(space);
+ self.dimensions = start_dimensions(self.ctx.alignment, space);
+ self.cursor = start_cursor(self.ctx.alignment, space);
self.in_extra_space = true;
Ok(())
} else {
@@ -183,19 +210,19 @@ impl StackLayouter {
}
}
-fn start_dimensions(space: LayoutSpace) -> Size2D {
- match space.alignment {
+fn start_dimensions(alignment: Alignment, space: LayoutSpace) -> Size2D {
+ match alignment {
Alignment::Left => Size2D::zero(),
Alignment::Right | Alignment::Center => Size2D::with_x(space.usable().x),
}
}
-fn start_cursor(space: LayoutSpace) -> Size2D {
+fn start_cursor(alignment: Alignment, space: LayoutSpace) -> Size2D {
Size2D {
// If left-align, the cursor points to the top-left corner of
// each box. If we right-align, it points to the top-right
// corner.
- x: match space.alignment {
+ x: match alignment {
Alignment::Left => space.padding.left,
Alignment::Right => space.dimensions.x - space.padding.right,
Alignment::Center => space.padding.left + (space.usable().x / 2),
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 3a064df4..79ace040 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -5,14 +5,24 @@ use super::*;
use crate::size::{Size, Size2D};
/// The context for text layouting.
+///
+/// See [`LayoutContext`] for details about the fields.
#[derive(Copy, Clone)]
pub struct TextContext<'a, 'p> {
- /// Loads fonts matching queries.
pub loader: &'a SharedFontLoader<'p>,
- /// Base style to set text with.
pub style: &'a TextStyle,
}
+impl<'a, 'p> TextContext<'a, 'p> {
+ /// Create a text context from a generic layout context.
+ pub fn from_layout_ctx(ctx: LayoutContext<'a, 'p>) -> TextContext<'a, 'p> {
+ TextContext {
+ loader: ctx.loader,
+ style: ctx.style,
+ }
+ }
+}
+
/// Layouts text into a box.
///
/// There is no complex layout involved. The text is simply laid out left-
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 7feed610..a1918ded 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -19,14 +19,12 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
TreeLayouter {
ctx,
- stack: StackLayouter::new(StackContext {
- space: ctx.space,
- extra_space: ctx.extra_space
- }),
+ stack: StackLayouter::new(StackContext::from_layout_ctx(ctx)),
flex: FlexLayouter::new(FlexContext {
- space: flex_space(ctx.space),
- extra_space: ctx.extra_space.map(|s| flex_space(s)),
- flex_spacing: flex_spacing(&ctx.style),
+ space: ctx.space.usable_space(),
+ followup_spaces: ctx.followup_spaces.map(|s| s.usable_space()),
+ shrink_to_fit: true,
+ .. FlexContext::from_layout_ctx(ctx, flex_spacing(&ctx.style))
}),
style: Cow::Borrowed(ctx.style),
}
@@ -119,15 +117,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
let mut ctx = self.ctx;
ctx.style = &self.style;
+ ctx.shrink_to_fit = true;
ctx.space.dimensions = self.stack.remaining();
ctx.space.padding = SizeBox::zero();
- ctx.space.shrink_to_fit = true;
- if let Some(space) = ctx.extra_space.as_mut() {
- space.dimensions = space.usable();
- space.padding = SizeBox::zero();
- space.shrink_to_fit = true;
+ if let Some(space) = ctx.followup_spaces.as_mut() {
+ *space = space.usable_space();
}
let commands = func.body.layout(ctx)?;
@@ -145,15 +141,6 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
}
-fn flex_space(space: LayoutSpace) -> LayoutSpace {
- LayoutSpace {
- dimensions: space.usable(),
- padding: SizeBox::zero(),
- alignment: space.alignment,
- shrink_to_fit: true,
- }
-}
-
fn flex_spacing(style: &TextStyle) -> Size {
(style.line_spacing - 1.0) * Size::pt(style.font_size)
}
diff --git a/src/lib.rs b/src/lib.rs
index fe9d32a6..424d8dbf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -96,8 +96,6 @@ impl<'p> Typesetter<'p> {
let space = LayoutSpace {
dimensions: self.page_style.dimensions,
padding: self.page_style.margins,
- alignment: Alignment::Left,
- shrink_to_fit: false,
};
let pages = layout_tree(
@@ -105,8 +103,10 @@ impl<'p> Typesetter<'p> {
LayoutContext {
loader: &self.loader,
style: &self.text_style,
+ alignment: Alignment::Left,
space,
- extra_space: Some(space),
+ followup_spaces: Some(space),
+ shrink_to_fit: false,
},
)?;
diff --git a/src/library/align.rs b/src/library/align.rs
index f81bae31..be564c1b 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -40,10 +40,7 @@ impl Function for AlignFunc {
fn layout(&self, mut ctx: LayoutContext) -> LayoutResult<FuncCommands> {
if let Some(body) = &self.body {
- ctx.space.alignment = self.alignment;
- if let Some(space) = ctx.extra_space.as_mut() {
- space.alignment = self.alignment;
- }
+ ctx.alignment = self.alignment;
let layouts = layout_tree(body, ctx)?;