Skip to content

feat(text): native justification, min-content width on request, and textFeatures() - #16

Merged
sidorares merged 1 commit into
mainfrom
feat/justify
Oct 4, 2026
Merged

sidorares merged 1 commit into
mainfrom
feat/justify

Conversation

@sidorares

Copy link
Copy Markdown
Contributor

This is performance work for react-x11's <Html> on Windows, measured with a Zen Garden page whose window is dragged from 1180 px to a quarter of that and back.

  • justify: true on layoutCreate sets DWRITE_TEXT_ALIGNMENT_JUSTIFIED: every line but a paragraph's last fills the width at its word separators. Until now <Html> justified a paragraph by giving every space its own span and laying it out again with letter spacing, three layouts each time. On Zen Garden 001, justified paragraphs were 57% of a resize frame.
  • layoutMetrics(handle, { minWidth: false }) skips DetermineMinWidth, and layoutMinWidth(handle) answers it when someone actually asks. Only yoga's floors pass needs it, and a layout made to be drawn never does.
  • textFeatures() returns { justify: true, lazyMinWidth: true }, so react-x11 can ask instead of keying on a version, which also covers WINDOWKIT_WIN32_PATH builds.

With the react-x11 half (the core PR that follows), Zen Garden 001's resize frame goes from a 59.7 ms median to 15.3 ms, and its layout share from 53 ms to 9 ms.

test/text.js: textFeatures; a justified column's lines fill the width while its last line stays as laid out, and justifying moves no break; minWidth is left out when asked and layoutMinWidth gives the same number. npm run test:unit and test:window pass on Windows 11.

🤖 Generated with Claude Code

… does, `layoutMetrics(handle, { minWidth: false })` leaves the min-content width to `layoutMinWidth`, and `textFeatures()` says what a layout takes — where a caller justified each paragraph by spacing its words itself, in three layouts, and every layout ran the line breaker a second time for a width nobody asked for

Co-Authored-By: Claude Opus 5.5 <[email protected]>
sidorares added a commit to sidorares/react-x11 that referenced this pull request Oct 4, 2026
…ped one laid out once and only as far as its cap, and a layout's min-content width asked for when it is read — Zen Garden 001's resize frames from 59.7ms to 15.3ms at the median, 005's from 99.5ms to 58.6ms

- `justifies` is `'rest'` where the bridge says it justifies
  (`textFeatures`, windowkit/win32#16): DirectWrite sets every line but a
  paragraph's last to fill its width, which is ntk's `justify: true`, and
  the engine passes that mode on. <Html> justified each paragraph on this
  engine by spacing its words itself, three layouts each.
- A capped paragraph is laid out as a prefix long enough to reach past its
  cap (`_prefixLaidOut`), and its first lines kept and drawn clipped to
  them (`_keep`), where it was laid out whole and then again cut. <Html>
  lays a paragraph beside a float out a line at a time, each line
  `maxLines: 1` over the rest of the paragraph, so a paragraph cost the
  square of its length.
- `layoutMetrics` is asked to leave out the min-content width, which the
  `minWidth` getter asks for the first time it is read.

Measured dragging each window from 1180px to a quarter and back in 8px
steps, on Windows 11.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@sidorares
sidorares merged commit 686defa into main Oct 4, 2026
7 checks passed
sidorares added a commit to sidorares/react-x11 that referenced this pull request Oct 4, 2026
…ped one laid out once and only as far as its cap, and a layout's min-content width asked for when it is read — Zen Garden 001's resize frames from 59.7ms to 15.3ms at the median, 005's from 99.5ms to 58.6ms (#878)

- `justifies` is `'rest'` where the bridge says it justifies
  (`textFeatures`, windowkit/win32#16): DirectWrite sets every line but a
  paragraph's last to fill its width, which is ntk's `justify: true`, and
  the engine passes that mode on. <Html> justified each paragraph on this
  engine by spacing its words itself, three layouts each.
- A capped paragraph is laid out as a prefix long enough to reach past its
  cap (`_prefixLaidOut`), and its first lines kept and drawn clipped to
  them (`_keep`), where it was laid out whole and then again cut. <Html>
  lays a paragraph beside a float out a line at a time, each line
  `maxLines: 1` over the rest of the paragraph, so a paragraph cost the
  square of its length.
- `layoutMetrics` is asked to leave out the min-content width, which the
  `minWidth` getter asks for the first time it is read.

Measured dragging each window from 1180px to a quarter and back in 8px
steps, on Windows 11.

Co-authored-by: Claude Opus 5.5 <[email protected]>
sidorares added a commit to sidorares/react-x11 that referenced this pull request Oct 4, 2026
…ce names, font widths, letter spacing, OpenType features, x-heights and DirectWrite's justification (#879)

The range admitted 0.0.7 alone (a caret on 0.0.x is exact), so what #874
asks the bridge for was answered by none: no link showed the hand on
Windows, Arial Black and Segoe UI Light fell back to the default face, a
`letter-spacing` was set tight and an `ex` had no face to be measured in.
0.0.8 (windowkit/win32#14) and 0.0.9 (windowkit/win32#16, the
justification and the min-content width on request #878 switches on).

Co-authored-by: Claude Opus 5.5 <[email protected]>
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.

1 participant