mirror of
https://github.com/typst/typst
synced 2025-05-15 17:45:27 +08:00
Remove inline alignment from stack
The current inline alignment is very broken and leads to lots of subtle weirdness. Getting rid of it simplifies the stack's interface a lot. At a later point either: - inline alignment will be added back in a better way, or - all nodes will be able to expand or align themselves, meaning that the stack's children take care of their alignment
This commit is contained in:
parent
9ac125dea8
commit
5f4dde0a6b
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use super::Str;
|
use super::Str;
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
|
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild,
|
Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild,
|
||||||
StackNode,
|
StackNode,
|
||||||
@ -335,8 +335,7 @@ impl Builder {
|
|||||||
/// Push a block node into the active stack, finishing the active paragraph.
|
/// Push a block node into the active stack, finishing the active paragraph.
|
||||||
fn block(&mut self, node: impl Into<LayoutNode>) {
|
fn block(&mut self, node: impl Into<LayoutNode>) {
|
||||||
self.parbreak();
|
self.parbreak();
|
||||||
let aligns = self.style.aligns;
|
self.stack.push(StackChild::Any(node.into(), self.style.aligns.block));
|
||||||
self.stack.push(StackChild::Any(node.into(), aligns));
|
|
||||||
self.parbreak();
|
self.parbreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +406,7 @@ impl PageBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct StackBuilder {
|
struct StackBuilder {
|
||||||
dirs: Gen<Dir>,
|
dir: Dir,
|
||||||
children: Vec<StackChild>,
|
children: Vec<StackChild>,
|
||||||
last: Last<StackChild>,
|
last: Last<StackChild>,
|
||||||
par: ParBuilder,
|
par: ParBuilder,
|
||||||
@ -416,7 +415,7 @@ struct StackBuilder {
|
|||||||
impl StackBuilder {
|
impl StackBuilder {
|
||||||
fn new(style: &Style) -> Self {
|
fn new(style: &Style) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dirs: Gen::new(style.dir, Dir::TTB),
|
dir: Dir::TTB,
|
||||||
children: vec![],
|
children: vec![],
|
||||||
last: Last::None,
|
last: Last::None,
|
||||||
par: ParBuilder::new(style),
|
par: ParBuilder::new(style),
|
||||||
@ -445,17 +444,17 @@ impl StackBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build(self) -> StackNode {
|
fn build(self) -> StackNode {
|
||||||
let Self { dirs, mut children, par, mut last } = self;
|
let Self { dir, mut children, par, mut last } = self;
|
||||||
if let Some(par) = par.build() {
|
if let Some(par) = par.build() {
|
||||||
children.extend(last.any());
|
children.extend(last.any());
|
||||||
children.push(par);
|
children.push(par);
|
||||||
}
|
}
|
||||||
StackNode { dirs, children }
|
StackNode { dir, children }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParBuilder {
|
struct ParBuilder {
|
||||||
aligns: Gen<Align>,
|
align: Align,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
line_spacing: Length,
|
line_spacing: Length,
|
||||||
children: Vec<ParChild>,
|
children: Vec<ParChild>,
|
||||||
@ -465,7 +464,7 @@ struct ParBuilder {
|
|||||||
impl ParBuilder {
|
impl ParBuilder {
|
||||||
fn new(style: &Style) -> Self {
|
fn new(style: &Style) -> Self {
|
||||||
Self {
|
Self {
|
||||||
aligns: style.aligns,
|
align: style.aligns.block,
|
||||||
dir: style.dir,
|
dir: style.dir,
|
||||||
line_spacing: style.line_spacing(),
|
line_spacing: style.line_spacing(),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
@ -508,10 +507,10 @@ impl ParBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build(self) -> Option<StackChild> {
|
fn build(self) -> Option<StackChild> {
|
||||||
let Self { aligns, dir, line_spacing, children, .. } = self;
|
let Self { align, dir, line_spacing, children, .. } = self;
|
||||||
(!children.is_empty()).then(|| {
|
(!children.is_empty()).then(|| {
|
||||||
let node = ParNode { dir, line_spacing, children };
|
let node = ParNode { dir, line_spacing, children };
|
||||||
StackChild::Any(node.into(), aligns)
|
StackChild::Any(node.into(), align)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use super::{Eval, EvalContext, Str, Template, Value};
|
use super::{Eval, EvalContext, Str, Template, Value};
|
||||||
use crate::diag::TypResult;
|
use crate::diag::TypResult;
|
||||||
use crate::geom::{Dir, Gen};
|
use crate::geom::Align;
|
||||||
use crate::layout::{ParChild, ParNode, StackChild, StackNode};
|
use crate::layout::{ParChild, ParNode, StackChild, StackNode};
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
use crate::util::BoolExt;
|
use crate::util::BoolExt;
|
||||||
@ -117,11 +117,11 @@ fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) {
|
|||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
StackNode {
|
StackNode {
|
||||||
dirs: Gen::new(Dir::TTB, style.dir),
|
dir: style.dir,
|
||||||
children: vec![
|
children: vec![
|
||||||
StackChild::Any(label.into(), Gen::default()),
|
StackChild::Any(label.into(), Align::Start),
|
||||||
StackChild::Spacing((style.text.size / 2.0).into()),
|
StackChild::Spacing((style.text.size / 2.0).into()),
|
||||||
StackChild::Any(body.to_stack(&style).into(), Gen::default()),
|
StackChild::Any(body.to_stack(&style).into(), Align::Start),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -6,11 +6,8 @@ use super::*;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "layout-cache", derive(Hash))]
|
#[cfg_attr(feature = "layout-cache", derive(Hash))]
|
||||||
pub struct StackNode {
|
pub struct StackNode {
|
||||||
/// The inline and block directions of this stack.
|
/// The stacking direction.
|
||||||
///
|
pub dir: Dir,
|
||||||
/// The children are stacked along the block direction. The inline direction
|
|
||||||
/// is required for aligning the children.
|
|
||||||
pub dirs: Gen<Dir>,
|
|
||||||
/// The nodes to be stacked.
|
/// The nodes to be stacked.
|
||||||
pub children: Vec<StackChild>,
|
pub children: Vec<StackChild>,
|
||||||
}
|
}
|
||||||
@ -21,7 +18,7 @@ pub enum StackChild {
|
|||||||
/// Spacing between other nodes.
|
/// Spacing between other nodes.
|
||||||
Spacing(Linear),
|
Spacing(Linear),
|
||||||
/// Any child node and how to align it in the stack.
|
/// Any child node and how to align it in the stack.
|
||||||
Any(LayoutNode, Gen<Align>),
|
Any(LayoutNode, Align),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for StackNode {
|
impl Layout for StackNode {
|
||||||
@ -72,7 +69,7 @@ struct StackLayouter<'a> {
|
|||||||
overflowing: bool,
|
overflowing: bool,
|
||||||
/// Offset, alignment and frame for all children that fit into the current
|
/// Offset, alignment and frame for all children that fit into the current
|
||||||
/// region. The exact positions are not known yet.
|
/// region. The exact positions are not known yet.
|
||||||
frames: Vec<(Length, Gen<Align>, Rc<Frame>)>,
|
frames: Vec<(Length, Align, Rc<Frame>)>,
|
||||||
/// Finished frames for previous regions.
|
/// Finished frames for previous regions.
|
||||||
finished: Vec<Constrained<Rc<Frame>>>,
|
finished: Vec<Constrained<Rc<Frame>>>,
|
||||||
}
|
}
|
||||||
@ -80,7 +77,7 @@ struct StackLayouter<'a> {
|
|||||||
impl<'a> StackLayouter<'a> {
|
impl<'a> StackLayouter<'a> {
|
||||||
/// Create a new stack layouter.
|
/// Create a new stack layouter.
|
||||||
fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
|
fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
|
||||||
let block = stack.dirs.block.axis();
|
let block = stack.dir.axis();
|
||||||
let full = regions.current;
|
let full = regions.current;
|
||||||
let expand = regions.expand;
|
let expand = regions.expand;
|
||||||
|
|
||||||
@ -107,14 +104,14 @@ impl<'a> StackLayouter<'a> {
|
|||||||
for child in &self.stack.children {
|
for child in &self.stack.children {
|
||||||
match *child {
|
match *child {
|
||||||
StackChild::Spacing(amount) => self.space(amount),
|
StackChild::Spacing(amount) => self.space(amount),
|
||||||
StackChild::Any(ref node, aligns) => {
|
StackChild::Any(ref node, align) => {
|
||||||
let nodes = node.layout(ctx, &self.regions);
|
let nodes = node.layout(ctx, &self.regions);
|
||||||
let len = nodes.len();
|
let len = nodes.len();
|
||||||
for (i, frame) in nodes.into_iter().enumerate() {
|
for (i, frame) in nodes.into_iter().enumerate() {
|
||||||
if i + 1 < len {
|
if i + 1 < len {
|
||||||
self.constraints.exact = self.full.to_spec().map(Some);
|
self.constraints.exact = self.full.to_spec().map(Some);
|
||||||
}
|
}
|
||||||
self.push_frame(frame.item, aligns);
|
self.push_frame(frame.item, align);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,11 +139,11 @@ impl<'a> StackLayouter<'a> {
|
|||||||
|
|
||||||
/// Push a frame into the current or next fitting region, finishing regions
|
/// Push a frame into the current or next fitting region, finishing regions
|
||||||
/// if necessary.
|
/// if necessary.
|
||||||
fn push_frame(&mut self, frame: Rc<Frame>, aligns: Gen<Align>) {
|
fn push_frame(&mut self, frame: Rc<Frame>, align: Align) {
|
||||||
let size = frame.size.to_gen(self.block);
|
let size = frame.size.to_gen(self.block);
|
||||||
|
|
||||||
// Don't allow `Start` after `End` in the same region.
|
// Don't allow `Start` after `End` in the same region.
|
||||||
if aligns.block < self.ruler {
|
if align < self.ruler {
|
||||||
self.finish_region();
|
self.finish_region();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,10 +169,10 @@ impl<'a> StackLayouter<'a> {
|
|||||||
let offset = self.used.block;
|
let offset = self.used.block;
|
||||||
self.used.block += size.block;
|
self.used.block += size.block;
|
||||||
self.used.inline.set_max(size.inline);
|
self.used.inline.set_max(size.inline);
|
||||||
self.ruler = aligns.block;
|
self.ruler = align;
|
||||||
|
|
||||||
// Remember the frame with offset and alignment.
|
// Remember the frame with offset and alignment.
|
||||||
self.frames.push((offset, aligns, frame));
|
self.frames.push((offset, align, frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the frame for one region.
|
/// Finish the frame for one region.
|
||||||
@ -211,20 +208,14 @@ impl<'a> StackLayouter<'a> {
|
|||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
||||||
// Place all frames.
|
// Place all frames.
|
||||||
for (offset, aligns, frame) in self.frames.drain(..) {
|
for (offset, align, frame) in self.frames.drain(..) {
|
||||||
let stack_size = size.to_gen(self.block);
|
let stack_size = size.to_gen(self.block);
|
||||||
let child_size = frame.size.to_gen(self.block);
|
let child_size = frame.size.to_gen(self.block);
|
||||||
|
|
||||||
// Align along the inline axis.
|
|
||||||
let inline = aligns.inline.resolve(
|
|
||||||
self.stack.dirs.inline,
|
|
||||||
Length::zero() .. stack_size.inline - child_size.inline,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Align along the block axis.
|
// Align along the block axis.
|
||||||
let block = aligns.block.resolve(
|
let block = align.resolve(
|
||||||
self.stack.dirs.block,
|
self.stack.dir,
|
||||||
if self.stack.dirs.block.is_positive() {
|
if self.stack.dir.is_positive() {
|
||||||
offset .. stack_size.block - self.used.block + offset
|
offset .. stack_size.block - self.used.block + offset
|
||||||
} else {
|
} else {
|
||||||
let offset_with_self = offset + child_size.block;
|
let offset_with_self = offset + child_size.block;
|
||||||
@ -233,7 +224,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let pos = Gen::new(inline, block).to_point(self.block);
|
let pos = Gen::new(Length::zero(), block).to_point(self.block);
|
||||||
|
|
||||||
// The baseline of the stack is that of the first frame.
|
// The baseline of the stack is that of the first frame.
|
||||||
if first {
|
if first {
|
||||||
|
@ -204,27 +204,11 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
Value::Template(v) => Self::Any(v),
|
Value::Template(v) => Self::Any(v),
|
||||||
}
|
}
|
||||||
|
|
||||||
let dir = args.named("dir")?;
|
let dir = args.named("dir")?.unwrap_or(Dir::TTB);
|
||||||
let spacing = args.named("spacing")?;
|
let spacing = args.named("spacing")?;
|
||||||
let list: Vec<Child> = args.all().collect();
|
let list: Vec<Child> = args.all().collect();
|
||||||
|
|
||||||
Ok(Value::Template(Template::from_block(move |style| {
|
Ok(Value::Template(Template::from_block(move |style| {
|
||||||
let mut dirs = Gen::new(style.dir, dir.unwrap_or(Dir::TTB));
|
|
||||||
|
|
||||||
// If the directions become aligned, fix up the inline direction since
|
|
||||||
// that's the one that is not user-defined.
|
|
||||||
if dirs.inline.axis() == dirs.block.axis() {
|
|
||||||
dirs.inline = Dir::TTB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the current alignments for all children, but take care to apply
|
|
||||||
// them to the correct axes (by swapping them if the stack axes are
|
|
||||||
// different from the style axes).
|
|
||||||
let mut aligns = style.aligns;
|
|
||||||
if dirs.inline.axis() != style.dir.axis() {
|
|
||||||
aligns = Gen::new(aligns.block, aligns.inline);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut children = vec![];
|
let mut children = vec![];
|
||||||
let mut delayed = None;
|
let mut delayed = None;
|
||||||
|
|
||||||
@ -241,13 +225,13 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let node = template.to_stack(style).into();
|
let node = template.to_stack(style).into();
|
||||||
children.push(StackChild::Any(node, aligns));
|
children.push(StackChild::Any(node, style.aligns.block));
|
||||||
delayed = spacing;
|
delayed = spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StackNode { dirs, children }
|
StackNode { dir, children }
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 319 B |
@ -20,9 +20,6 @@
|
|||||||
#stack(dir: btt, ..items)
|
#stack(dir: btt, ..items)
|
||||||
#pagebreak()
|
#pagebreak()
|
||||||
|
|
||||||
// Currently stack works like flexbox apparently.
|
|
||||||
#stack(dir: ltr, ..items)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test spacing.
|
// Test spacing.
|
||||||
#page(width: 50pt, margins: 0pt)
|
#page(width: 50pt, margins: 0pt)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user