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:
Laurenz 2021-10-13 14:15:17 +02:00
parent 9ac125dea8
commit 5f4dde0a6b
6 changed files with 33 additions and 62 deletions

View File

@ -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)
}) })
} }
} }

View File

@ -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),
], ],
} }
}); });

View File

@ -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 {

View File

@ -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

View File

@ -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)