fix(android): the page controls stay put, and the ink stops climbing over the toolbar #22

Merged
brian merged 1 commit from test into main 2026-08-28 03:51:58 +00:00
Owner

Two faults met while scrolling a day.

The pin/paper-type bar lived in the same list row as the page it belongs to, so
it scrolled off with it. A page is taller than the screen, which meant that by
the time you were writing in the middle of one, the controls for that page —
which paper, the locks, the zoom bookmark — were off the top of the list, and
reaching them meant scrolling the page you were working on out of sight. Each
page is now two rows: a sticky header and the paper, so the page slides under
its own controls.

That split them across two compositions, and a remember in either row dies
when that row leaves. The surface reference the header acts through and the
externalEdit tick the paper adopts move into a per-page holder outside the list,
cleared by view identity on release and pruned when a page leaves the day.
Parsing moved out with them: PageContent.parse ran per row per recomposition,
and this screen recomposes on every stroke.

The second is the front-buffered ink layer. It is composited above the whole
window, so no Compose clip touches it — while it is up the page is painted over
the toolbar and the day's files instead of scrolling beneath them. It is only
ever taken down by the settle timer, and the timer is only ever scheduled by a
render, so firing mid-gesture and doing nothing meant a gesture that ended
without one more render after it — a pinch held still, a stroke finished with
the pen resting — left the layer up for good. The decision is now settleDecision()
with Wait as an answer that reschedules, covered by FrontBufferSettleTest, and
InkSurface.foldFrontBuffer() lets the day screen take the layer down the moment
the list starts scrolling, which the surface has no way to notice itself.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Two faults met while scrolling a day. The pin/paper-type bar lived in the same list row as the page it belongs to, so it scrolled off with it. A page is taller than the screen, which meant that by the time you were writing in the middle of one, the controls for that page — which paper, the locks, the zoom bookmark — were off the top of the list, and reaching them meant scrolling the page you were working on out of sight. Each page is now two rows: a sticky header and the paper, so the page slides under its own controls. That split them across two compositions, and a `remember` in either row dies when that row leaves. The surface reference the header acts through and the externalEdit tick the paper adopts move into a per-page holder outside the list, cleared by view identity on release and pruned when a page leaves the day. Parsing moved out with them: PageContent.parse ran per row per recomposition, and this screen recomposes on every stroke. The second is the front-buffered ink layer. It is composited above the whole window, so no Compose clip touches it — while it is up the page is painted over the toolbar and the day's files instead of scrolling beneath them. It is only ever taken down by the settle timer, and the timer is only ever scheduled by a render, so firing mid-gesture and doing nothing meant a gesture that ended without one more render after it — a pinch held still, a stroke finished with the pen resting — left the layer up for good. The decision is now settleDecision() with Wait as an answer that reschedules, covered by FrontBufferSettleTest, and InkSurface.foldFrontBuffer() lets the day screen take the layer down the moment the list starts scrolling, which the surface has no way to notice itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two faults met while scrolling a day.

The pin/paper-type bar lived in the same list row as the page it belongs to, so
it scrolled off with it. A page is taller than the screen, which meant that by
the time you were writing in the middle of one, the controls for that page —
which paper, the locks, the zoom bookmark — were off the top of the list, and
reaching them meant scrolling the page you were working on out of sight. Each
page is now two rows: a sticky header and the paper, so the page slides under
its own controls.

That split them across two compositions, and a `remember` in either row dies
when that row leaves. The surface reference the header acts through and the
externalEdit tick the paper adopts move into a per-page holder outside the list,
cleared by view identity on release and pruned when a page leaves the day.
Parsing moved out with them: PageContent.parse ran per row per recomposition,
and this screen recomposes on every stroke.

The second is the front-buffered ink layer. It is composited above the whole
window, so no Compose clip touches it — while it is up the page is painted over
the toolbar and the day's files instead of scrolling beneath them. It is only
ever taken down by the settle timer, and the timer is only ever scheduled by a
render, so firing mid-gesture and doing nothing meant a gesture that ended
without one more render after it — a pinch held still, a stroke finished with
the pen resting — left the layer up for good. The decision is now settleDecision()
with Wait as an answer that reschedules, covered by FrontBufferSettleTest, and
InkSurface.foldFrontBuffer() lets the day screen take the layer down the moment
the list starts scrolling, which the surface has no way to notice itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brian merged commit 1b91af9351 into main 2026-08-28 03:51:58 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
brian/Journal!22
No description provided.