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

@ -58,6 +58,15 @@ pub fn define(global: &mut Scope) {
pub struct MetaElem {
/// Metadata that should be attached to all elements affected by this style
/// 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]
pub data: SmallVec<[Meta; 1]>,
}

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

@ -71,14 +71,7 @@ impl Layout for Packed<FlowElem> {
let layoutable = child.with::<dyn Layout>().unwrap();
layouter.layout_single(engine, layoutable, styles)?;
} else if child.is::<MetaElem>() {
let mut frame = Frame::soft(Size::zero());
frame.meta(styles, true);
layouter.items.push(FlowItem::Frame {
frame,
align: Axes::splat(FixedAlignment::Start),
sticky: true,
movable: false,
});
layouter.layout_meta(styles);
} else if let Some(placed) = child.to_packed::<PlaceElem>() {
layouter.layout_placed(engine, placed, styles)?;
} else if child.can::<dyn Layout>() {
@ -297,7 +290,8 @@ impl<'a> FlowLayouter<'a> {
let align = AlignElem::alignment_in(styles).resolve(styles);
let sticky = BlockElem::sticky_in(styles);
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(
engine,
FlowItem::Frame { frame, align, sticky, movable: true },
@ -306,6 +300,18 @@ impl<'a> FlowLayouter<'a> {
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.
fn layout_placed(
&mut self,
@ -321,7 +327,8 @@ impl<'a> FlowLayouter<'a> {
align.x().unwrap_or_default().resolve(styles)
});
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 };
self.layout_item(engine, item)
}
@ -361,7 +368,7 @@ impl<'a> FlowLayouter<'a> {
let sticky = BlockElem::sticky_in(styles);
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.
if self.root {
find_footnotes(&mut notes, &frame);
@ -371,8 +378,11 @@ impl<'a> FlowLayouter<'a> {
self.finish_region(engine, false)?;
}
let item = FlowItem::Frame { frame, align, sticky, movable: false };
self.layout_item(engine, item)?;
frame.meta(styles, false);
self.layout_item(
engine,
FlowItem::Frame { frame, align, sticky, movable: false },
)?;
}
self.try_handle_footnotes(engine, notes)?;

@ -289,20 +289,32 @@ impl Frame {
/// Attach metadata from an iterator.
pub fn meta_iter(&mut self, iter: impl IntoIterator<Item = Meta>) {
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) {
hide = true;
None
} else {
self.prepend(Point::zero(), FrameItem::Meta(meta, self.size));
Some((Point::zero(), FrameItem::Meta(meta, size)))
}
}
}));
if hide {
Arc::make_mut(&mut self.items).retain(|(_, item)| {
matches!(item, FrameItem::Group(_) | FrameItem::Meta(Meta::Elem(_), _))
});
self.hide();
}
}
/// 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.
pub fn fill(&mut self, fill: Paint) {
self.prepend(

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

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

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

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

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

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

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

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