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

View File

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

View File

@ -34,14 +34,10 @@ pub struct StackLayouter {
/// The context for stack layouting.
#[derive(Debug, Clone)]
pub struct StackContext {
/// The spaces to layout into.
pub spaces: Vec<LayoutSpace>,
/// The initial layouting system, which can be updated through `set_sys`.
pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line
/// layouting itself, but rather how the finished layout will be positioned
/// in a parent layout.
pub align: LayoutAlign,
/// The spaces to layout into.
pub spaces: Vec<LayoutSpace>,
/// Whether to spill over into copies of the last space or finish layouting
/// when the last space is used up.
pub repeat: bool,
@ -59,11 +55,11 @@ impl StackLayouter {
}
/// 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.
// TODO: Issue warning for non-fitting alignment in non-repeating
// context.
if !self.update_rulers(layout.align) && self.ctx.repeat {
if !self.update_rulers(align) && self.ctx.repeat {
self.finish_space(true);
}
@ -84,7 +80,7 @@ impl StackLayouter {
// Add the box to the vector and remember that spacings are allowed
// again.
self.space.layouts.push((self.ctx.sys, layout));
self.space.layouts.push((self.ctx.sys, align, layout));
self.space.last_spacing = LastSpacing::None;
}
@ -100,10 +96,8 @@ impl StackLayouter {
self.update_metrics(size);
self.space.layouts.push((
self.ctx.sys,
BoxLayout::new(
size.specialized(self.ctx.sys),
LayoutAlign::default(),
),
LayoutAlign::default(),
BoxLayout::new(size.specialized(self.ctx.sys)),
));
self.space.last_spacing = LastSpacing::Hard;
@ -279,7 +273,7 @@ impl StackLayouter {
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
// by the predecessors of this layout) as the initial bounding box
// of this layout.
@ -303,7 +297,7 @@ impl StackLayouter {
let mut rotation = SpecAxis::Vertical;
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`)
// 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
// 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);
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 align = child.align;
// The space in which this layout is aligned is given by the
// 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.
hard: bool,
/// The so-far accumulated layouts.
layouts: Vec<(LayoutSystem, BoxLayout)>,
layouts: Vec<(LayoutSystem, LayoutAlign, BoxLayout)>,
/// The specialized size of this space.
size: Size,
/// The specialized remaining space.

View File

@ -1,5 +1,7 @@
//! Layouting of syntax trees.
use fontdock::FontStyle;
use super::*;
use crate::eval::Eval;
use crate::shaping;
@ -25,7 +27,6 @@ impl<'a> TreeLayouter<'a> {
let layouter = LineLayouter::new(LineContext {
spaces: ctx.constraints.spaces.clone(),
sys: ctx.state.sys,
align: ctx.state.align,
repeat: ctx.constraints.repeat,
line_spacing: ctx.state.text.line_spacing(),
});
@ -99,16 +100,31 @@ impl<'a> TreeLayouter<'a> {
}
async fn layout_text(&mut self, text: &str) {
self.layouter.add(
shaping::shape(
text,
self.ctx.state.sys.primary,
self.ctx.state.align,
&self.ctx.state.text,
&mut self.ctx.loader.borrow_mut(),
)
.await,
);
let mut variant = self.ctx.state.text.variant;
if self.ctx.state.text.strong {
variant.weight = variant.weight.thicken(300);
}
if self.ctx.state.text.emph {
variant.style = match variant.style {
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) {
@ -160,9 +176,7 @@ impl<'a> TreeLayouter<'a> {
};
let val = expr.v.eval(self.ctx).await;
let commands = val.span_with(expr.span).into_commands();
for command in commands {
self.execute_command(command, expr.span).await;
}
@ -173,7 +187,7 @@ impl<'a> TreeLayouter<'a> {
match command {
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 {
GenAxis::Primary => self.layouter.add_primary_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");
args.done(ctx);
let align = ctx.state.align;
let constraints = &mut ctx.constraints;
constraints.base = constraints.spaces[0].size;
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 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 fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant};
use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant};
use ttf_parser::GlyphId;
use crate::eval::TextState;
use crate::font::FontLoader;
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.
pub async fn shape(
text: &str,
dir: Dir,
align: LayoutAlign,
state: &TextState,
size: f64,
variant: FontVariant,
fallback: &FallbackTree,
loader: &mut FontLoader,
) -> 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.
@ -86,32 +86,19 @@ impl<'a> Shaper<'a> {
fn new(
text: &'a str,
dir: Dir,
align: LayoutAlign,
state: &'a TextState,
size: f64,
variant: FontVariant,
fallback: &'a FallbackTree,
loader: &'a mut FontLoader,
) -> 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 {
text,
dir,
variant,
fallback: &state.fallback,
fallback,
loader,
shaped: Shaped::new(FaceId::MAX, state.font_size()),
layout: BoxLayout::new(Size::new(0.0, state.font_size()), align),
shaped: Shaped::new(FaceId::MAX, size),
layout: BoxLayout::new(Size::new(0.0, size)),
offset: 0.0,
}
}