Skip to content

Keep layout from looping and the scroll offset from leaving its range - #123

Merged
MegatronKing merged 1 commit into
reqable:mainfrom
koldoon:fix/scroll-layout-loop
Aug 21, 2026
Merged

Keep layout from looping and the scroll offset from leaving its range#123
MegatronKing merged 1 commit into
reqable:mainfrom
koldoon:fix/scroll-layout-loop

Conversation

@koldoon

@koldoon koldoon commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What this fixes

_updateDisplayRenderParagraphs calls itself when layout has corrected the
scroll offset, and that recursion has no limit. It does not always converge:
applyContentDimensions starts a ballistic activity while the offset is out of
range, the activity moves the offset, and the next cycle sees the same mismatch.

Two failures our users hit, both taken from a running process rather than
guessed:

  • The application froze hard after a few PageUp/PageDown presses. The
    stack pulled from the VM service was thousands of frames of this one method.
  • The editor shuddered at the top of the file. The offset sat a fraction of
    a point above zero (we logged −0.7), layout kept asking for layout, and the
    ballistic activity that would have returned the offset to zero was restarted
    by every frame and never got to run.

The change

  • The number of layout cycles is bounded, the way RenderViewport bounds itself
    with _maxLayoutCycles. The estimate behind the recursion cannot be made
    exact — lines outside the viewport are counted as one grid line each, while a
    wrapped line takes more — so it needs a bound rather than a better guess.
    Stopping early costs at most one imprecise frame.
  • The rebuild is skipped when there is nothing above to build: if line 0 is
    already the first one shown, rebuilding cannot close a gap at the top.
  • makePositionVisible no longer leaves the scrollable range, and the distance
    to a line below the viewport is counted from the last displayed line rather
    than the first — counting from first overshot by a whole screen. Landing out
    of range is what hands the offset to the spring simulation in the first place.

Found while building a file manager on re_editor.


Related: #122 — that one removes the source of the accumulating correction. This one keeps layout terminating whatever the estimate, since with word wrap the estimate cannot be exact.

Two reports from our users, one root: `_updateDisplayRenderParagraphs` calls
itself when layout has corrected the scroll offset, and that recursion had no
limit.

It does not always converge. `applyContentDimensions` starts a ballistic
activity while the offset is out of range, the activity moves the offset, and
the next cycle sees the same mismatch. Paging through a file froze the
application hard; the stack taken from the running process was thousands of
frames of this one method.

The estimate behind it cannot be made exact: lines outside the viewport are
counted as one grid line each, while a wrapped line takes more. So the number
of cycles is bounded, the way `RenderViewport` bounds itself with
`_maxLayoutCycles`. Stopping early costs at most one imprecise frame.

The rebuild is also skipped when there is nothing above to build. A gap at the
top appears when the offset is merely out of range — a hair above the first
line — and rebuilding cannot close it if line 0 is already the first one
shown. Layout kept asking for layout, the ballistic activity that would have
returned the offset to zero was restarted by every frame, and the editor
shuddered at the top of the file.

Finally, `makePositionVisible` no longer leaves the scrollable range, and the
distance to a line below the viewport is counted from the last displayed line
rather than the first — counting from `first` overshot by a whole screen.
Landing out of range is what hands the offset to the spring simulation in the
first place.
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