summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/flex.rs198
-rw-r--r--src/layout/line.rs283
-rw-r--r--src/layout/mod.rs9
-rw-r--r--src/layout/stack.rs100
-rw-r--r--src/layout/text.rs44
-rw-r--r--src/layout/tree.rs57
6 files changed, 378 insertions, 313 deletions
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
deleted file mode 100644
index 72878a29..00000000
--- a/src/layout/flex.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-use super::*;
-
-/// The flex layouter first arranges boxes along a primary and if necessary also
-/// along a secondary axis.
-#[derive(Debug, Clone)]
-pub struct FlexLayouter {
- axes: LayoutAxes,
- flex_spacing: Size,
- stack: StackLayouter,
-
- units: Vec<FlexUnit>,
- line: FlexLine,
- part: PartialLine,
-}
-
-#[derive(Debug, Clone)]
-enum FlexUnit {
- Boxed(Layout),
- Space(Size, SpacingKind),
- SetAxes(LayoutAxes),
- Break,
-}
-
-#[derive(Debug, Clone)]
-struct FlexLine {
- usable: Size,
- actions: LayoutActions,
- combined_dimensions: Size2D,
-}
-
-impl FlexLine {
- fn new(usable: Size) -> FlexLine {
- FlexLine {
- usable,
- actions: LayoutActions::new(),
- combined_dimensions: Size2D::ZERO,
- }
- }
-}
-
-#[derive(Debug, Clone)]
-struct PartialLine {
- usable: Size,
- content: Vec<(Size, Layout)>,
- dimensions: Size2D,
- space: LastSpacing,
-}
-
-impl PartialLine {
- fn new(usable: Size) -> PartialLine {
- PartialLine {
- usable,
- content: vec![],
- dimensions: Size2D::ZERO,
- space: LastSpacing::Hard,
- }
- }
-}
-
-/// The context for flex layouting.
-///
-/// See [`LayoutContext`] for details about the fields.
-#[derive(Debug, Clone)]
-pub struct FlexContext {
- pub spaces: LayoutSpaces,
- pub axes: LayoutAxes,
- pub alignment: LayoutAlignment,
- pub flex_spacing: Size,
- pub repeat: bool,
- pub debug: bool,
-}
-
-impl FlexLayouter {
- /// Create a new flex layouter.
- pub fn new(ctx: FlexContext) -> FlexLayouter {
- let stack = StackLayouter::new(StackContext {
- spaces: ctx.spaces,
- axes: ctx.axes,
- alignment: ctx.alignment,
- repeat: ctx.repeat,
- debug: ctx.debug,
- });
-
- let usable = stack.primary_usable();
-
- FlexLayouter {
- axes: ctx.axes,
- flex_spacing: ctx.flex_spacing,
- stack,
-
- units: vec![],
- line: FlexLine::new(usable),
- part: PartialLine::new(usable),
- }
- }
-
- pub fn add(&mut self, layout: Layout) {
- self.units.push(FlexUnit::Boxed(layout));
- }
-
- pub fn add_multiple(&mut self, layouts: MultiLayout) {
- for layout in layouts {
- self.add(layout);
- }
- }
-
- pub fn add_break(&mut self) {
- self.units.push(FlexUnit::Break);
- }
-
- pub fn add_primary_space(&mut self, space: Size, kind: SpacingKind) {
- self.units.push(FlexUnit::Space(space, kind))
- }
-
- pub fn add_secondary_space(&mut self, space: Size, kind: SpacingKind) -> LayoutResult<()> {
- if !self.run_is_empty() {
- self.finish_run()?;
- }
- Ok(self.stack.add_spacing(space, kind))
- }
-
- pub fn set_axes(&mut self, axes: LayoutAxes) {
- self.units.push(FlexUnit::SetAxes(axes));
- }
-
- pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
- if replace_empty && self.run_is_empty() && self.stack.space_is_empty() {
- self.stack.set_spaces(spaces, true);
- self.start_line();
- } else {
- self.stack.set_spaces(spaces, false);
- }
- }
-
- pub fn remaining(&self) -> LayoutSpaces {
- self.stack.remaining()
- }
-
- pub fn run_is_empty(&self) -> bool {
- !self.units.iter().any(|unit| matches!(unit, FlexUnit::Boxed(_)))
- }
-
- pub fn run_last_is_space(&self) -> bool {
- matches!(self.units.last(), Some(FlexUnit::Space(_, _)))
- }
-
- pub fn finish(mut self) -> LayoutResult<MultiLayout> {
- self.finish_space(false)?;
- self.stack.finish()
- }
-
- pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> {
- if !self.run_is_empty() {
- self.finish_run()?;
- }
-
- self.stack.finish_space(hard)?;
- Ok(self.start_line())
- }
-
- pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
- let units = std::mem::replace(&mut self.units, vec![]);
- for unit in units {
- match unit {
- FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
- FlexUnit::Space(space, kind) => self.layout_space(space, kind),
- FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
- FlexUnit::Break => { self.finish_line()?; },
- }
- }
-
- self.finish_line()
- }
-
- fn finish_line(&mut self) -> LayoutResult<Size2D> {
- unimplemented!()
- }
-
- fn start_line(&mut self) {
- unimplemented!()
- }
-
- fn finish_partial_line(&mut self) {
- unimplemented!()
- }
-
- fn layout_box(&mut self, _boxed: Layout) -> LayoutResult<()> {
- unimplemented!()
- }
-
- fn layout_space(&mut self, _space: Size, _kind: SpacingKind) {
- unimplemented!()
- }
-
- fn layout_set_axes(&mut self, _axes: LayoutAxes) {
- unimplemented!()
- }
-}
diff --git a/src/layout/line.rs b/src/layout/line.rs
new file mode 100644
index 00000000..5aae7dcd
--- /dev/null
+++ b/src/layout/line.rs
@@ -0,0 +1,283 @@
+use super::*;
+
+/// The line layouter arranges boxes next to each other along a primary axis
+/// and arranges the resulting lines using an underlying stack layouter.
+#[derive(Debug, Clone)]
+pub struct LineLayouter {
+ /// The context for layouting.
+ ctx: LineContext,
+ /// The underlying stack layouter.
+ stack: StackLayouter,
+ /// The currently written line.
+ run: LineRun,
+}
+
+/// The context for line layouting.
+#[derive(Debug, Clone)]
+pub struct LineContext {
+ /// The spaces to layout in.
+ pub spaces: LayoutSpaces,
+ /// The initial layouting axes, which can be updated by the
+ /// [`LineLayouter::set_axes`] method.
+ pub axes: LayoutAxes,
+ /// Which alignment to set on the resulting layout. This affects how it will
+ /// be positioned in a parent box.
+ pub alignment: LayoutAlignment,
+ /// Whether to have repeated spaces or to use only the first and only once.
+ pub repeat: bool,
+ /// Whether to output a command which renders a debugging box showing the
+ /// extent of the layout.
+ pub debug: bool,
+ /// The line spacing.
+ pub line_spacing: Size,
+}
+
+/// A simple line of boxes.
+#[derive(Debug, Clone)]
+struct LineRun {
+ /// The so-far accumulated layouts in the line.
+ layouts: Vec<(Size, Layout)>,
+ /// The width (primary size) and maximal height (secondary size) of the
+ /// line.
+ size: Size2D,
+ /// The alignment of all layouts in the line.
+ alignment: Option<LayoutAlignment>,
+ /// The remaining usable space if another differently aligned line run
+ /// already took up some space.
+ usable: Option<Size>,
+ /// A possibly cached soft spacing or spacing state.
+ last_spacing: LastSpacing,
+}
+
+impl LineLayouter {
+ /// Create a new line layouter.
+ pub fn new(ctx: LineContext) -> LineLayouter {
+ LineLayouter {
+ stack: StackLayouter::new(StackContext {
+ spaces: ctx.spaces.clone(),
+ axes: ctx.axes,
+ alignment: ctx.alignment,
+ repeat: ctx.repeat,
+ debug: ctx.debug,
+ }),
+ ctx,
+ run: LineRun::new(),
+ }
+ }
+
+ /// Add a layout to the run.
+ pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
+ let axes = self.ctx.axes;
+
+ if let Some(alignment) = self.run.alignment {
+ if layout.alignment.secondary != alignment.secondary {
+ if self.stack.is_fitting_alignment(layout.alignment) {
+ self.finish_line()?;
+ } else {
+ self.finish_space(true)?;
+ }
+ } else if layout.alignment.primary < alignment.primary {
+ self.finish_line()?;
+
+ } else if layout.alignment.primary > alignment.primary {
+ let mut rest_run = LineRun::new();
+
+ let usable = self.stack.usable().get_primary(axes);
+ rest_run.usable = Some(match layout.alignment.primary {
+ Alignment::Origin => unreachable!("origin > x"),
+ Alignment::Center => usable - 2 * self.run.size.x,
+ Alignment::End => usable - self.run.size.x,
+ });
+
+ rest_run.size.y = self.run.size.y;
+
+ self.finish_line()?;
+ self.stack.add_spacing(-rest_run.size.y, SpacingKind::Hard);
+
+ self.run = rest_run;
+ }
+ }
+
+ if let LastSpacing::Soft(spacing, _) = self.run.last_spacing {
+ self.add_primary_spacing(spacing, SpacingKind::Hard);
+ }
+
+ let size = layout.dimensions.generalized(axes);
+
+ while !self.usable().fits(size) {
+ if !self.line_is_empty() {
+ self.finish_line()?;
+ } else {
+ self.finish_space(true)?;
+ }
+ }
+
+ self.run.alignment = Some(layout.alignment);
+ self.run.layouts.push((self.run.size.x, layout));
+
+ self.run.size.x += size.x;
+ self.run.size.y.max_eq(size.y);
+ self.run.last_spacing = LastSpacing::None;
+
+ Ok(())
+ }
+
+ /// Add multiple layouts to the run.
+ ///
+ /// This function simply calls `add` repeatedly for each layout.
+ pub fn add_multiple(&mut self, layouts: MultiLayout) -> LayoutResult<()> {
+ for layout in layouts {
+ self.add(layout)?;
+ }
+ Ok(())
+ }
+
+ /// The remaining usable size in the run.
+ fn usable(&self) -> Size2D {
+ // The base is the usable space per stack layouter.
+ let mut usable = self.stack.usable().generalized(self.ctx.axes);
+
+ // If this is a alignment-continuing line, we override the primary
+ // usable size.
+ if let Some(primary) = self.run.usable {
+ usable.x = primary;
+ }
+
+ usable.x -= self.run.size.x;
+ usable
+ }
+
+ /// Add primary spacing to the line.
+ pub fn add_primary_spacing(&mut self, mut spacing: Size, kind: SpacingKind) {
+ match kind {
+ // A hard space is simply an empty box.
+ SpacingKind::Hard => {
+ spacing.min_eq(self.usable().x);
+ self.run.size.x += spacing;
+ self.run.last_spacing = LastSpacing::Hard;
+ }
+
+ // A soft space is cached if it is not consumed by a hard space or
+ // previous soft space with higher level.
+ SpacingKind::Soft(level) => {
+ let consumes = match self.run.last_spacing {
+ LastSpacing::None => true,
+ LastSpacing::Soft(_, prev) if level < prev => true,
+ _ => false,
+ };
+
+ if consumes {
+ self.run.last_spacing = LastSpacing::Soft(spacing, level);
+ }
+ }
+ }
+ }
+
+ /// Finish the run and add secondary spacing to the underlying stack.
+ pub fn add_secondary_spacing(
+ &mut self,
+ mut spacing: Size,
+ kind: SpacingKind
+ ) -> LayoutResult<()> {
+ self.finish_line_if_not_empty()?;
+ Ok(self.stack.add_spacing(spacing, kind))
+ }
+
+ /// Change the layouting axes used by this layouter.
+ pub fn set_axes(&mut self, axes: LayoutAxes) -> LayoutResult<()> {
+ self.finish_line_if_not_empty()?;
+ self.ctx.axes = axes;
+ Ok(self.stack.set_axes(axes))
+ }
+
+ /// Change the layouting spaces to use.
+ ///
+ /// If `replace_empty` is true, the current space is replaced if there are
+ /// no boxes laid into it yet. Otherwise, only the followup spaces are
+ /// replaced.
+ pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
+ self.stack.set_spaces(spaces, replace_empty && self.line_is_empty());
+ }
+
+ /// Change the line spacing.
+ pub fn set_line_spacing(&mut self, line_spacing: Size) {
+ self.ctx.line_spacing = line_spacing;
+ }
+
+ /// The remaining unpadded, unexpanding spaces.
+ pub fn remaining(&self) -> LayoutSpaces {
+ let mut spaces = self.stack.remaining();
+ *spaces[0].dimensions.get_secondary_mut(self.ctx.axes)
+ -= self.run.size.y;
+ spaces
+ }
+
+ /// Whether the currently set line is empty.
+ pub fn line_is_empty(&self) -> bool {
+ self.run.size == Size2D::ZERO && self.run.layouts.is_empty()
+ }
+
+ /// Finish the last line and compute the final multi-layout.
+ pub fn finish(mut self) -> LayoutResult<MultiLayout> {
+ self.finish_line_if_not_empty()?;
+ self.stack.finish()
+ }
+
+ /// Finish the currently active space and start a new one.
+ pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> {
+ self.finish_line_if_not_empty()?;
+ self.stack.finish_space(hard)
+ }
+
+ /// Add the current line to the stack and start a new line.
+ pub fn finish_line(&mut self) -> LayoutResult<()> {
+ let mut actions = LayoutActions::new();
+
+ let layouts = std::mem::replace(&mut self.run.layouts, vec![]);
+ for (offset, layout) in layouts {
+ let x = match self.ctx.axes.primary.is_positive() {
+ true => offset,
+ false => self.run.size.x
+ - offset
+ - layout.dimensions.get_primary(self.ctx.axes),
+ };
+
+ let pos = Size2D::with_x(x);
+ actions.add_layout(pos, layout);
+ }
+
+ self.stack.add(Layout {
+ dimensions: self.run.size.specialized(self.ctx.axes),
+ alignment: self.run.alignment
+ .unwrap_or(LayoutAlignment::new(Origin, Origin)),
+ actions: actions.to_vec(),
+ })?;
+
+ self.run = LineRun::new();
+
+ self.stack.add_spacing(self.ctx.line_spacing, LINE_KIND);
+
+ Ok(())
+ }
+
+ /// Finish the current line if it is not empty.
+ fn finish_line_if_not_empty(&mut self) -> LayoutResult<()> {
+ if !self.line_is_empty() {
+ self.finish_line()
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl LineRun {
+ fn new() -> LineRun {
+ LineRun {
+ layouts: vec![],
+ size: Size2D::ZERO,
+ alignment: None,
+ usable: None,
+ last_spacing: LastSpacing::Hard,
+ }
+ }
+}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index f937a054..53c3e91e 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -9,7 +9,7 @@ use crate::style::LayoutStyle;
mod actions;
mod tree;
-mod flex;
+mod line;
mod stack;
mod text;
@@ -30,7 +30,7 @@ pub mod prelude {
/// Different kinds of layouters (fully re-exported).
pub mod layouters {
pub use super::tree::layout;
- pub use super::flex::{FlexLayouter, FlexContext};
+ pub use super::line::{LineLayouter, LineContext};
pub use super::stack::{StackLayouter, StackContext};
pub use super::text::{layout_text, TextContext};
}
@@ -174,7 +174,7 @@ impl LayoutExpansion {
LayoutExpansion { horizontal, vertical }
}
- /// Borrow the spcified component mutably.
+ /// Borrow the specified component mutably.
pub fn get_mut(&mut self, axis: SpecificAxis) -> &mut bool {
match axis {
Horizontal => &mut self.horizontal,
@@ -366,6 +366,9 @@ const PARAGRAPH_KIND: SpacingKind = SpacingKind::Soft(1);
/// The standard spacing kind used for line spacing.
const LINE_KIND: SpacingKind = SpacingKind::Soft(2);
+/// The standard spacing kind used for word spacing.
+const WORD_KIND: SpacingKind = SpacingKind::Soft(1);
+
/// The last appeared spacing.
#[derive(Debug, Copy, Clone, PartialEq)]
enum LastSpacing {
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 3c659d8a..e0562672 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -1,11 +1,9 @@
use smallvec::smallvec;
+use crate::size::ValueBox;
use super::*;
/// The stack layouter stack boxes onto each other along the secondary layouting
/// axis.
-///
-/// The boxes are aligned along both axes according to their requested
-/// alignment.
#[derive(Debug, Clone)]
pub struct StackLayouter {
/// The context for layouting.
@@ -42,30 +40,21 @@ struct Space {
index: usize,
/// Whether to add the layout for this space even if it would be empty.
hard: bool,
- /// The so-far accumulated subspaces.
+ /// The so-far accumulated layouts.
layouts: Vec<(LayoutAxes, Layout)>,
- /// The specialized size of this subspace.
+ /// The specialized size of this space.
size: Size2D,
/// The specialized remaining space.
usable: Size2D,
/// The specialized extra-needed dimensions to affect the size at all.
extra: Size2D,
- /// Dictates the valid alignments for new boxes in this space.
- rulers: Rulers,
+ /// The rulers of a space dictate which alignments for new boxes are still
+ /// allowed and which require a new space to be started.
+ rulers: ValueBox<Alignment>,
/// The last added spacing if the last added thing was spacing.
last_spacing: LastSpacing,
}
-/// The rulers of a space dictate which alignments for new boxes are still
-/// allowed and which require a new space to be started.
-#[derive(Debug, Clone)]
-struct Rulers {
- top: Alignment,
- bottom: Alignment,
- left: Alignment,
- right: Alignment,
-}
-
impl StackLayouter {
/// Create a new stack layouter.
pub fn new(ctx: StackContext) -> StackLayouter {
@@ -157,26 +146,6 @@ impl StackLayouter {
}
}
- /// Update the rulers to account for the new layout. Returns true if a
- /// space break is necessary.
- fn update_rulers(&mut self, alignment: LayoutAlignment) -> bool {
- let axes = self.ctx.axes;
- let allowed = self.alignment_allowed(axes.primary, alignment.primary)
- && self.alignment_allowed(axes.secondary, alignment.secondary);
-
- if allowed {
- *self.space.rulers.get(axes.secondary) = alignment.secondary;
- }
-
- allowed
- }
-
- /// Whether the given alignment is still allowed according to the rulers.
- fn alignment_allowed(&mut self, direction: Direction, alignment: Alignment) -> bool {
- alignment >= *self.space.rulers.get(direction)
- && alignment <= self.space.rulers.get(direction.inv()).inv()
- }
-
/// Update the size metrics to reflect that a layout or spacing with the
/// given generalized dimensions has been added.
fn update_metrics(&mut self, dimensions: Size2D) {
@@ -196,10 +165,31 @@ impl StackLayouter {
*self.space.usable.get_secondary_mut(axes) -= dimensions.y;
}
+ /// Update the rulers to account for the new layout. Returns true if a
+ /// space break is necessary.
+ fn update_rulers(&mut self, alignment: LayoutAlignment) -> bool {
+ let allowed = self.is_fitting_alignment(alignment);
+ if allowed {
+ *self.space.rulers.get_mut(self.ctx.axes.secondary, Origin)
+ = alignment.secondary;
+ }
+ allowed
+ }
+
+ /// Whether a layout with the given alignment can still be layouted in the
+ /// active space.
+ pub fn is_fitting_alignment(&mut self, alignment: LayoutAlignment) -> bool {
+ self.is_fitting_axis(self.ctx.axes.primary, alignment.primary)
+ && self.is_fitting_axis(self.ctx.axes.secondary, alignment.secondary)
+ }
+
+ /// Whether the given alignment is still allowed according to the rulers.
+ fn is_fitting_axis(&mut self, direction: Direction, alignment: Alignment) -> bool {
+ alignment >= *self.space.rulers.get_mut(direction, Origin)
+ && alignment <= self.space.rulers.get_mut(direction, End).inv()
+ }
+
/// Change the layouting axes used by this layouter.
- ///
- /// This starts a new subspace (if the axes are actually different from the
- /// current ones).
pub fn set_axes(&mut self, axes: LayoutAxes) {
// Forget the spacing because it is not relevant anymore.
if axes.secondary != self.ctx.axes.secondary {
@@ -227,9 +217,7 @@ impl StackLayouter {
/// The remaining unpadded, unexpanding spaces. If a multi-layout is laid
/// out into these spaces, it will fit into this stack.
pub fn remaining(&self) -> LayoutSpaces {
- let dimensions = self.space.usable
- - Size2D::with_y(self.space.last_spacing.soft_or_zero())
- .specialized(self.ctx.axes);
+ let dimensions = self.usable();
let mut spaces = smallvec![LayoutSpace {
dimensions,
@@ -244,9 +232,11 @@ impl StackLayouter {
spaces
}
- /// The usable size along the primary axis.
- pub fn primary_usable(&self) -> Size {
- self.space.usable.get_primary(self.ctx.axes)
+ /// The remaining usable size.
+ pub fn usable(&self) -> Size2D {
+ self.space.usable
+ - Size2D::with_y(self.space.last_spacing.soft_or_zero())
+ .specialized(self.ctx.axes)
}
/// Whether the current layout space (not subspace) is empty.
@@ -409,24 +399,8 @@ impl Space {
size: Size2D::ZERO,
usable,
extra: Size2D::ZERO,
- rulers: Rulers {
- top: Origin,
- bottom: Origin,
- left: Origin,
- right: Origin,
- },
+ rulers: ValueBox::with_all(Origin),
last_spacing: LastSpacing::Hard,
}
}
}
-
-impl Rulers {
- fn get(&mut self, direction: Direction) -> &mut Alignment {
- match direction {
- TopToBottom => &mut self.top,
- BottomToTop => &mut self.bottom,
- LeftToRight => &mut self.left,
- RightToLeft => &mut self.right,
- }
- }
-}
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 996c5139..e9721429 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -12,6 +12,7 @@ use super::*;
pub struct TextContext<'a, 'p> {
pub loader: &'a SharedFontLoader<'p>,
pub style: &'a TextStyle,
+ pub axes: LayoutAxes,
pub alignment: LayoutAlignment,
}
@@ -50,22 +51,14 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
/// Layout the text
fn layout(mut self) -> LayoutResult<Layout> {
- for c in self.text.chars() {
- let (index, char_width) = self.select_font(c)?;
-
- self.width += char_width;
-
- if self.active_font != index {
- if !self.buffer.is_empty() {
- self.actions.add(LayoutAction::WriteText(self.buffer));
- self.buffer = String::new();
- }
-
- self.actions.add(LayoutAction::SetFont(index, self.ctx.style.font_size()));
- self.active_font = index;
+ if self.ctx.axes.primary.is_positive() {
+ for c in self.text.chars() {
+ self.layout_char(c)?;
+ }
+ } else {
+ for c in self.text.chars().rev() {
+ self.layout_char(c)?;
}
-
- self.buffer.push(c);
}
if !self.buffer.is_empty() {
@@ -79,6 +72,27 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
})
}
+ /// Layout an individual character.
+ fn layout_char(&mut self, c: char) -> LayoutResult<()> {
+ let (index, char_width) = self.select_font(c)?;
+
+ self.width += char_width;
+
+ if self.active_font != index {
+ if !self.buffer.is_empty() {
+ let text = std::mem::replace(&mut self.buffer, String::new());
+ self.actions.add(LayoutAction::WriteText(text));
+ }
+
+ self.actions.add(LayoutAction::SetFont(index, self.ctx.style.font_size()));
+ self.active_font = index;
+ }
+
+ self.buffer.push(c);
+
+ Ok(())
+ }
+
/// Select the best font for a character and return its index along with
/// the width of the char in the font.
fn select_font(&mut self, c: char) -> LayoutResult<(usize, Size)> {
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 7910fdd3..db59ca8d 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -16,7 +16,7 @@ pub fn layout(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout
#[derive(Debug, Clone)]
struct TreeLayouter<'a, 'p> {
ctx: LayoutContext<'a, 'p>,
- stack: StackLayouter,
+ layouter: LineLayouter,
style: LayoutStyle,
}
@@ -24,12 +24,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
/// Create a new syntax tree layouter.
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
TreeLayouter {
- stack: StackLayouter::new(StackContext {
+ layouter: LineLayouter::new(LineContext {
spaces: ctx.spaces.clone(),
axes: ctx.axes,
alignment: ctx.alignment,
repeat: ctx.repeat,
debug: ctx.debug,
+ line_spacing: ctx.style.text.line_spacing(),
}),
style: ctx.style.clone(),
ctx,
@@ -59,29 +60,27 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
let layout = layout_text(text, TextContext {
loader: &self.ctx.loader,
style: &self.style.text,
+ axes: self.ctx.axes,
alignment: self.ctx.alignment,
})?;
- self.stack.add(layout)
+ self.layouter.add(layout)
}
fn layout_space(&mut self) {
-
+ self.layouter.add_primary_spacing(self.style.text.word_spacing(), WORD_KIND);
}
fn layout_paragraph(&mut self) -> LayoutResult<()> {
- Ok(self.stack.add_spacing(
- paragraph_spacing(&self.style.text),
- PARAGRAPH_KIND,
- ))
+ self.layouter.add_secondary_spacing(self.style.text.paragraph_spacing(), PARAGRAPH_KIND)
}
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
let commands = func.0.layout(LayoutContext {
style: &self.style,
- spaces: self.stack.remaining(),
+ spaces: self.layouter.remaining(),
nested: true,
- debug: true,
+ debug: false,
.. self.ctx
})?;
@@ -98,26 +97,28 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
match command {
LayoutTree(tree) => self.layout(tree)?,
- Add(layout) => self.stack.add(layout)?,
- AddMultiple(layouts) => self.stack.add_multiple(layouts)?,
+ Add(layout) => self.layouter.add(layout)?,
+ AddMultiple(layouts) => self.layouter.add_multiple(layouts)?,
AddSpacing(space, kind, axis) => match axis {
- Primary => {},
- Secondary => self.stack.add_spacing(space, kind),
+ Primary => self.layouter.add_primary_spacing(space, kind),
+ Secondary => self.layouter.add_secondary_spacing(space, kind)?,
}
- FinishLine => {},
- FinishRun => {},
- FinishSpace => self.stack.finish_space(true)?,
+ FinishLine => self.layouter.finish_line()?,
+ FinishSpace => self.layouter.finish_space(true)?,
BreakParagraph => self.layout_paragraph()?,
BreakPage => {
if self.ctx.nested {
error!("page break cannot be issued from nested context");
}
- self.stack.finish_space(true)?
+ self.layouter.finish_space(true)?
}
- SetTextStyle(style) => self.style.text = style,
+ SetTextStyle(style) => {
+ self.layouter.set_line_spacing(style.line_spacing());
+ self.style.text = style;
+ }
SetPageStyle(style) => {
if self.ctx.nested {
error!("page style cannot be altered in nested context");
@@ -127,7 +128,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
let margins = style.margins();
self.ctx.base = style.dimensions.unpadded(margins);
- self.stack.set_spaces(smallvec![
+ self.layouter.set_spaces(smallvec![
LayoutSpace {
dimensions: style.dimensions,
padding: margins,
@@ -137,7 +138,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
SetAlignment(alignment) => self.ctx.alignment = alignment,
SetAxes(axes) => {
- self.stack.set_axes(axes);
+ self.layouter.set_axes(axes);
self.ctx.axes = axes;
}
}
@@ -146,18 +147,6 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
fn finish(self) -> LayoutResult<MultiLayout> {
- self.stack.finish()
+ self.layouter.finish()
}
}
-
-fn word_spacing(style: &TextStyle) -> Size {
- style.word_spacing * style.font_size()
-}
-
-fn flex_spacing(style: &TextStyle) -> Size {
- (style.line_spacing - 1.0) * style.font_size()
-}
-
-fn paragraph_spacing(style: &TextStyle) -> Size {
- (style.paragraph_spacing - 1.0) * style.font_size()
-}