Skip to content

Make TextArea measurement side-effect free#1824

Open
0k-dot-computer wants to merge 3 commits into
linebender:mainfrom
0k-dot-computer:main
Open

Make TextArea measurement side-effect free#1824
0k-dot-computer wants to merge 3 commits into
linebender:mainfrom
0k-dot-computer:main

Conversation

@0k-dot-computer

@0k-dot-computer 0k-dot-computer commented Jun 12, 2026

Copy link
Copy Markdown

Hi, thanks for all of the work on xilem and the linebender projects in general. I've been using it for some time and have accumulated quite a few patches which I'd like to start contributing upstream if useful to the project.

I originally came up with this one when auditing my own widget set for correct definition of measure and ended up removing the hack in measure of masonry's TextArea along the way (from #1560):

// TODO: Remove this hack
  and do efficient side-effect free measurement with no
  alignment

TextArea'smeasure worked by laying out PlainEditor with the new width, relayout, and then resetting the width, which means selection is re-resolved and text is shaped for a transient state. I demonstrated the issue with a new test that fails on main.

My approach for this attempt at a fix is to extract Label's TextLayoutCache into a shared module and to re-use it from TextArea. The approach requires manually invalidating the cache, but avoiding that seems to require changes to the parley public API, so I was hesitant to go there without some initial feedback.

I directed Claude Code to make the changes and went through a few rounds of feedback and review with it before opening this PR. I also used Claude to help me understand more about how masonry works both in general and in relation to this change.

Pure refactor: `TextLayout` and the caching logic introduced for `Label`
move verbatim to a new crate-private `widgets::text_layout_cache` module,
parameterized on the text and styles instead of reading `Label`'s fields.
This makes the cache usable by `TextArea` measurement in the next commit.

No behavior change; `Label`'s render snapshot tests are unchanged.
Previously `TextArea::measure` did its layout work through the live
`PlainEditor`, temporarily changing its width and then changing it back,
relayouting twice in the process. Beyond the wasted work, the restore
isn't faithful: every relayout nudges the editor's generation and
refreshes the selection against the transient layout, so a speculative
measure invalidates editor state that `measure` must leave untouched.

Instead, measure through a `TextLayoutCache` (the same caching that
`Label` uses) built from the editor's current text and styles, with no
alignment, which doesn't affect the measured size. The cache is cleared
whenever text, styles, or fonts change, mirroring `Label`. The editor is
no longer touched during measurement, resolving the layout-system TODO
about side-effect free measurement.

The first new test fails on the previous commit: a parent measuring its
child at min- and max-content nudged the editor generation on every
layout pass. The second test guards the cache invalidation: inserting
text must grow the measured max-content width.
…measure

Make `TextArea` measurement side-effect free. [fork-internal review]
@0k-dot-computer 0k-dot-computer marked this pull request as ready for review June 12, 2026 22:02
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