Skip to content

fix(reader): give the page back its top and bottom margins - #98

Merged
vitofico merged 1 commit into
mainfrom
fix/reader-page-margins
Aug 24, 2026
Merged

fix(reader): give the page back its top and bottom margins#98
vitofico merged 1 commit into
mainfrom
fix/reader-page-margins

Conversation

@vitofico

Copy link
Copy Markdown
Owner

Summary

The reader's text has been running the full height of the screen since v2026.08.18.227, and on a phone with a camera in the display it runs straight under it. Readium applies the page's vertical margins and the display-cutout clearance in the same pass, and that pass only ever ran from the window-insets listener it registers when shouldApplyInsetsPadding is on, so turning that flag off in #95 took the margins with it and not just the late resize. Quire applies both itself now, from the same place it already measures the viewport, so the page keeps its margins without losing the reader's place when the insets arrive a frame late.

Component(s)

  • Android app
  • Sync server
  • Documentation
  • CI / build

What changed

ReaderPageArea holds the text clear of WindowInsets.displayCutout and adds Readium's own 40dp (20dp landscape) on top. Using the cutout inset means there is no device list to keep current: a punch-hole, a notch, or nothing at all all come back through the same per-device, per-orientation value. Asking for it unconditionally is safe because windowInsetsPadding subtracts what an ancestor already consumed, so with full-screen reading off — where the subtree is already padded by the system bars, which in portrait cover the cutout — the page comes away with nothing left to clear rather than inset twice.

Two things follow from owning the margins here rather than in Readium's view tree:

  • The size that is left is measured by the same onSizeChanged the re-anchor machinery keys on, so insets arriving late are just another viewport change. That is the failure mode Fullscreen readin mode #95 could not cover, because it happened inside Readium's own views where nothing here could see it.
  • The strip the padding exposes is ours to paint, so ReaderTheme now carries the colour ReadiumCSS paints the page with and the margin reads as part of the page instead of a band around it.

Verification

Emulator with a punch-hole cutout enabled, on the marker book from #95. WebView bounds, screen 1080x2424 at 420dpi:

before after
portrait [0,0][1080,2424] [0,241][1080,2319]
landscape full bleed [136,53][2424,1027]
full-screen reading off full bleed [0,242][1080,2256]

241px = 136 of camera + 105 of margin. In landscape the camera moves to the left edge and the page moves with it. With full-screen reading off it is status bar plus margin, the cutout adding nothing.

Three background-and-resume cycles and three rotate-and-return trips leave the portrait screenshot byte-identical, so #95 stays fixed. Dark theme samples #000000 in the margin as well as in the page.

For reference, the same build at the commit before #95 settles at [0,241][1080,2319] too, but only after the first refocus — which is the resize that made it lose the reader's place.

Checklist

  • Tests added or updated (or N/A — explain)
  • Docs updated in the same PR (or N/A — no user-facing docs cover reader layout)
  • If Android UI changed: screenshots attached (geometry table above; screenshots to follow in the thread)
  • If server schema changed: Alembic migration included (N/A)
  • If core/identity changed: matching change on the other side + fixtures updated (N/A)
  • No new third-party SDKs / analytics / telemetry
  • Commit messages follow gitmoji + conventional commits

Tests: three new ReaderPageAreaTest cases covering cutout clearance, margin with nothing to clear, and an ancestor's inset not being applied twice; plus a ReaderThemeTest guard pinning each theme's page colour against Readium's own, so a Readium upgrade that repaints a theme fails there rather than on screen. Full suite green (166 app tests, reader module tests).

Related issues

Closes #97, refs #95

The text has been running the full height of the screen since the last release, and
on a phone with a camera in the display it runs straight under it — the reporter has
been tilting their Pixel 8 Pro on every page to read the first couple of sentences.

Readium applies the page's vertical margins and the display-cutout clearance in the
same pass, and that pass only ever ran from the window-insets listener it registers
when `shouldApplyInsetsPadding` is on. Turning that flag off to stop it re-paginating
the chapter under the reader (#95) therefore took the margins with it, not just the
late resize. Nothing else was applying either, so the page went edge to edge: on an
emulator with a punch-hole the WebView measured [0,0][1080,2424], where the same
build before #95 settles at [0,241][1080,2319] — 136px of camera plus 105px of margin
at the top, 105px at the bottom.

Quire already owns the reader's insets, so it now owns the page's margins too.
ReaderPageArea holds the text clear of `WindowInsets.displayCutout` — Android's own
per-device, per-orientation answer to what is covering the screen, so a punch-hole,
a notch or nothing at all all come through the same value with no device list to
keep current — and adds Readium's own 40dp (20dp landscape) on top of it. Asking for
the cutout unconditionally is safe because `windowInsetsPadding` subtracts what an
ancestor already consumed: with full-screen reading off the subtree is padded by the
system bars, which in portrait cover the cutout, and the page comes away with nothing
left to clear rather than inset twice.

Two things fall out of putting the margins here rather than in Readium's view tree.
The size that is left is measured by the same `onSizeChanged` the re-anchor machinery
keys on, so insets arriving a frame late are just another viewport change and the
reader keeps its place across them — which is what #95 could not do. And the strip
the padding exposes is Quire's to paint, so ReaderTheme now carries the colour
ReadiumCSS paints the page with and the margin reads as part of the page instead of
a band around it; ReaderThemeTest pins those against Readium's own values so an
upgrade that repaints a theme fails there rather than on screen.

Verified on an emulator with a punch-hole cutout, against the book from #95. Portrait
[0,241][1080,2319] and landscape [136,53][2424,1027] — the camera moves to the left
edge on rotation and the page moves with it. Three background-and-resume cycles and
three rotate-and-return trips leave the portrait screenshot byte-identical, so #95
stays fixed. With full-screen reading off the page sits at [0,242][1080,2256]: status
bar plus margin, the cutout adding nothing. Dark theme samples black in the margin as
well as the page.

Closes #97
@vitofico
vitofico merged commit fbba6db into main Aug 24, 2026
5 checks passed
@vitofico
vitofico deleted the fix/reader-page-margins branch August 24, 2026 12:59
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.

Choose Top/Bottom Margins

1 participant