Hold a row's top edge during layout, so nothing is drawn out of place
The correction ran in a coroutine, so it landed a frame or more after the layout it was correcting: the wrong position was drawn once and then fixed, which reads as a flick and gets worse the faster the screen refreshes. That is a race with the display rather than a bug that can be tuned out, so the fix is not a shorter delay but a different phase. It now happens in the layout phase. `Modifier.holdTopEdge` learns the row's new height from the measurement that produced it and asks the list to shift by exactly that much, before anything is drawn. `requestScrollToItem` is the form that may be asked for during layout; `dispatchRawDelta` is not -- it calls forceRemeasure and dies with "performMeasureAndLayout called during measure layout", which cost one crash to establish. The arming flag and the per-row height are deliberately not snapshot state. Both are written from layout, where a snapshot write that composition reads would schedule another recomposition -- another frame, which is the thing being removed. This also drops the machinery the previous attempt needed: no waiting on a size change, no timeout, no marking the rows above to find one that could still report the move. A row measures itself, so a row that shrinks out of the viewport is no longer a special case. Verified with ui-trace sampling at ~1kHz, where a single bad frame would show as ten to twenty samples: expanding and collapsing from a heading are each one step from old position to held position with nothing in between, collapsing from the foot bar holds the four rows below it, a drag 120ms after a tap is left alone, and scrolling back stays put for six seconds. ktfmt, lint and 85 tests clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cab21442d6
commit
5eba6ec529
2 files changed
+173
-172
No files matched your search
@@ -628,14 +628,14 @@ dev-updater (Kotlin 2.4.x, CMP 1.11.x, JDK 21). Screens:
|
||||
a group's heading or the bar at its foot — is the end that stays put,
|
||||
and the row grows away from it. The transcript is laid out from the
|
||||
bottom, so a row's bottom edge is anchored for free and the top one
|
||||
has to be arranged. `toggleAnchored` waits on the row's *size*, which
|
||||
a scroll cannot change — a version that waited on the top edge instead
|
||||
mistook the reader's own drag for the row resizing and undid it — and
|
||||
then reads how far things moved off any row from the pressed one
|
||||
upwards, since a group taller than the screen disappears under the
|
||||
bottom edge as it shrinks and cannot report its own move (2026-08-30,
|
||||
asked for after groups opened upwards and sent their own heading off
|
||||
the top of the screen).
|
||||
has to be arranged. The correction lives in the *layout* phase
|
||||
(`Modifier.holdTopEdge`): the measurement that discovers the row's new
|
||||
height asks the list to shift by that much, via
|
||||
`requestScrollToItem`, before anything is drawn. Doing it from an
|
||||
effect instead means the wrong position is drawn once first, which
|
||||
reads as a flick and gets worse the faster the screen refreshes
|
||||
(2026-08-30, asked for after groups opened upwards and sent their own
|
||||
heading off the top of the screen).
|
||||
- Input bar: text, attach (camera/gallery/file), send — **always enabled**;
|
||||
mid-run sends become steering messages.
|
||||
- Top bar: model chip (tap to change), stop button while running, token
|
||||
|
||||
Reference in new issue
Block a user