diff --git a/src/layout/frame.rs b/src/layout/frame.rs index 667c45662..c74f2abd1 100644 --- a/src/layout/frame.rs +++ b/src/layout/frame.rs @@ -26,8 +26,8 @@ pub struct Frame { pub enum FrameChild { /// A leaf node in the frame tree. Element(Element), - /// An interior node with an optional index. - Frame(Option, Rc), + /// An interior group. + Group(Rc), } /// The building block frames are composed of. @@ -110,12 +110,7 @@ impl Frame { /// Add a frame element. pub fn push_frame(&mut self, pos: Point, subframe: Rc) { - self.children.push((pos, FrameChild::Frame(None, subframe))) - } - - /// Add a frame element with an index of arbitrary use. - pub fn push_indexed_frame(&mut self, pos: Point, index: usize, subframe: Rc) { - self.children.push((pos, FrameChild::Frame(Some(index), subframe))); + self.children.push((pos, FrameChild::Group(subframe))) } /// Add all elements of another frame, placing them relative to the given @@ -152,7 +147,7 @@ impl<'a> Iterator for Elements<'a> { fn next(&mut self) -> Option { let (cursor, offset, frame) = self.stack.last_mut()?; match frame.children.get(*cursor) { - Some((pos, FrameChild::Frame(_, f))) => { + Some((pos, FrameChild::Group(f))) => { let new_offset = *offset + *pos; self.stack.push((0, new_offset, f.as_ref())); self.next() @@ -194,7 +189,7 @@ impl Debug for FrameChild { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::Element(element) => element.fmt(f), - Self::Frame(_, frame) => frame.fmt(f), + Self::Group(frame) => frame.fmt(f), } } } diff --git a/src/layout/par.rs b/src/layout/par.rs index c8ced2f84..8166779ee 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -50,7 +50,7 @@ impl Layout for ParNode { let layouter = ParLayouter::new(self, ctx, regions, bidi); // Find suitable linebreaks. - layouter.layout(ctx, &self.children, regions.clone()) + layouter.layout(ctx, regions.clone()) } } @@ -131,25 +131,25 @@ impl<'a> ParLayouter<'a> { let mut ranges = vec![]; // Layout the children and collect them into items. - for (i, (range, child)) in par.ranges().zip(&par.children).enumerate() { - match *child { + for (range, child) in par.ranges().zip(&par.children) { + match child { ParChild::Spacing(amount) => { let resolved = amount.resolve(regions.current.w); items.push(ParItem::Spacing(resolved)); ranges.push(range); } - ParChild::Text(_, align, ref state, _) => { + ParChild::Text(_, align, state, decos) => { // TODO: Also split by language and script. for (subrange, dir) in split_runs(&bidi, range) { let text = &bidi.text[subrange.clone()]; let shaped = shape(ctx, text, dir, state); - items.push(ParItem::Text(shaped, align, i)); + items.push(ParItem::Text(shaped, *align, decos)); ranges.push(subrange); } } - ParChild::Any(ref node, align, _) => { + ParChild::Any(node, align, decos) => { let frame = node.layout(ctx, regions).remove(0); - items.push(ParItem::Frame(frame.item, align, i)); + items.push(ParItem::Frame(frame.item, *align, decos)); ranges.push(range); } } @@ -168,10 +168,9 @@ impl<'a> ParLayouter<'a> { fn layout( self, ctx: &mut LayoutContext, - children: &[ParChild], regions: Regions, ) -> Vec>> { - let mut stack = LineStack::new(self.line_spacing, children, regions); + let mut stack = LineStack::new(self.line_spacing, regions); // The current line attempt. // Invariant: Always fits into `stack.regions.current`. @@ -290,9 +289,9 @@ enum ParItem<'a> { /// Spacing between other items. Spacing(Length), /// A shaped text run with consistent direction. - Text(ShapedText<'a>, Align, usize), + Text(ShapedText<'a>, Align, &'a [Decoration]), /// A layouted child node. - Frame(Rc, Align, usize), + Frame(Rc, Align, &'a [Decoration]), } impl ParItem<'_> { @@ -313,113 +312,6 @@ impl ParItem<'_> { Self::Frame(frame, ..) => frame.baseline, } } - - /// The index of the `ParChild` that this item belongs to. - pub fn index(&self) -> Option { - match *self { - Self::Spacing(_) => None, - Self::Text(.., index) => Some(index), - Self::Frame(.., index) => Some(index), - } - } -} - -/// Stacks lines on top of each other. -struct LineStack<'a> { - line_spacing: Length, - children: &'a [ParChild], - full: Size, - regions: Regions, - size: Size, - lines: Vec>, - finished: Vec>>, - constraints: Constraints, - overflowing: bool, -} - -impl<'a> LineStack<'a> { - /// Create an empty line stack. - fn new(line_spacing: Length, children: &'a [ParChild], regions: Regions) -> Self { - Self { - line_spacing, - children, - full: regions.current, - constraints: Constraints::new(regions.expand), - regions, - size: Size::zero(), - lines: vec![], - finished: vec![], - overflowing: false, - } - } - - /// Push a new line into the stack. - fn push(&mut self, line: LineLayout<'a>) { - self.regions.current.h -= line.size.h + self.line_spacing; - - self.size.w.set_max(line.size.w); - self.size.h += line.size.h; - if !self.lines.is_empty() { - self.size.h += self.line_spacing; - } - - self.lines.push(line); - } - - /// Finish the frame for one region. - fn finish_region(&mut self, ctx: &LayoutContext) { - if self.regions.expand.x { - self.size.w = self.regions.current.w; - self.constraints.exact.x = Some(self.regions.current.w); - } - - if self.overflowing { - self.constraints.min.y = None; - self.constraints.max.y = None; - self.constraints.exact = self.full.to_spec().map(Some); - } - - let mut output = Frame::new(self.size, self.size.h); - let mut offset = Length::zero(); - let mut first = true; - - for line in self.lines.drain(..) { - let frame = line.build(self.size.w); - - let pos = Point::new(Length::zero(), offset); - if first { - output.baseline = pos.y + frame.baseline; - first = false; - } - - offset += frame.size.h + self.line_spacing; - output.merge_frame(pos, frame); - } - - for (_, child) in &mut output.children { - if let FrameChild::Frame(Some(frame_idx), frame) = child { - for deco in match &self.children[*frame_idx] { - ParChild::Spacing(_) => continue, - ParChild::Text(.., decos) => decos, - ParChild::Any(.., decos) => decos, - } { - deco.apply(ctx, Rc::make_mut(frame)); - } - } - } - - self.finished.push(output.constrain(self.constraints)); - self.regions.next(); - self.full = self.regions.current; - self.constraints = Constraints::new(self.regions.expand); - self.size = Size::zero(); - } - - /// Finish the last region and return the built frames. - fn finish(mut self, ctx: &LayoutContext) -> Vec>> { - self.finish_region(ctx); - self.finished - } } /// A lightweight representation of a line that spans a specific range in a @@ -537,7 +429,7 @@ impl<'a> LineLayout<'a> { } /// Build the line's frame. - fn build(&self, width: Length) -> Frame { + fn build(&self, ctx: &LayoutContext, width: Length) -> Frame { let size = Size::new(self.size.w.max(width), self.size.h); let free = size.w - self.size.w; @@ -545,33 +437,36 @@ impl<'a> LineLayout<'a> { let mut offset = Length::zero(); let mut ruler = Align::Start; - self.reordered(|item| { - let frame = match *item { - ParItem::Spacing(amount) => { - offset += amount; - return; - } - ParItem::Text(ref shaped, align, _) => { - ruler = ruler.max(align); - Rc::new(shaped.build()) - } - ParItem::Frame(ref frame, align, _) => { - ruler = ruler.max(align); - frame.clone() - } + self.reordered(ctx, |ctx, item| { + let mut position = |frame: &Frame, align| { + // FIXME: Ruler alignment for RTL. + ruler = ruler.max(align); + let x = ruler.resolve(self.dir, offset .. free + offset); + let y = self.baseline - frame.baseline; + offset += frame.size.w; + Point::new(x, y) }; - // FIXME: Ruler alignment for RTL. - let pos = Point::new( - ruler.resolve(self.dir, offset .. free + offset), - self.baseline - frame.baseline, - ); - - offset += frame.size.w; - - match item.index() { - Some(idx) => output.push_indexed_frame(pos, idx, frame), - None => output.push_frame(pos, frame), + match *item { + ParItem::Spacing(amount) => { + offset += amount; + } + ParItem::Text(ref shaped, align, decos) => { + let mut frame = shaped.build(); + for deco in decos { + deco.apply(ctx, &mut frame); + } + let pos = position(&frame, align); + output.merge_frame(pos, frame); + } + ParItem::Frame(ref frame, align, decos) => { + let mut frame = frame.clone(); + for deco in decos { + deco.apply(ctx, Rc::make_mut(&mut frame)); + } + let pos = position(&frame, align); + output.push_frame(pos, frame); + } } }); @@ -579,7 +474,10 @@ impl<'a> LineLayout<'a> { } /// Iterate through the line's items in visual order. - fn reordered(&self, mut f: impl FnMut(&ParItem<'a>)) { + fn reordered(&self, ctx: &LayoutContext, mut f: F) + where + F: FnMut(&LayoutContext, &ParItem<'a>), + { // The bidi crate doesn't like empty lines. if self.line.is_empty() { return; @@ -606,11 +504,11 @@ impl<'a> LineLayout<'a> { // direction. if levels[run.start].is_ltr() { for item in range { - f(self.get(item).unwrap()); + f(ctx, self.get(item).unwrap()); } } else { for item in range.rev() { - f(self.get(item).unwrap()); + f(ctx, self.get(item).unwrap()); } } } @@ -627,6 +525,90 @@ impl<'a> LineLayout<'a> { } } +/// Stacks lines on top of each other. +struct LineStack<'a> { + line_spacing: Length, + full: Size, + regions: Regions, + size: Size, + lines: Vec>, + finished: Vec>>, + constraints: Constraints, + overflowing: bool, +} + +impl<'a> LineStack<'a> { + /// Create an empty line stack. + fn new(line_spacing: Length, regions: Regions) -> Self { + Self { + line_spacing, + full: regions.current, + constraints: Constraints::new(regions.expand), + regions, + size: Size::zero(), + lines: vec![], + finished: vec![], + overflowing: false, + } + } + + /// Push a new line into the stack. + fn push(&mut self, line: LineLayout<'a>) { + self.regions.current.h -= line.size.h + self.line_spacing; + + self.size.w.set_max(line.size.w); + self.size.h += line.size.h; + if !self.lines.is_empty() { + self.size.h += self.line_spacing; + } + + self.lines.push(line); + } + + /// Finish the frame for one region. + fn finish_region(&mut self, ctx: &LayoutContext) { + if self.regions.expand.x { + self.size.w = self.regions.current.w; + self.constraints.exact.x = Some(self.regions.current.w); + } + + if self.overflowing { + self.constraints.min.y = None; + self.constraints.max.y = None; + self.constraints.exact = self.full.to_spec().map(Some); + } + + let mut output = Frame::new(self.size, self.size.h); + let mut offset = Length::zero(); + let mut first = true; + + for line in self.lines.drain(..) { + let frame = line.build(ctx, self.size.w); + + let pos = Point::new(Length::zero(), offset); + if first { + output.baseline = pos.y + frame.baseline; + first = false; + } + + offset += frame.size.h + self.line_spacing; + output.merge_frame(pos, frame); + } + + self.finished.push(output.constrain(self.constraints)); + self.regions.next(); + self.full = self.regions.current; + self.constraints = Constraints::new(self.regions.expand); + self.size = Size::zero(); + } + + /// Finish the last region and return the built frames. + fn finish(mut self, ctx: &LayoutContext) -> Vec>> { + self.finish_region(ctx); + self.finished + } +} + /// A decoration for a paragraph child. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum Decoration {