Land a closed card centred on the tap that closed it
Two things were wrong with the hold, and each hid the other. It held a *share* of the row's height: the point the finger was on stayed, in proportion, which is the same miss in miniature as holding an edge. Tap away from the middle of a long card and the heading landed most of a card's height from the finger, and off it. A shut card is a heading, and the only place it belongs is centred under the hand that shut it, wherever down the card the tap was. And the correction was worked out from the change in height, which needs the list to behave the way the arithmetic assumed. It does not: which item it holds still across a resize depends on what it has composed -- a row taller than the screen is anchored on itself -- and a scroll it cannot honour in full is honoured in part with nothing said. Measured rather than predicted now: each measure pass asks for the error it can see, and the pass that answers is where the rest becomes askable. Three passes is the worst seen, including the one where a card that reached past the bottom of the screen has shut, left the viewport entirely, and has to be asked back to the bottom edge before there is anything to measure at all. The pass has to be the *measure* one. A scroll asked for during placement is never picked up by another measure and does nothing whatever -- which is what the first version of this did, and why a close moved nothing -- so the list took an `afterMeasure` hook and the correction lives there. Checked with ktfmtFormat, compileDebugKotlin, lintDebug and testDebugUnitTest, and on the emulator against the sandbox, against a 2,785px card in a conversation with room on both sides: closed at 1450 it lands 1387..1513, at 1700 it lands 1637..1763, at 1950 it lands 1887..2013 -- centred on the tap to the pixel each time. Opening by the heading still holds the heading still, and a group closed from its footer bar lands on the bar. Where the conversation runs out -- a card at the very start with nothing above it to scroll -- it lands as close as the list can put it, which is what it could always do.
This commit is contained in:
1 parent
b86a5dc37a
commit
1b38579b97
3 files changed
+179
-113
No files matched your search
@@ -1189,28 +1189,34 @@ dev-updater (Kotlin 2.4.x, CMP 1.11.x, JDK 21).
|
||||
question *was*, and "Deny" alone does not say Allow was the alternative.
|
||||
One rule in two places (`AskedQuestion` and `PermissionAsk`). An answer
|
||||
typed into **Other** matches no option, so that one is still written out.
|
||||
- **Opening a row keeps still the end nearest the tap, and closing one
|
||||
keeps still the point touched** (2026-09-16). A row about to open is
|
||||
small, so both its edges are within a heading's height of the finger and
|
||||
the wanted one is the edge pressed: touch the upper half and the top
|
||||
edge holds, so it opens downwards; touch the lower half and the bottom
|
||||
edge holds, as the list does by default. A row about to close leaves
|
||||
almost nothing of itself, and the only place the closed card can
|
||||
sensibly appear is under the finger that closed it — the case that
|
||||
settles it is a card taller than the screen, where holding either edge
|
||||
throws the card a screen's length from the hand. One number carries
|
||||
both: the share of the row's height above the touch, spent as it is on a
|
||||
close and rounded to the nearer edge on an open. Which half, rather than
|
||||
which control, so everything behaves alike whether or not it has a
|
||||
control at each end. The transcript is laid out from the bottom, so a
|
||||
bottom edge is anchored for free and anything else has to be arranged:
|
||||
`Modifier.holdTouchedPoint` asks the list to shift during the *layout*
|
||||
phase, before anything is drawn. From an effect instead, the wrong
|
||||
position is drawn once first, which reads as a flick. The scroll offset
|
||||
that asks for it **goes negative on a close** — that is the list being
|
||||
asked for the rows below what it has composed — and clamping it at zero,
|
||||
which is what the growing-only version did, leaves every close
|
||||
uncorrected.
|
||||
- **Opening a row keeps still the end nearest the tap; closing one lands
|
||||
the closed row centred on the tap itself** (2026-09-16). A row about to
|
||||
open is small, so both its edges are within a heading's height of the
|
||||
finger and the wanted one is the edge pressed: touch the upper half and
|
||||
the top edge holds, so it opens downwards; touch the lower half and the
|
||||
bottom edge holds, as the list does by default. A row about to close
|
||||
leaves a heading where a screenful of card was, and the place that
|
||||
heading belongs is under the finger that shut it — a card taller than the
|
||||
screen settles it, since both of its edges can be a screen's length from
|
||||
the hand, and holding a *share* of the card's height was the same miss in
|
||||
miniature: tap away from the middle of a long card and the heading landed
|
||||
a card's height off. Where there is not enough conversation on the far
|
||||
side to scroll, the row lands as close as the list can put it.
|
||||
**The correction is measured, never predicted, and it is asked for from
|
||||
inside the list's own measure** (`settleHold`, called from
|
||||
`TranscriptList`'s `afterMeasure`). Both halves of that were paid for:
|
||||
working the scroll out from the change in height needs the list to behave
|
||||
the way the arithmetic assumed, and it does not — which item it holds
|
||||
still across a resize, and how much of a scroll it can honour, depend on
|
||||
what it has composed, and a row taller than the screen is anchored on
|
||||
*itself*. And a scroll asked for during the *placement* phase is never
|
||||
picked up by another measure, so it does nothing at all; from a coroutine
|
||||
it arrives a frame late, which is the flick this mechanism exists to
|
||||
avoid. So each pass asks for the error it can see, and the pass that
|
||||
answers is where the rest becomes askable — three passes is the worst
|
||||
case seen, including the one where the shut card has left the viewport
|
||||
entirely and has to be asked back to the bottom edge before it can be
|
||||
measured at all.
|
||||
- **The full-screen image lives on the screen, not in the row that drew
|
||||
the thumbnail** (`SessionImageViewer`). A `Read` whose result is an image
|
||||
is a row of one call until the next call arrives and makes it a group — a
|
||||
|
||||
Reference in new issue
Block a user