Move align out of BoxLayout 🍫

This commit is contained in:
Laurenz 2020-10-05 13:39:33 +02:00
parent 335fa2d118
commit d1c07260c0
6 changed files with 71 additions and 85 deletions

View File

@ -23,14 +23,10 @@ pub struct LineLayouter {
/// The context for line layouting. /// The context for line layouting.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LineContext { pub struct LineContext {
/// The spaces to layout into.
pub spaces: Vec<LayoutSpace>,
/// The initial layouting system, which can be updated through `set_sys`. /// The initial layouting system, which can be updated through `set_sys`.
pub sys: LayoutSystem, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The spaces to layout into.
/// layouting itself, but rather how the finished layout will be positioned pub spaces: Vec<LayoutSpace>,
/// in a parent layout.
pub align: LayoutAlign,
/// 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,
@ -45,7 +41,6 @@ impl LineLayouter {
stack: StackLayouter::new(StackContext { stack: StackLayouter::new(StackContext {
spaces: ctx.spaces.clone(), spaces: ctx.spaces.clone(),
sys: ctx.sys, sys: ctx.sys,
align: ctx.align,
repeat: ctx.repeat, repeat: ctx.repeat,
}), }),
ctx, ctx,
@ -54,26 +49,26 @@ impl LineLayouter {
} }
/// Add a layout. /// Add a layout.
pub fn add(&mut self, layout: BoxLayout) { pub fn add(&mut self, layout: BoxLayout, align: LayoutAlign) {
let sys = self.ctx.sys; let sys = self.ctx.sys;
if let Some(align) = self.run.align { if let Some(prev) = self.run.align {
if layout.align.secondary != align.secondary { if align.secondary != prev.secondary {
// TODO: Issue warning for non-fitting alignment in // TODO: Issue warning for non-fitting alignment in
// non-repeating context. // non-repeating context.
let fitting = self.stack.is_fitting_alignment(layout.align); let fitting = self.stack.is_fitting_alignment(align);
if !fitting && self.ctx.repeat { if !fitting && self.ctx.repeat {
self.finish_space(true); self.finish_space(true);
} else { } else {
self.finish_line(); self.finish_line();
} }
} else if layout.align.primary < align.primary { } else if align.primary < prev.primary {
self.finish_line(); self.finish_line();
} else if layout.align.primary > align.primary { } else if align.primary > prev.primary {
let mut rest_run = LineRun::new(); let mut rest_run = LineRun::new();
let usable = self.stack.usable().primary(sys); let usable = self.stack.usable().primary(sys);
rest_run.usable = Some(match layout.align.primary { rest_run.usable = Some(match align.primary {
GenAlign::Start => unreachable!("start > x"), GenAlign::Start => unreachable!("start > x"),
GenAlign::Center => usable - 2.0 * self.run.size.width, GenAlign::Center => usable - 2.0 * self.run.size.width,
GenAlign::End => usable - self.run.size.width, GenAlign::End => usable - self.run.size.width,
@ -105,7 +100,7 @@ impl LineLayouter {
} }
} }
self.run.align = Some(layout.align); self.run.align = Some(align);
self.run.layouts.push((self.run.size.width, layout)); self.run.layouts.push((self.run.size.width, layout));
self.run.size.width += size.width; self.run.size.width += size.width;
@ -211,10 +206,8 @@ impl LineLayouter {
/// Finish the active line and start a new one. /// Finish the active line and start a new one.
pub fn finish_line(&mut self) { pub fn finish_line(&mut self) {
let mut layout = BoxLayout::new( let mut layout = BoxLayout::new(self.run.size.specialized(self.ctx.sys));
self.run.size.specialized(self.ctx.sys), let align = self.run.align.unwrap_or_default();
self.run.align.unwrap_or_default(),
);
let layouts = std::mem::take(&mut self.run.layouts); let layouts = std::mem::take(&mut self.run.layouts);
for (offset, child) in layouts { for (offset, child) in layouts {
@ -227,7 +220,7 @@ impl LineLayouter {
layout.push_layout(pos, child); layout.push_layout(pos, child);
} }
self.stack.add(layout); self.stack.add(layout, align);
self.run = LineRun::new(); self.run = LineRun::new();
self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE); self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE);

View File

@ -54,16 +54,14 @@ pub async fn layout(
pub struct BoxLayout { pub struct BoxLayout {
/// The size of the box. /// The size of the box.
pub size: Size, pub size: Size,
/// How to align this box in a parent container.
pub align: LayoutAlign,
/// The elements composing this layout. /// The elements composing this layout.
pub elements: Vec<(Point, LayoutElement)>, pub elements: Vec<(Point, LayoutElement)>,
} }
impl BoxLayout { impl BoxLayout {
/// Create an new empty collection. /// Create a new empty collection.
pub fn new(size: Size, align: LayoutAlign) -> Self { pub fn new(size: Size) -> Self {
Self { size, align, elements: vec![] } Self { size, elements: vec![] }
} }
/// Add an element at a position. /// Add an element at a position.
@ -161,7 +159,7 @@ pub enum Command {
LayoutSyntaxTree(SynTree), LayoutSyntaxTree(SynTree),
/// Add a finished layout. /// Add a finished layout.
Add(BoxLayout), Add(BoxLayout, LayoutAlign),
/// Add spacing of the given kind along the primary or secondary axis. The /// Add spacing of the given kind along the primary or secondary axis. The
/// kind defines how the spacing interacts with surrounding spacing. /// kind defines how the spacing interacts with surrounding spacing.
AddSpacing(f64, SpacingKind, GenAxis), AddSpacing(f64, SpacingKind, GenAxis),

View File

@ -34,14 +34,10 @@ pub struct StackLayouter {
/// The context for stack layouting. /// The context for stack layouting.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StackContext { pub struct StackContext {
/// The spaces to layout into.
pub spaces: Vec<LayoutSpace>,
/// The initial layouting system, which can be updated through `set_sys`. /// The initial layouting system, which can be updated through `set_sys`.
pub sys: LayoutSystem, pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line /// The spaces to layout into.
/// layouting itself, but rather how the finished layout will be positioned pub spaces: Vec<LayoutSpace>,
/// in a parent layout.
pub align: LayoutAlign,
/// 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,
@ -59,11 +55,11 @@ impl StackLayouter {
} }
/// Add a layout to the stack. /// Add a layout to the stack.
pub fn add(&mut self, layout: BoxLayout) { pub fn add(&mut self, layout: BoxLayout, align: LayoutAlign) {
// If the alignment cannot be fitted in this space, finish it. // If the alignment cannot be fitted in this space, finish it.
// TODO: Issue warning for non-fitting alignment in non-repeating // TODO: Issue warning for non-fitting alignment in non-repeating
// context. // context.
if !self.update_rulers(layout.align) && self.ctx.repeat { if !self.update_rulers(align) && self.ctx.repeat {
self.finish_space(true); self.finish_space(true);
} }
@ -84,7 +80,7 @@ impl StackLayouter {
// 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.sys, layout)); self.space.layouts.push((self.ctx.sys, align, layout));
self.space.last_spacing = LastSpacing::None; self.space.last_spacing = LastSpacing::None;
} }
@ -100,10 +96,8 @@ impl StackLayouter {
self.update_metrics(size); self.update_metrics(size);
self.space.layouts.push(( self.space.layouts.push((
self.ctx.sys, self.ctx.sys,
BoxLayout::new( LayoutAlign::default(),
size.specialized(self.ctx.sys), BoxLayout::new(size.specialized(self.ctx.sys)),
LayoutAlign::default(),
),
)); ));
self.space.last_spacing = LastSpacing::Hard; self.space.last_spacing = LastSpacing::Hard;
@ -279,7 +273,7 @@ impl StackLayouter {
y1: start.y + self.space.size.height, y1: start.y + self.space.size.height,
}; };
for (sys, 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.
@ -303,7 +297,7 @@ impl StackLayouter {
let mut rotation = SpecAxis::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 (sys, layout) = entry; let (sys, _, layout) = entry;
// When the axes are rotated, the maximal primary size (`extent.x`) // When the axes are rotated, the maximal primary size (`extent.x`)
// dictates how much secondary extent the whole run had. This value // dictates how much secondary extent the whole run had. This value
@ -331,12 +325,11 @@ impl StackLayouter {
// Step 4: Align each layout in its bounding box and collect everything // Step 4: Align each layout in its bounding box and collect everything
// into a single finished layout. // into a single finished layout.
let mut layout = BoxLayout::new(size, self.ctx.align); let mut layout = BoxLayout::new(size);
let layouts = std::mem::take(&mut self.space.layouts); let layouts = std::mem::take(&mut self.space.layouts);
for ((sys, child), bound) in layouts.into_iter().zip(bounds) { for ((sys, align, child), bound) in layouts.into_iter().zip(bounds) {
let size = child.size.specialized(sys); let size = child.size.specialized(sys);
let align = child.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 its bounding box. // distances between the borders of its bounding box.
@ -373,7 +366,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<(LayoutSystem, BoxLayout)>, layouts: Vec<(LayoutSystem, LayoutAlign, 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.

View File

@ -1,5 +1,7 @@
//! Layouting of syntax trees. //! Layouting of syntax trees.
use fontdock::FontStyle;
use super::*; use super::*;
use crate::eval::Eval; use crate::eval::Eval;
use crate::shaping; use crate::shaping;
@ -25,7 +27,6 @@ impl<'a> TreeLayouter<'a> {
let layouter = LineLayouter::new(LineContext { let layouter = LineLayouter::new(LineContext {
spaces: ctx.constraints.spaces.clone(), spaces: ctx.constraints.spaces.clone(),
sys: ctx.state.sys, sys: ctx.state.sys,
align: ctx.state.align,
repeat: ctx.constraints.repeat, repeat: ctx.constraints.repeat,
line_spacing: ctx.state.text.line_spacing(), line_spacing: ctx.state.text.line_spacing(),
}); });
@ -99,16 +100,31 @@ impl<'a> TreeLayouter<'a> {
} }
async fn layout_text(&mut self, text: &str) { async fn layout_text(&mut self, text: &str) {
self.layouter.add( let mut variant = self.ctx.state.text.variant;
shaping::shape(
text, if self.ctx.state.text.strong {
self.ctx.state.sys.primary, variant.weight = variant.weight.thicken(300);
self.ctx.state.align, }
&self.ctx.state.text,
&mut self.ctx.loader.borrow_mut(), if self.ctx.state.text.emph {
) variant.style = match variant.style {
.await, FontStyle::Normal => FontStyle::Italic,
); FontStyle::Italic => FontStyle::Normal,
FontStyle::Oblique => FontStyle::Normal,
}
}
let boxed = shaping::shape(
text,
self.ctx.state.sys.primary,
self.ctx.state.text.font_size(),
variant,
&self.ctx.state.text.fallback,
&mut self.ctx.loader.borrow_mut(),
)
.await;
self.layouter.add(boxed, self.ctx.state.align);
} }
async fn layout_heading(&mut self, heading: &NodeHeading) { async fn layout_heading(&mut self, heading: &NodeHeading) {
@ -160,9 +176,7 @@ impl<'a> TreeLayouter<'a> {
}; };
let val = expr.v.eval(self.ctx).await; let val = expr.v.eval(self.ctx).await;
let commands = val.span_with(expr.span).into_commands(); let commands = val.span_with(expr.span).into_commands();
for command in commands { for command in commands {
self.execute_command(command, expr.span).await; self.execute_command(command, expr.span).await;
} }
@ -173,7 +187,7 @@ impl<'a> TreeLayouter<'a> {
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, align) => self.layouter.add(layout, align),
AddSpacing(space, kind, axis) => match axis { AddSpacing(space, kind, axis) => match axis {
GenAxis::Primary => self.layouter.add_primary_spacing(space, kind), GenAxis::Primary => self.layouter.add_primary_spacing(space, kind),
GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind), GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind),

View File

@ -12,6 +12,7 @@ pub async fn boxed(mut args: Args, ctx: &mut LayoutContext) -> Value {
let height = args.get::<_, Linear>(ctx, "height"); let height = args.get::<_, Linear>(ctx, "height");
args.done(ctx); args.done(ctx);
let align = ctx.state.align;
let constraints = &mut ctx.constraints; let constraints = &mut ctx.constraints;
constraints.base = constraints.spaces[0].size; constraints.base = constraints.spaces[0].size;
constraints.spaces.truncate(1); constraints.spaces.truncate(1);
@ -34,5 +35,5 @@ pub async fn boxed(mut args: Args, ctx: &mut LayoutContext) -> Value {
let layouted = layout_tree(&body, ctx).await; let layouted = layout_tree(&body, ctx).await;
let layout = layouted.into_iter().next().unwrap(); let layout = layouted.into_iter().next().unwrap();
Value::Commands(vec![Add(layout)]) Value::Commands(vec![Add(layout, align)])
} }

View File

@ -6,23 +6,23 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant}; use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant};
use ttf_parser::GlyphId; use ttf_parser::GlyphId;
use crate::eval::TextState;
use crate::font::FontLoader; use crate::font::FontLoader;
use crate::geom::{Point, Size}; use crate::geom::{Point, Size};
use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement}; use crate::layout::{BoxLayout, Dir, LayoutElement};
/// Shape text into a box containing shaped runs. /// Shape text into a box containing shaped runs.
pub async fn shape( pub async fn shape(
text: &str, text: &str,
dir: Dir, dir: Dir,
align: LayoutAlign, size: f64,
state: &TextState, variant: FontVariant,
fallback: &FallbackTree,
loader: &mut FontLoader, loader: &mut FontLoader,
) -> BoxLayout { ) -> BoxLayout {
Shaper::new(text, dir, align, state, loader).shape().await Shaper::new(text, dir, size, variant, fallback, loader).shape().await
} }
/// A shaped run of text. /// A shaped run of text.
@ -86,32 +86,19 @@ impl<'a> Shaper<'a> {
fn new( fn new(
text: &'a str, text: &'a str,
dir: Dir, dir: Dir,
align: LayoutAlign, size: f64,
state: &'a TextState, variant: FontVariant,
fallback: &'a FallbackTree,
loader: &'a mut FontLoader, loader: &'a mut FontLoader,
) -> Self { ) -> Self {
let mut variant = state.variant;
if state.strong {
variant.weight = variant.weight.thicken(300);
}
if state.emph {
variant.style = match variant.style {
FontStyle::Normal => FontStyle::Italic,
FontStyle::Italic => FontStyle::Normal,
FontStyle::Oblique => FontStyle::Normal,
}
}
Self { Self {
text, text,
dir, dir,
variant, variant,
fallback: &state.fallback, fallback,
loader, loader,
shaped: Shaped::new(FaceId::MAX, state.font_size()), shaped: Shaped::new(FaceId::MAX, size),
layout: BoxLayout::new(Size::new(0.0, state.font_size()), align), layout: BoxLayout::new(Size::new(0.0, size)),
offset: 0.0, offset: 0.0,
} }
} }