Close a card on the point that was touched

Collapsing held one of the row's edges -- whichever the tap was nearer -- which
is right for opening and wrong for closing: the row that shuts leaves a heading
where a screenful of card was, and both its old edges can be a screen's length
from the finger that shut it. It now keeps the touched point itself, which for a
closed card is the same thing as landing under the hand that closed it. Opening
is unchanged and deliberately so: those rows are small, every point in them is
within a heading's height of both edges, and the edge pressed is what the reader
wants held rather than a fraction of an unbounded expansion.

One number carries both readings -- 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.

The scroll offset the correction asks for goes negative on a close, and has to:
that is the list being asked for the rows below what it has composed, which is
where the newer content comes from when a card gives a screenful back. It was
clamped at zero, which was invisible while every correction was a row growing
and is what left closes uncorrected.

Checked with ktfmtFormat, compileDebugKotlin, lintDebug and testDebugUnitTest,
and on the emulator against the sandbox: a 1441px card closed at a quarter of
its height put the collapsed card's top at 743px against 743 predicted, and
opening a card by its heading still holds the heading still.
This commit is contained in:
iris-ai committed 2026-09-16 01:50:33 -04:00
1 parent 827a30768c
commit 463acb28fa
2 files changed
+108 -46

No files matched your search

+22 -9
View File
@@ -1178,15 +1178,28 @@ 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.
- **Expanding a row keeps still the end nearest the tap**: touch a row's
upper half and its top edge holds, so it opens downwards; touch its
lower half and the bottom edge holds, as the list does by default. Which
half, rather than which control, so everything that opens 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 the top one
has to be arranged: `Modifier.holdTopEdge` 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.
- **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.
- **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