Skip to content

Downgrade inline box break priority to emergency#550

Closed
devunt wants to merge 1 commit intolinebender:mainfrom
devunt:fix/inline-box-break-priority
Closed

Downgrade inline box break priority to emergency#550
devunt wants to merge 1 commit intolinebender:mainfrom
devunt:fix/inline-box-break-priority

Conversation

@devunt
Copy link

@devunt devunt commented Feb 13, 2026

Summary

When an InlineBox is followed by text that uses OverflowWrap::Anywhere or WordBreak::BreakAll, the inline box is unconditionally separated onto its own line instead of remaining on the same line as the text. This PR fixes the issue by downgrading the break opportunity after inline boxes from regular to emergency priority.

Problem

In break_next(), after an InlineBox is appended to the current line, mark_line_break_opportunity() is called unconditionally (line 258). This stores the position as a regular (prev_boundary) break opportunity.

When the following text uses BreakAll/Anywhere, character-level breaks are marked via mark_emergency_break_opportunity(), which stores them as emergency (emergency_boundary) breaks.

When overflow occurs, parley's priority order is:

  1. Space break (highest)
  2. Regular break (prev_boundary)
  3. Emergency break (emergency_boundary)
  4. No break / overflow (lowest)

Because regular always wins over emergency regardless of proximity to the overflow point, the inline box boundary is chosen over closer character-level breaks. This causes the inline box to be placed on its own line, and the text starts fresh on the next line — with full max_advance width available, as if the inline box didn't exist.

Concrete example

A common use case is paragraph indentation via a zero-height InlineBox:

builder.push_inline_box(InlineBox { id: 0, index: 0, width: 32.0, height: 0.0 });
// Followed by continuous text with BreakAll, e.g. "aaaa..." or CJK characters

Expected: [InlineBox][aaaaaaa...] on the first line, text wraps between characters at overflow.

Actual: [InlineBox] alone on line 1 (invisible, zero-height), [aaaaaaa...] starts on line 2 with the full line width — the indent has no visible effect.

Fix

Change mark_line_break_opportunity()mark_emergency_break_opportunity() after inline boxes (1-line change).

This makes the inline box break the same priority as BreakAll/Anywhere character breaks. Since emergency_boundary stores only the latest emergency break seen, character-level breaks closer to the overflow point naturally win.

Behavior change analysis

Since prev_boundary and emergency_boundary are both Option<PrevBoundaryState> (storing only the latest position), the behavior difference depends on what other break opportunities exist:

Scenario Before After Change?
[InlineBox][continuous-text] + BreakAll Break after InlineBox (regular wins) → box on separate line Break between characters (closer emergency wins) Fixed
[InlineBox][continuous-text] + normal mode Break after InlineBox (only regular break) Break after InlineBox (emergency fallback) No
[InlineBox][word1 word2 ...] overflow in later word Break at space (later regular overwrites InlineBox's) Break at space (regular from space, InlineBox's emergency overwritten by later emergency or unused) No
[InlineBox][word1] where InlineBox+word1 overflows Break after InlineBox (only regular) Break after InlineBox (emergency fallback) No
text [InlineBox] longword overflow in longword Break after InlineBox (regular, overwrote earlier space) Break at space (regular preserved, InlineBox is emergency) Minor improvement — space is a more natural break point

In all non-BreakAll/Anywhere scenarios, behavior is effectively unchanged because:

  • If other regular breaks exist (e.g., spaces), they overwrite prev_boundary after the InlineBox anyway, so the InlineBox break was already irrelevant.
  • If no other breaks exist, the InlineBox break still works as an emergency fallback (emergency is used before accepting overflow).

No API changes

  • InlineBox struct: unchanged
  • Public API surface: unchanged
  • Only internal break priority logic is adjusted

Change the line break opportunity marked after inline boxes from
regular priority to emergency priority. This prevents inline boxes
from being unconditionally separated onto their own line when
followed by text using OverflowWrap::Anywhere or WordBreak::BreakAll.

Previously, the regular-priority break after an inline box always won
over emergency-priority character-level breaks (from BreakAll/Anywhere),
regardless of proximity to the overflow point. This caused the inline
box to be placed on its own line with the text starting on the next
line — effectively losing the inline box's intended effect on the
first line's layout (e.g., paragraph indentation via zero-height
inline box).

By marking the break as emergency priority instead, character-level
breaks that are closer to the overflow point are now preferred. In
scenarios without BreakAll/Anywhere, the behavior is preserved because
emergency breaks serve as the same last-resort fallback.
@nicoburns
Copy link
Collaborator

Hmm... I can see the issue here. But I don't think this is the right fix. Seems to me that when WordBreak is set to BreakAll then it should be generating regular line break opportunities, not emergency ones.

I would add that creating a text indent is not an intended use case for inline boxes, and we really ought to implement a dedicated text-indent property instead.

@devunt
Copy link
Author

devunt commented Feb 14, 2026

I see. Let me discuss this further in #545. I'll close this for now. See you there!

@devunt devunt closed this Feb 14, 2026
@devunt devunt deleted the fix/inline-box-break-priority branch February 14, 2026 10:25
@devunt devunt mentioned this pull request Feb 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants