Gamepad UI (iOS): palette-washed glass, room to breathe, in-place screens, and a real option drum #91

Merged
enricobuehler merged 1 commits from worktree-gamepad-ios-polish into main 2026-08-07 12:07:57 +00:00
Owner

Four reworks from the on-glass review of the post-#66/#74 iOS gamepad UI.

Theme follows the palette now

The reported "background changes but the theme doesn't" had a one-line root cause: ink.glass — the palette-derived surface colour, the same one pf-console-ui fills every panel with — was defined and consumed nowhere. Every surface was OS material with only a light/dark flip.

  • consoleGlass washes all three tiers with it: Glass.tint on 26+ (mixed 60 % toward the caller's focus tint so the focused row still reads accented), a fill overlay on the pre-26 material, and on tvOS. In passing this fixes a latent bug: the pre-26 branch dropped tint entirely, so focused rows there had no accent wash at all.
  • The gamepad close buttons move to a new ink-aware consoleGlassBackground (plain glassBackground follows the system appearance — wrong frost under a pale palette). Non-gamepad floating surfaces are untouched.
  • Literal-colour sweep: ConnectOverlay's console takeover rides ink.fg (it was the one console surface that stayed white-on-white on pale palettes), card shadows soften on pale fields via ink.shadow, the focused keycap reads ink.onAccent. The online pip stays status-green on purpose — a status colour must not change meaning with the wallpaper.

The header breathes

Title top padding 4/10 → 10/18 (compact/regular), plus shared gamepadHeaderSpacing / gamepadTitleBottomPadding helpers mapped from the console shell's rhythm — applied to the launcher, Settings and Add Host alike, and the Add Host close ✕ is re-anchored to the title row the way Settings' is. First row now sits ~22 pt below the tab pills instead of shoulder-to-shoulder with them.

Screens present in place (iOS)

Settings, Add Host and the Library are no longer opaque bottom-up fullScreenCovers that each mount their own backdrop. GamepadHomeView is now a layered shell (GamepadShell.swift): one persistent aurora that never unmounts, its calm chased (GamepadScreenBackground gained a continuous calmMix — the console's bg_mix), and screens as transparent layers with the console's exact choreography: 0.26 s ease-out cubic, incoming slides up out of a fade at 0.985→1, launcher recedes to 0.96; pop mirrors. Controller input is dropped for the transition (epoch-guarded), and the poll hands off through injected close/controllerActive — the coverflow also stops polling under the connect takeover, a latent double-poll. The screen enum is derived from the existing triggers, so returnToLibrary and every writer keep working; a controller arriving/leaving mid-browse hands the open library to whichever presentation the new mode owns.

macOS keeps its windowed sheets, tvOS its focus-engine covers — their paths compile and behave exactly as before.

The select is a real band

GamepadOptionBand replaces the faked ±14 pt value slip on choice rows: options ride a spring-driven drum about a vertical axis (View + Animatable, so intermediate options genuinely enter and leave mid-spin), ring-distance math makes A's wrap keep spinning forward, and spring retargeting turns rapid steps into one continuous accelerating spin instead of five restarted crossfades. Neighbours appear only under focus (an unfocused row is one flat Text — yesterday's look and cost), Reduce Motion falls back to a plain crossfade, and the fixed band frame means a step never reflows the row (the old chevron shimmy is gone). Toggles deliberately keep the quiet slip — a two-position switch on a drum reads as a coin flip.

Verification

  • swift build (macOS) — green
  • swift build --triple arm64-apple-ios17.0 against the iPhoneOS SDK — green
  • swift test — 208 passed, 5 skipped, 0 failures

On-glass QA owed before merge:

  • palette switch recolours rows/tiles/hint bar/close buttons on one dark + one pale palette; focused row still reads accented (escape hatch documented in code: tint ?? wash)
  • push/pop fade composites the material trays as one unit (fallback: .compositingGroup())
  • drum feel on device; rapid L/R spin; A-wrap; Reduce Motion crossfade
  • iPhone landscape + iPad header spacing; touch UI / macOS sheets / tvOS covers unchanged
Four reworks from the on-glass review of the post-#66/#74 iOS gamepad UI. ## Theme follows the palette now The reported "background changes but the theme doesn't" had a one-line root cause: `ink.glass` — the palette-derived surface colour, the same one `pf-console-ui` fills every panel with — was defined and consumed **nowhere**. Every surface was OS material with only a light/dark flip. - `consoleGlass` washes all three tiers with it: `Glass.tint` on 26+ (mixed 60 % toward the caller's focus tint so the focused row still reads accented), a fill overlay on the pre-26 material, and on tvOS. In passing this fixes a latent bug: the pre-26 branch dropped `tint` entirely, so focused rows there had no accent wash at all. - The gamepad close buttons move to a new ink-aware `consoleGlassBackground` (plain `glassBackground` follows the *system* appearance — wrong frost under a pale palette). Non-gamepad floating surfaces are untouched. - Literal-colour sweep: ConnectOverlay's console takeover rides `ink.fg` (it was the one console surface that stayed white-on-white on pale palettes), card shadows soften on pale fields via `ink.shadow`, the focused keycap reads `ink.onAccent`. The online pip stays status-green on purpose — a status colour must not change meaning with the wallpaper. ## The header breathes Title top padding 4/10 → **10/18** (compact/regular), plus shared `gamepadHeaderSpacing` / `gamepadTitleBottomPadding` helpers mapped from the console shell's rhythm — applied to the launcher, Settings and Add Host alike, and the Add Host close ✕ is re-anchored to the title row the way Settings' is. First row now sits ~22 pt below the tab pills instead of shoulder-to-shoulder with them. ## Screens present in place (iOS) Settings, Add Host **and the Library** are no longer opaque bottom-up `fullScreenCover`s that each mount their own backdrop. `GamepadHomeView` is now a layered shell (`GamepadShell.swift`): one persistent aurora that never unmounts, its calm **chased** (`GamepadScreenBackground` gained a continuous `calmMix` — the console's `bg_mix`), and screens as transparent layers with the console's exact choreography: 0.26 s ease-out cubic, incoming slides up out of a fade at 0.985→1, launcher recedes to 0.96; pop mirrors. Controller input is dropped for the transition (epoch-guarded), and the poll hands off through injected `close`/`controllerActive` — the coverflow also stops polling under the connect takeover, a latent double-poll. The screen enum is *derived* from the existing triggers, so `returnToLibrary` and every writer keep working; a controller arriving/leaving mid-browse hands the open library to whichever presentation the new mode owns. macOS keeps its windowed sheets, tvOS its focus-engine covers — their paths compile and behave exactly as before. ## The select is a real band `GamepadOptionBand` replaces the faked ±14 pt value slip on choice rows: options ride a spring-driven drum about a vertical axis (`View + Animatable`, so intermediate options genuinely enter and leave mid-spin), ring-distance math makes A's wrap keep spinning forward, and spring retargeting turns rapid steps into one continuous accelerating spin instead of five restarted crossfades. Neighbours appear only under focus (an unfocused row is one flat Text — yesterday's look and cost), Reduce Motion falls back to a plain crossfade, and the fixed band frame means a step never reflows the row (the old chevron shimmy is gone). Toggles deliberately keep the quiet slip — a two-position switch on a drum reads as a coin flip. ## Verification - `swift build` (macOS) — green - `swift build --triple arm64-apple-ios17.0` against the iPhoneOS SDK — green - `swift test` — 208 passed, 5 skipped, 0 failures **On-glass QA owed before merge:** - [ ] palette switch recolours rows/tiles/hint bar/close buttons on one dark + one pale palette; focused row still reads accented (escape hatch documented in code: `tint ?? wash`) - [ ] push/pop fade composites the material trays as one unit (fallback: `.compositingGroup()`) - [ ] drum feel on device; rapid L/R spin; A-wrap; Reduce Motion crossfade - [ ] iPhone landscape + iPad header spacing; touch UI / macOS sheets / tvOS covers unchanged
enricobuehler added 1 commit 2026-08-07 12:05:53 +00:00
feat(client/apple): the gamepad UI moves and colours like the console it mirrors
ci / bun-nix (pull_request) Successful in 49s
ci / web (pull_request) Successful in 1m3s
ci / docs-site (pull_request) Successful in 1m16s
apple / swift (pull_request) Successful in 1m33s
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 4m38s
ci / rust (pull_request) Successful in 16m9s
c010139e6e
Four reworks from the first palette-era on-glass review, all iOS-facing:

- Surfaces carry the palette now, not just the text on them: ConsoleGlass
  washes every tier (Liquid Glass tint, pre-26 material, tvOS material)
  with ink.glass — the same colour the desktop console fills its panels
  with — and the close buttons move to an ink-aware consoleGlassBackground.
  The pre-26 branch also gains the focus tint it had silently dropped.
  Stray literals follow: ConnectOverlay text rides ink in the console
  takeover, card shadows soften on pale fields, the focused keycap reads
  onAccent. The online pip stays status-green on purpose.

- The header breathes: title top padding 4/10 -> 10/18 plus shared
  header-spacing and title-bottom helpers mapped from the console shell's
  rhythm, applied to the launcher, settings and add-host alike, with the
  add-host close X re-anchored to the title row.

- Settings, Add Host and the Library present IN PLACE on iOS: one
  persistent aurora whose calm is chased (the console's bg_mix), screens
  as transparent layers with the console's 0.26 s ease-out-cubic push/pop,
  an input drop for the transition, and the controller handed off through
  isActive — no more opaque bottom-up covers, no backdrop teardown.
  macOS keeps its sheets, tvOS its focus-engine covers.

- The settings select is a real band: choice rows mount GamepadOptionBand,
  a spring-driven drum (Animatable body, ring-distance wrap, neighbours
  gated by focus and flight) whose retargeting spring accumulates rapid
  steps into one continuous spin. Reduce Motion falls back to a plain
  crossfade; toggles keep the quiet 14 pt slip.

Verified: swift build (macOS), swift build --triple arm64-apple-ios17.0,
swift test 208 passed / 0 failed. On-glass QA still owed: palette sweep on
a pale palette, transition compositing over materials, drum feel on device.
enricobuehler merged commit 6c4df043fe into main 2026-08-07 12:07:57 +00:00
enricobuehler deleted branch worktree-gamepad-ios-polish 2026-08-07 12:08:07 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#91