Enable floats and other advanced layouts#421
Conversation
dd3a1d6 to
62d4418
Compare
e7a038b to
257754b
Compare
f6e5cef to
e55c5cb
Compare
486eb53 to
d8566d3
Compare
taj-p
left a comment
There was a problem hiding this comment.
Sorry for the delay on the review - this PR is actually way more complex than I expected. Kudos on being able to wrangle the complexity of line breaking to support this custom behaviour.
There was a problem hiding this comment.
Can you please update this documentation?
There was a problem hiding this comment.
I think this still needs a little bit of updating.
| if self.state.layout_max_advance >= f32::MAX { | ||
| self.layout.data.alignment_width = full_width; | ||
| for line in &mut self.lines.lines { | ||
| line.metrics.inline_max_coord = line.metrics.inline_min_coord + width; |
There was a problem hiding this comment.
For my information, why do we want to overwrite the inline_max_coord for a line with the maximum width + inline_min_coord? What utility does it provide?
There was a problem hiding this comment.
Hmm... I don't think this is correct anymore actually. The basic logic of "if a width constraint is infinite, then set it to a computed value" (+ we treat f32::MAX as infinite) makes sense. But I think it should be done on a line-by-line basis rather than using the layout_max_advance now.
The key thing this is trying to avoid is Parley performing alignment against an infinite width. That not only positions text completely incorrectly, but also causes performance issues.
There was a problem hiding this comment.
Ok, I've dug into this a bit. This handles the case where layout is called with a None or Infinite width constraint (and no line-specific constraints are set during the layout). In that case we'd like alignment to be relative to the width of the longest line.
It's a bit of an edge case (you probably shouldn't be calling layout with an infinite constraint), but I think this is a nice fallback behaviour for alignment in case you do.
34f30bf to
c85fecd
Compare
taj-p
left a comment
There was a problem hiding this comment.
This very much LGTM - left some minor comments. Just want to ensure I understand everything.
When you've addressed the comments, hit the "re-request review" button so it shows up in my review queue and I PROMISE I'll review it the next business day
| pub line_y_start: f64, | ||
| pub line_y_end: f64, |
There was a problem hiding this comment.
Do we need to specify somewhere that coordinates use physical x/y axes. For horizontal text, x corresponds to the inline direction and y to the block direction? Or is this captured elsewhere in Parley?
There was a problem hiding this comment.
Um... big question! Perhaps something we can revist when we actually support vertical text? (he says hopefully)
There was a problem hiding this comment.
SGTM!
he says hopefully
👀 From my understanding, I think this will be worked on in 2026
taj-p
left a comment
There was a problem hiding this comment.
Hey Nico! Just checking whether you saw the previous comments - if you disagree with them, that's fine. Just want to check that they weren't missed accidentally.
| struct BreakerState { | ||
| /// Reason that the line breaker has yielded control flow | ||
| pub enum YieldData { | ||
| /// Control flow was yielded because a line break was encountered |
There was a problem hiding this comment.
Did we not want to adopt any of the below suggestions?
|
@taj-p I had missed a few things! I think I've got everything now. |
taj-p
left a comment
There was a problem hiding this comment.
LGTM 🎉 !!!! Let's goo!! Nice work!! 🚀
Signed-off-by: Nico Burns <nico@nicoburns.com> Ignore out-of-flow boxes for the purpose of whitespace collapsing Rename coords to block Signed-off-by: Nico Burns <nico@nicoburns.com> Fixup tests (inline box kind) Allow the caller to set x/y/max_advance for each line Signed-off-by: Nico Burns <nico@nicoburns.com> Add advance to box yield Align each line to it's own max-width Don't include last line in layout's height if it's empty Fix Cluster::from_point to work with offset lines Signed-off-by: Nico Burns <nico@nicoburns.com> Remove alignment_width argument from tests Use inline min coord in more places Use layout_max_advance for rendering tests Accept new snapshots Signed-off-by: Nico Burns <nico@nicoburns.com> Fix max_advance of f32::MAX MaxLineHeight WIP Signed-off-by: Nico Burns <nico@nicoburns.com> Fix: only InFlow InlineBox's contribute size to the layout Make InlineBoxKind Copy Including InlineBoxKind in PositionedInlineBox Replace break_on_box with InlineBoxKind::CustomOutOfFlow Signed-off-by: Nico Burns <nico@nicoburns.com> Remove is_infinite check in alignment Signed-off-by: Nico Burns <nico@nicoburns.com> Implement yielding when height is exceeded Signed-off-by: Nico Burns <nico@nicoburns.com> Update content_widths_rtl test snapshot Signed-off-by: Nico Burns <nico@nicoburns.com> Fixup swash reference Fixup new tests Fixup vello_cpu_render example Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Signed-off-by: Nico Burns <nico@nicoburns.com>
Related PRs
Cluster::from_point_exactmethod for hit-testing spans #447container_widtha required alignment parameter #283Introduction
This PR is aimed at enabling Blitz to implement CSS floats on top of Parley. However, the changes made are not float-specific, and will also enable other advanced layouts such as those that have "exluded regions" or want to lay out text into a complex shape.
Parley's existing architecture
Layout::break_all_lines(max_advance)to perform line breaking, Parley does already have a lower-levelLayout::break_lines()function which returns aBreakLinesstruct, on which you can callBreakLines::next(). Each call toBreakLines::next()lays out a single line and then returns. This PR extends the functionality of this method so that in addition returning when a line-break occurs, it will also return when:max_line_heightis exceededLinestruct hasmin_coordandmax_coordfields.Changes Made
Per-line bounding box
max_advanceWhen using the
BreakLinesstruct directly, themax_advance(max width) can now be configured on a per-line basis. This makes it possible to create layouts with lines broken to different lengths. Additionally, it is valid to adjust themax_advancemidway through line-breaking a line (it is advised to ensure that the existing content of the line still fits, although nothing terrible will happen if you don't (the contents will just overflow)).Automatically determined (per-line) alignment width
Related to the per-line max-advances, the width used for alignment is now per-line and determined automatically and is equal to the
max_advancefor that line (at the time that the line is "committed"). This means that each line is aligned within it's own width bound, separately from the other liens. This is really the only way to sensibly align lines of differing widths, but making alignment width automatic had previously been discussed as being desirable anyway.Added
max_line_heightEach line now has a
max_line_height. If at any point the line exceeds this height, control flow will be yielded fromBreakLines::next(), allowing the user to find a new, larger space to lay out into. By default,max_line_heightis set tof32::MAXand thus has no effect.Customisable per-line x- and y-offset
Parley previously assumed that every line started at x=0 and that the start of the next line would be immediately below the previous line. Parley now allows an x and y offset to be manually configured for each line. This does not affect line breaking, but is used when reporting the positions of glyphs and for selection.
The x offset is in addition to and stored separately from the offset generated by alignment.
Added
InlineBoxKindenumA new
kind: InlineBoxKindfield has been added theInlineBoxstruct, where InlineBoxKind is defined as:InlineBoxKind::InFlowrepresents the existing kind of inline box that is is laid out in-flow with text like adisplay: inline-blockbox in CSS.InlineBoxKind::OutOfFlowis assigned a position during layout in exactly the same way as anInFlowbox. However it does not take up space or affect the position of other items (glyphs or boxes) in the layout. This corresponds to aposition: absolutebox in CSS. Blitz was previously representing this as a zero-sized box, but I have taken the opportunity here to represent it explicitly.InlineBoxKind::CustomOutOfFlowis the box kind for a floated box. Parley does not attempt to lay these out at all. When it encountered a box of this kind it yields control flow back to the caller who then responsible for positioning the box, adjusting the line's position/size, and then resuming layout (details below).Tasks