Skip to content

Fit the Library grid to wide screens, the Mac and the iPhone Duo's fold - #1651

Merged
GianniCarlo merged 2 commits into
developfrom
feat/duo-book-pose-grid
Oct 17, 2026
Merged

GianniCarlo merged 2 commits into
developfrom
feat/duo-book-pose-grid

Conversation

@GianniCarlo

Copy link
Copy Markdown
Collaborator

Problem

Follow-ups to the Library grid (#1649) on bigger and foldable screens:

  • iPhone Duo, book pose (half open in landscape): the fold runs top to bottom through the middle of the grid, so a column of tiles sat bent across it.
  • Wide screens (iPad, the open Duo): Covers kept their 100 pt minimum, so a wide window filled with many small covers (seven a row on the open Duo).
  • Mac: the app is drawn at 77% there, so even 140 pt covers looked small, and tiles didn't follow the app's Text Size setting, which stands in for Dynamic Type on a Mac.
  • Cards reserved two title lines and an author line on every card so rows would line up, which left a gap under short titles.

Changes

  • Fold-aware columns (ItemListView+Grid.swift): the grid reads the fold with reservedRegions(kind: .division) (iOS 27.1, the same signal the player's L-pose split uses). While a fold runs top to bottom across the grid, FoldedTileColumns lays out fixed columns that leave it in the gap between two of them:

    • every tile is one width (the narrower side decides), so rows still line up;
    • the leftover space goes to the outer edges, away from the fold;
    • on the Duo: Covers 3 + 2 at ~141 pt, cards 2 + 2 at ~169 pt.

    Flat, closed, or folded across the screen, the grid keeps its flexible columns.

  • Covers on wide screens: a 140 pt minimum in a regular-width window, 100 pt otherwise (iPhone, the closed Duo). On the open Duo that's five a row at ~160 pt instead of seven.

  • Mac: every tile minimum is 1.3× (offsetting the 77% drawing scale) and follows the Text Size setting through BPFontModifier.macOSScaleFactor(forIndex:), the same factor the fonts use.

  • Card heights (LibraryTileView): each row is as tall as its tallest card, and cards fill it (LazyVGrid stretches a tile to its row). The hidden template that reserved space on every card is gone. The title keeps up to two lines (three at the accessibility sizes); the author now gets up to two. The text sits 6 pt below the cover with 10 pt under it.

  • CLAUDE.md: the Library's Layouts paragraph corrects the earlier claim that the grid can't stretch a tile to its row, and covers the fold columns and the Mac sizing.

Testing

  • iPhone Duo simulator (iOS 27.1):
    • book pose, Covers and Grid: the fold falls in the gap between columns;
    • flat open and closed: the grid as before, with the larger Covers when open;
    • folding and unfolding with the Library open: the columns switch cleanly;
    • reordering with per-row heights: rows resize smoothly.
  • iPad Air 11 simulator: Covers five a row at ~151 pt in portrait.
  • Mac ("Designed for iPad"): larger covers and cards, four covers a row in the narrowest window.
  • iPhone (device): per-row card heights, two-line authors, the new padding; Covers unchanged.
  • Unit tests: all 988 pass, including the new FoldedTileColumnsTests (the column arithmetic on the Duo's fold, the fold as the only wider gap, no columns when a side can't hold a tile).

On iPhone Duo half open in landscape (the book pose) the fold ran through
a column of tiles. The grid now reads the fold's position from
reservedRegions(kind: .division) and, while it runs top to bottom across
the grid, switches to fixed columns that leave it in the gap between two
of them (FoldedTileColumns): one tile width on both sides so rows still
line up, the leftover space at the outer edges.

Covers fit too many small tiles on wide screens: they now use a 140 pt
minimum in a regular-width window (100 pt otherwise). On a Mac, which
draws the app at 77%, every tile minimum is 1.3x and follows the Text
Size setting, as the fonts do.

Cards size each row to its tallest card instead of reserving two title
lines and an author line on every card: LazyVGrid stretches a tile to
its row, so a row lines up while short titles no longer leave a gap. The
author now gets up to two lines, and the text sits 6 pt below the cover
with 10 pt under it.
}
/// A grid centers fixed columns in its width: given just theirs, the inset places them
.frame(width: foldedTileColumns?.width)
.padding(.leading, foldedTileColumns?.leadingInset ?? 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 INFO — The fold span comes from reservedRegions frames, which count x from the physical left edge. But the inset is applied with .padding(.leading, …) / .topLeading, and LazyVGrid columns follow the layout direction. In a right-to-left layout, the leading inset and the foldGap column would probably be mirrored, so the gap would land at width - fold rather than on the fold. That isn't reachable today because the app ships no RTL localizations. If RTL is ever added, either compute the inset from width - fold.upperBound when layoutDirection == .rightToLeft, or force .environment(\.layoutDirection, .leftToRight) on the folded grid's frame. A short comment noting this assumption would help.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked on the iPhone Duo simulator in Arabic, in the book pose: the grid mirrors (first item top right) and the fold still falls exactly in the gap between columns. reservedRegions(kind:options:layoutDirectionBehavior:) defaults to .mirrors, so the fold's frame is reported from the leading edge, the same way the leading inset and the grid's columns run. No change needed here. (RTL is reachable: the app ships Arabic.)

The same test turned up a separate right-to-left bug, fixed in fb04173: a folder tile's measured width came back a rounding error apart on every pass, and since the tile sizes itself from that width, the app re-laid it out forever and hung on the launch screen. The tile now ignores width changes under half a point.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reported again on the newest commit, worded differently — the current wording is:

Still applies: the fold span comes from reservedRegions frames in physical left-to-right x, but the inset is applied with .padding(.leading, …) and .topLeading, and the fixed LazyVGrid columns (including the foldGap one) follow the layout direction. In a right-to-left layout the gap would probably be mirrored to width - fold instead of sitting on the fold. Nothing hits this today because the app ships no RTL .lproj. The new comment in LibraryTileView shows RTL is already being thought about, so either note the assumption here or force .environment(\.layoutDirection, .leftToRight) on the folded grid's frame (or compute the inset from width - fold.upperBound when right-to-left).

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

✅ Claude PR Review — PASS

This PR changes only the Library grid's layout in SwiftUI. It adds fold-aware fixed columns (FoldedTileColumns, which reads reservedRegions(kind: .division) on iOS 27.1+), a larger 140 pt minimum for Covers in regular-width windows, and Mac tile sizes that are 1.3× and follow the Text Size setting through a new static BPFontModifier.macOSScaleFactor(forIndex:). Cards now get per-row heights through frame(maxHeight: .infinity), which replaces the hidden template that reserved the same space on every card. Concurrency, the player and AVAudioSession, CoreData/SwiftData and auth/entitlements are untouched. The new state is view-local @State/@AppStorage, and macOSTextScale already lives in standard defaults, consistent with BPFontModifier and Settings. There is no layout feedback loop: onGeometryChange measures the frame that fills the full width, which doesn't depend on the fold columns, and the coverWidth update now ignores changes under 0.5 pt, so rounding can't keep re-triggering it. I checked the column arithmetic in FoldedTileColumnsTests by hand against the Duo values, and the test file is in the BookPlayerTests target. The only open note is still the left-to-right assumption in the fold inset; it can't happen today because the app ships no right-to-left localizations.

Findings: 1 info

Model claude-opus-5-5 · run log · 0 new · 1 carried over · 1 re-worded on their own thread · 0 resolved · advisory (a human should still review). Findings are de-duplicated across pushes; an earlier finding closes only when the verification pass judges it against the current code — fixed, no longer applicable, accepted by a maintainer, or a duplicate of a finding reported on this push.

In Arabic, Covers or Grid with a folder hung the app at launch on its
launch screen. The tile sizes its stack and progress mark from its own
measured width, and in a right-to-left layout that width came back a
rounding error apart on every pass (160.6, then 160.59999999999997), so
each update triggered another. The tile now only takes a width that
changed by half a point or more.
@GianniCarlo
GianniCarlo merged commit 05a1ca2 into develop Oct 17, 2026
3 checks passed
@GianniCarlo
GianniCarlo deleted the feat/duo-book-pose-grid branch October 17, 2026 02:55

This branch was successfully deployed

1 active deployment
reviewer — fb041732 Deployed Oct 17, 2026 by GianniCarlo via review #278
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.

1 participant