Make TextArea measurement side-effect free#1824
Open
0k-dot-computer wants to merge 3 commits into
Open
Conversation
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]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Hi, thanks for all of the work on
xilemand 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
measureand ended up removing the hack inmeasureof masonry'sTextAreaalong the way (from #1560):TextArea'smeasureworked by laying outPlainEditorwith 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 onmain.My approach for this attempt at a fix is to extract
Label'sTextLayoutCacheinto a shared module and to re-use it fromTextArea. 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.