Show block-level elements as blocks #4310
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR reworks how block-level elements are represented and laid out:
block.{above, below}
, but other than that, they weren't blocks per seShow
and show themselves asBlockElem
sLayoutRoot
,LayoutMultiple
, andLayoutSingle
.BlockElem
can be a closure which is given access to the regions (just likelayout(size => ..)
in Typst).This change has the following benefits:
First of all, things like
show list: set block(..)
now make more sense conceptually, since these blocks are really physically present. Due to this, the internal handling of theabove
andbelow
properties is much more natural now. The exception for this are paragraphs, which are still not blocks with this PR. They are merely a collection of inline-level items and, in a future PR, text and other inline elements will likely be able to exist without a wrapping paragraph, directly as a flow child. This makes it sensible for them not be wrapped in a block. Rather, the paragraph would apply paragraph-specific things like first-line-indent and then show itself as its children, which would then be collected directly into a flow. Since, in contrast to e.g. headings, paragraphs also almost never need differentabove
andbelow
values, I am considering to introduceset par(spacing: ..)
specifically for paragraphs, which would also be more beginner-friendly than the current show-set rule. The show-set rule would only be needed to override the block spacing (which would inheritpar.spacing
) for specific block-level elements.While the layout traits were static, in the new setup, an element can show itself differently under different circumstances:
All block-like elements can now have block properties, which can be uniformly handled by the layout engine. For instance, a
rect
shows itself as ablock
and forwards its width and height to theblock
. This means that we can quite easily add support forheight: 1fr
on blocks, shapes, and images in a future PR. Previously, the opaque layout traits simply had no mechanism to communicate the fractional ratios between the flow and its children.This change brings the internals closer to the way Typst already works for users: A show rule that returns
layout(size => ..)
is the user-space equivalent to the new block layouter model. In the future, we could consider moving this functionality from a speciallayout
function directly toblock
. (In this PR,layout
simply defers toblock
internally.)A note on the implementation: We can't directly use normal capturing Rust closures for the callbacks because they cannot be hashed, which is a requirement for being a field of
BlockElem
. For this reason,src/layout/container.rs
now contains a small manual implementation of closure types that capturePacked<T>
for the relevant signatures.