Fixed par layouter directions 🗜

This commit is contained in:
Laurenz 2021-03-29 11:39:35 +02:00
parent 309b8f20a8
commit 88da325c7d

View File

@ -1,4 +1,5 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::mem;
use super::*; use super::*;
use crate::exec::FontProps; use crate::exec::FontProps;
@ -77,39 +78,35 @@ impl From<ParNode> for AnyNode {
} }
struct ParLayouter { struct ParLayouter {
dirs: Gen<Dir>, dir: Dir,
main: SpecAxis,
cross: SpecAxis,
line_spacing: Length, line_spacing: Length,
areas: Areas, areas: Areas,
finished: Vec<Frame>, finished: Vec<Frame>,
stack: Vec<(Length, Frame, Align)>, stack: Vec<(Length, Frame, Align)>,
stack_size: Gen<Length>, stack_size: Size,
line: Vec<(Length, Frame, Align)>, line: Vec<(Length, Frame, Align)>,
line_size: Gen<Length>, line_size: Size,
line_ruler: Align, line_ruler: Align,
} }
impl ParLayouter { impl ParLayouter {
fn new(dir: Dir, line_spacing: Length, areas: Areas) -> Self { fn new(dir: Dir, line_spacing: Length, areas: Areas) -> Self {
Self { Self {
dirs: Gen::new(Dir::TTB, dir), dir,
main: SpecAxis::Vertical,
cross: SpecAxis::Horizontal,
line_spacing, line_spacing,
areas, areas,
finished: vec![], finished: vec![],
stack: vec![], stack: vec![],
stack_size: Gen::ZERO, stack_size: Size::ZERO,
line: vec![], line: vec![],
line_size: Gen::ZERO, line_size: Size::ZERO,
line_ruler: Align::Start, line_ruler: Align::Start,
} }
} }
fn push_spacing(&mut self, amount: Length) { fn push_spacing(&mut self, amount: Length) {
let cross_max = self.areas.current.get(self.cross); let max = self.areas.current.width;
self.line_size.cross = (self.line_size.cross + amount).min(cross_max); self.line_size.width = (self.line_size.width + amount).min(max);
} }
fn push_text(&mut self, ctx: &mut LayoutContext, node: &TextNode, align: Align) { fn push_text(&mut self, ctx: &mut LayoutContext, node: &TextNode, align: Align) {
@ -192,98 +189,80 @@ impl ParLayouter {
} }
} }
// A line can contain frames with different alignments. They exact // A line can contain frames with different alignments. Their exact
// positions are calculated later depending on the alignments. // positions are calculated later depending on the alignments.
let size = frame.size.switch(self.main); let size = frame.size;
self.line.push((self.line_size.cross, frame, align)); self.line.push((self.line_size.width, frame, align));
self.line_size.cross += size.cross; self.line_size.width += size.width;
self.line_size.main = self.line_size.main.max(size.main); self.line_size.height = self.line_size.height.max(size.height);
self.line_ruler = align; self.line_ruler = align;
} }
fn usable(&self) -> Size { fn usable(&self) -> Size {
// Space occupied by previous lines is already removed from // Space occupied by previous lines is already removed from
// `areas.current`, but the cross-extent of the current line needs to be // `areas.current`, but the width of the current line needs to be
// subtracted to make sure the frame fits. // subtracted to make sure the frame fits.
let mut usable = self.areas.current; let mut usable = self.areas.current;
*usable.get_mut(self.cross) -= self.line_size.cross; usable.width -= self.line_size.width;
usable usable
} }
fn finish_line(&mut self) { fn finish_line(&mut self) {
let full_size = { let full_size = {
let expand = self.areas.expand.get(self.cross); let expand = self.areas.expand.horizontal;
let full = self.areas.full.get(self.cross); let full = self.areas.full.width;
Gen::new( Size::new(
self.line_size.main, expand.resolve(self.line_size.width, full),
expand.resolve(self.line_size.cross, full), self.line_size.height,
) )
}; };
let mut output = Frame::new(full_size.switch(self.main).to_size()); let mut output = Frame::new(full_size);
for (before, frame, align) in mem::take(&mut self.line) {
for (before, frame, align) in std::mem::take(&mut self.line) { // Align along the x axis.
let child_cross_size = frame.size.get(self.cross); let x = align.resolve(if self.dir.is_positive() {
before .. full_size.width - self.line_size.width + before
// Position along the cross axis.
let cross = align.resolve(if self.dirs.cross.is_positive() {
let after_with_self = self.line_size.cross - before;
before .. full_size.cross - after_with_self
} else { } else {
let before_with_self = before + child_cross_size; let before_with_self = before + frame.size.width;
let after = self.line_size.cross - (before + child_cross_size); full_size.width - before_with_self
full_size.cross - before_with_self .. after .. self.line_size.width - before_with_self
}); });
let pos = Gen::new(Length::ZERO, cross).switch(self.main).to_point(); let pos = Point::new(x, Length::ZERO);
output.push_frame(pos, frame); output.push_frame(pos, frame);
} }
// Add line spacing, but only between lines. // Add line spacing, but only between lines, not after the last line.
if !self.stack.is_empty() { if !self.stack.is_empty() {
self.stack_size.main += self.line_spacing; self.stack_size.height += self.line_spacing;
*self.areas.current.get_mut(self.main) -= self.line_spacing; self.areas.current.height -= self.line_spacing;
} }
// Update metrics of paragraph and reset for line. self.stack.push((self.stack_size.height, output, self.line_ruler));
self.stack.push((self.stack_size.main, output, self.line_ruler)); self.stack_size.height += full_size.height;
self.stack_size.main += full_size.main; self.stack_size.width = self.stack_size.width.max(full_size.width);
self.stack_size.cross = self.stack_size.cross.max(full_size.cross); self.areas.current.height -= full_size.height;
*self.areas.current.get_mut(self.main) -= full_size.main; self.line_size = Size::ZERO;
self.line_size = Gen::ZERO;
self.line_ruler = Align::Start; self.line_ruler = Align::Start;
} }
fn finish_area(&mut self) { fn finish_area(&mut self) {
let full_size = self.stack_size; let mut output = Frame::new(self.stack_size);
let mut output = Frame::new(full_size.switch(self.main).to_size()); for (before, line, align) in mem::take(&mut self.stack) {
// Align along the x axis.
for (before, line, cross_align) in std::mem::take(&mut self.stack) { let x = align.resolve(if self.dir.is_positive() {
let child_size = line.size.switch(self.main); Length::ZERO .. self.stack_size.width - line.size.width
// Position along the main axis.
let main = if self.dirs.main.is_positive() {
before
} else { } else {
full_size.main - (before + child_size.main) self.stack_size.width - line.size.width .. Length::ZERO
};
// Align along the cross axis.
let cross = cross_align.resolve(if self.dirs.cross.is_positive() {
Length::ZERO .. full_size.cross - child_size.cross
} else {
full_size.cross - child_size.cross .. Length::ZERO
}); });
let pos = Gen::new(main, cross).switch(self.main).to_point(); let pos = Point::new(x, before);
output.push_frame(pos, line); output.push_frame(pos, line);
} }
self.finished.push(output); self.finished.push(output);
self.areas.next(); self.areas.next();
self.stack_size = Size::ZERO;
// Reset metrics for the whole paragraph.
self.stack_size = Gen::ZERO;
} }
fn finish(mut self) -> Vec<Frame> { fn finish(mut self) -> Vec<Frame> {