Handle metadata application where styles are managed

This commit is contained in:
Laurenz 2024-01-17 20:53:09 +01:00
parent fe56fb29fa
commit 50741209a8
12 changed files with 59 additions and 46 deletions

View File

@ -58,6 +58,15 @@ pub fn define(global: &mut Scope) {
pub struct MetaElem { pub struct MetaElem {
/// Metadata that should be attached to all elements affected by this style /// Metadata that should be attached to all elements affected by this style
/// property. /// property.
///
/// This must be accessed and applied to all frames produced by elements
/// that manually handle styles (because their children can have varying
/// styles). This currently includes flow, par, and equation.
///
/// Other elements don't manually need to handle it because their parents
/// that result from realization will take care of it and the metadata can
/// only apply to them as a whole, not part of it (because they don't manage
/// styles).
#[fold] #[fold]
pub data: SmallVec<[Meta; 1]>, pub data: SmallVec<[Meta; 1]>,
} }

View File

@ -172,7 +172,6 @@ impl Layout for Packed<BoxElem> {
} }
// Apply metadata. // Apply metadata.
frame.meta(styles, false);
frame.set_kind(FrameKind::Hard); frame.set_kind(FrameKind::Hard);
Ok(Fragment::frame(frame)) Ok(Fragment::frame(frame))
@ -454,7 +453,6 @@ impl Layout for Packed<BlockElem> {
// Apply metadata. // Apply metadata.
for frame in &mut frames { for frame in &mut frames {
frame.set_kind(FrameKind::Hard); frame.set_kind(FrameKind::Hard);
frame.meta(styles, false);
} }
Ok(Fragment::frames(frames)) Ok(Fragment::frames(frames))

View File

@ -71,14 +71,7 @@ impl Layout for Packed<FlowElem> {
let layoutable = child.with::<dyn Layout>().unwrap(); let layoutable = child.with::<dyn Layout>().unwrap();
layouter.layout_single(engine, layoutable, styles)?; layouter.layout_single(engine, layoutable, styles)?;
} else if child.is::<MetaElem>() { } else if child.is::<MetaElem>() {
let mut frame = Frame::soft(Size::zero()); layouter.layout_meta(styles);
frame.meta(styles, true);
layouter.items.push(FlowItem::Frame {
frame,
align: Axes::splat(FixedAlignment::Start),
sticky: true,
movable: false,
});
} else if let Some(placed) = child.to_packed::<PlaceElem>() { } else if let Some(placed) = child.to_packed::<PlaceElem>() {
layouter.layout_placed(engine, placed, styles)?; layouter.layout_placed(engine, placed, styles)?;
} else if child.can::<dyn Layout>() { } else if child.can::<dyn Layout>() {
@ -297,7 +290,8 @@ impl<'a> FlowLayouter<'a> {
let align = AlignElem::alignment_in(styles).resolve(styles); let align = AlignElem::alignment_in(styles).resolve(styles);
let sticky = BlockElem::sticky_in(styles); let sticky = BlockElem::sticky_in(styles);
let pod = Regions::one(self.regions.base(), Axes::splat(false)); let pod = Regions::one(self.regions.base(), Axes::splat(false));
let frame = content.layout(engine, styles, pod)?.into_frame(); let mut frame = content.layout(engine, styles, pod)?.into_frame();
frame.meta(styles, false);
self.layout_item( self.layout_item(
engine, engine,
FlowItem::Frame { frame, align, sticky, movable: true }, FlowItem::Frame { frame, align, sticky, movable: true },
@ -306,6 +300,18 @@ impl<'a> FlowLayouter<'a> {
Ok(()) Ok(())
} }
/// Place explicit metadata into the flow.
fn layout_meta(&mut self, styles: StyleChain) {
let mut frame = Frame::soft(Size::zero());
frame.meta(styles, true);
self.items.push(FlowItem::Frame {
frame,
align: Axes::splat(FixedAlignment::Start),
sticky: true,
movable: false,
});
}
/// Layout a placed element. /// Layout a placed element.
fn layout_placed( fn layout_placed(
&mut self, &mut self,
@ -321,7 +327,8 @@ impl<'a> FlowLayouter<'a> {
align.x().unwrap_or_default().resolve(styles) align.x().unwrap_or_default().resolve(styles)
}); });
let y_align = alignment.map(|align| align.y().map(VAlignment::fix)); let y_align = alignment.map(|align| align.y().map(VAlignment::fix));
let frame = placed.layout(engine, styles, self.regions)?.into_frame(); let mut frame = placed.layout(engine, styles, self.regions)?.into_frame();
frame.meta(styles, false);
let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance }; let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance };
self.layout_item(engine, item) self.layout_item(engine, item)
} }
@ -361,7 +368,7 @@ impl<'a> FlowLayouter<'a> {
let sticky = BlockElem::sticky_in(styles); let sticky = BlockElem::sticky_in(styles);
let fragment = block.layout(engine, styles, self.regions)?; let fragment = block.layout(engine, styles, self.regions)?;
for (i, frame) in fragment.into_iter().enumerate() { for (i, mut frame) in fragment.into_iter().enumerate() {
// Find footnotes in the frame. // Find footnotes in the frame.
if self.root { if self.root {
find_footnotes(&mut notes, &frame); find_footnotes(&mut notes, &frame);
@ -371,8 +378,11 @@ impl<'a> FlowLayouter<'a> {
self.finish_region(engine, false)?; self.finish_region(engine, false)?;
} }
let item = FlowItem::Frame { frame, align, sticky, movable: false }; frame.meta(styles, false);
self.layout_item(engine, item)?; self.layout_item(
engine,
FlowItem::Frame { frame, align, sticky, movable: false },
)?;
} }
self.try_handle_footnotes(engine, notes)?; self.try_handle_footnotes(engine, notes)?;

View File

@ -289,20 +289,32 @@ impl Frame {
/// Attach metadata from an iterator. /// Attach metadata from an iterator.
pub fn meta_iter(&mut self, iter: impl IntoIterator<Item = Meta>) { pub fn meta_iter(&mut self, iter: impl IntoIterator<Item = Meta>) {
let mut hide = false; let mut hide = false;
for meta in iter { let size = self.size;
self.prepend_multiple(iter.into_iter().filter_map(|meta| {
if matches!(meta, Meta::Hide) { if matches!(meta, Meta::Hide) {
hide = true; hide = true;
None
} else { } else {
self.prepend(Point::zero(), FrameItem::Meta(meta, self.size)); Some((Point::zero(), FrameItem::Meta(meta, size)))
}
} }
}));
if hide { if hide {
Arc::make_mut(&mut self.items).retain(|(_, item)| { self.hide();
matches!(item, FrameItem::Group(_) | FrameItem::Meta(Meta::Elem(_), _))
});
} }
} }
/// Hide all content in the frame, but keep metadata.
pub fn hide(&mut self) {
Arc::make_mut(&mut self.items).retain_mut(|(_, item)| match item {
FrameItem::Group(group) => {
group.frame.hide();
!group.frame.is_empty()
}
FrameItem::Meta(Meta::Elem(_), _) => true,
_ => false,
});
}
/// Add a background fill. /// Add a background fill.
pub fn fill(&mut self, fill: Paint) { pub fn fill(&mut self, fill: Paint) {
self.prepend( self.prepend(

View File

@ -570,13 +570,8 @@ impl<'a> GridLayouter<'a> {
} }
self.finish_region(engine)?; self.finish_region(engine)?;
self.render_fills_strokes()?; self.render_fills_strokes()?;
for frame in &mut self.finished {
frame.meta(self.styles, false);
}
Ok(Fragment::frames(self.finished)) Ok(Fragment::frames(self.finished))
} }

View File

@ -501,7 +501,11 @@ fn collect<'a>(
Segment::Text(full.len() - prev) Segment::Text(full.len() - prev)
} else if let Some(elem) = child.to_packed::<EquationElem>() { } else if let Some(elem) = child.to_packed::<EquationElem>() {
let pod = Regions::one(region, Axes::splat(false)); let pod = Regions::one(region, Axes::splat(false));
let items = elem.layout_inline(engine, styles, pod)?; let mut items = elem.layout_inline(engine, styles, pod)?;
for item in &mut items {
let MathParItem::Frame(frame) = item else { continue };
frame.meta(styles, false);
}
full.extend(items.iter().map(MathParItem::text)); full.extend(items.iter().map(MathParItem::text));
Segment::Equation(elem, items) Segment::Equation(elem, items)
} else if let Some(elem) = child.to_packed::<BoxElem>() { } else if let Some(elem) = child.to_packed::<BoxElem>() {
@ -591,6 +595,7 @@ fn prepare<'a>(
} else { } else {
let pod = Regions::one(region, Axes::splat(false)); let pod = Regions::one(region, Axes::splat(false));
let mut frame = elem.layout(engine, styles, pod)?.into_frame(); let mut frame = elem.layout(engine, styles, pod)?.into_frame();
frame.meta(styles, false);
frame.translate(Point::with_y(TextElem::baseline_in(styles))); frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame)); items.push(Item::Frame(frame));
} }
@ -1315,6 +1320,7 @@ fn commit(
let region = Size::new(amount, full); let region = Size::new(amount, full);
let pod = Regions::one(region, Axes::new(true, false)); let pod = Regions::one(region, Axes::new(true, false));
let mut frame = elem.layout(engine, *styles, pod)?.into_frame(); let mut frame = elem.layout(engine, *styles, pod)?.into_frame();
frame.meta(*styles, false);
frame.translate(Point::with_y(TextElem::baseline_in(*styles))); frame.translate(Point::with_y(TextElem::baseline_in(*styles)));
push(&mut offset, frame); push(&mut offset, frame);
} else { } else {
@ -1322,8 +1328,9 @@ fn commit(
} }
} }
Item::Text(shaped) => { Item::Text(shaped) => {
let frame = let mut frame =
shaped.build(engine, justification_ratio, extra_justification); shaped.build(engine, justification_ratio, extra_justification);
frame.meta(shaped.styles, false);
push(&mut offset, frame); push(&mut offset, frame);
} }
Item::Frame(frame) | Item::Meta(frame) => { Item::Frame(frame) | Item::Meta(frame) => {

View File

@ -322,9 +322,6 @@ impl<'a> ShapedText<'a> {
offset += width; offset += width;
} }
// Apply metadata.
frame.meta(self.styles, false);
frame frame
} }

View File

@ -188,9 +188,6 @@ impl Packed<EquationElem> {
let descent = bottom_edge.max(frame.descent() - slack); let descent = bottom_edge.max(frame.descent() - slack);
frame.translate(Point::with_y(ascent - frame.baseline())); frame.translate(Point::with_y(ascent - frame.baseline()));
frame.size_mut().y = ascent + descent; frame.size_mut().y = ascent + descent;
// Apply metadata.
frame.meta(styles, false);
} }
Ok(items) Ok(items)
@ -251,9 +248,6 @@ impl Layout for Packed<EquationElem> {
frame.push_frame(Point::new(x, y), counter) frame.push_frame(Point::new(x, y), counter)
} }
// Apply metadata.
frame.meta(styles, false);
Ok(Fragment::frame(frame)) Ok(Fragment::frame(frame))
} }
} }

View File

@ -236,13 +236,9 @@ impl Layout for Packed<ImageElem> {
// Create a clipping group if only part of the image should be visible. // Create a clipping group if only part of the image should be visible.
if fit == ImageFit::Cover && !target.fits(fitted) { if fit == ImageFit::Cover && !target.fits(fitted) {
frame.meta(styles, false);
frame.clip(Path::rect(frame.size())); frame.clip(Path::rect(frame.size()));
} }
// Apply metadata.
frame.meta(styles, false);
Ok(Fragment::frame(frame)) Ok(Fragment::frame(frame))
} }
} }

View File

@ -89,7 +89,6 @@ impl Layout for Packed<LineElem> {
let mut frame = Frame::soft(target); let mut frame = Frame::soft(target);
let shape = Geometry::Line(delta.to_point()).stroked(stroke); let shape = Geometry::Line(delta.to_point()).stroked(stroke);
frame.push(start.to_point(), FrameItem::Shape(shape, self.span())); frame.push(start.to_point(), FrameItem::Shape(shape, self.span()));
frame.meta(styles, false);
Ok(Fragment::frame(frame)) Ok(Fragment::frame(frame))
} }
} }

View File

@ -171,7 +171,6 @@ impl Layout for Packed<PolygonElem> {
let shape = Shape { geometry: Geometry::Path(path), stroke, fill }; let shape = Shape { geometry: Geometry::Path(path), stroke, fill };
frame.push(Point::zero(), FrameItem::Shape(shape, self.span())); frame.push(Point::zero(), FrameItem::Shape(shape, self.span()));
frame.meta(styles, false);
Ok(Fragment::frame(frame)) Ok(Fragment::frame(frame))
} }

View File

@ -523,9 +523,6 @@ fn layout(
} }
} }
// Apply metadata.
frame.meta(styles, false);
Ok(Fragment::frame(frame)) Ok(Fragment::frame(frame))
} }