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.
|
/// 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);
|
||||||
|
@ -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),
|
||||||
|
@ -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.
|
||||||
|
@ -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),
|
||||||
|
@ -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)])
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user