mirror of
https://github.com/typst/typst
synced 2025-05-17 02:25:27 +08:00
Move align out of BoxLayout 🍫
This commit is contained in:
parent
335fa2d118
commit
d1c07260c0
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
),
|
||||
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.
|
||||
|
@ -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(
|
||||
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.align,
|
||||
&self.ctx.state.text,
|
||||
self.ctx.state.text.font_size(),
|
||||
variant,
|
||||
&self.ctx.state.text.fallback,
|
||||
&mut self.ctx.loader.borrow_mut(),
|
||||
)
|
||||
.await,
|
||||
);
|
||||
.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),
|
||||
|
@ -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)])
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user