The console's own settings never left the console, and the layer under a card missed its corners #362

Merged
enricobuehler merged 2 commits from worktree-android-console-extra-keys into main 2026-08-20 20:03:48 +00:00
Owner

Reported on glass: the "Controller-optimized UI" switch is there, turning it off does nothing — the console stays up.

The switch

pf_client_core::trust::Settings::extra is #[serde(flatten)], so the android.* console settings are top-level keys of the settings document, beside width and codec. There is no "extra" object — trust.rs's own settings_unknown_keys_survive_round_trip already asserts a serialized default contains no such key.

ConsoleJson wrote and read them nested under one. Serde put that whole object into the map under the literal key "extra", so:

  • no console row ever found android.gamepad_ui_enabled — each showed its compiled default, whatever the touch UI had stored;
  • the value the console saved came back to Kotlin as the one Kotlin had just sent, so applySettings saw no change, raised no onSettingsChange, and App never recomputed gamepadUiActive.

Hence a switch you can flip with nothing behind it.

It hit every Android-only row, not just this one: low latency, phone rumble, phone gyro, SC2 capture, DualSense capture, and the Show it mode picker. None of them round-tripped from the console.

A store written by the nesting build carries the dead wrapper; it is dropped on the next write rather than echoed for the life of the install.

Why nothing caught it

Both halves agreed on the same wrong nesting, so a write-then-read round-trip passes while the feature is broken — theRoundTripKeepsEveryAndroidRow in the new test still passes with the fix reverted. The other three cases assert the shape (android.… present at the top level, no "extra" wrapper, a console-shaped save read back) and all three fail without it.

The layer under the cards

Also reported: the layer behind every card, the one that reads as an outline, does not follow the card's corners.

That is theme::focus_halo. It grows the card's rect by 4 design units on every side but drew it with the card's own radius. A shape grown by d keeps its corners parallel to the original's only if its radius grows by d too — otherwise the two arcs stop sharing a centre. So the halo came out squarer than the card: clean along the edges, visibly off at the four corners.

Same rule applied to panel_highlight, which pulls in half a unit and kept the full radius. drop_shadow only offsets, so its geometry was already right, and the collections plate uses RRect::with_outset, which adjusts the radii itself.

Every card in the console goes through these two helpers — home tiles, library grid, coverflow, collections deck.

Verification

  • 98 app unit tests green; the new ConsoleSettingsExtraTest fails 3 of 4 with the fix stashed.
  • cargo fmt --check -p pf-console-ui clean.
  • Native compiles for arm64-v8a, armeabi-v7a and x86_64 (:kit:cargoNdkRelease).

On glass owed — nobody has yet watched the console actually drop to the touch UI, and the halo change is a visual call worth eyeballing.

Reported on glass: the "Controller-optimized UI" switch is there, turning it off does nothing — the console stays up. ## The switch `pf_client_core::trust::Settings::extra` is `#[serde(flatten)]`, so the `android.*` console settings are **top-level** keys of the settings document, beside `width` and `codec`. There is no `"extra"` object — `trust.rs`'s own `settings_unknown_keys_survive_round_trip` already asserts a serialized default contains no such key. `ConsoleJson` wrote and read them nested under one. Serde put that whole object into the map under the literal key `"extra"`, so: - no console row ever found `android.gamepad_ui_enabled` — each showed its compiled default, whatever the touch UI had stored; - the value the console saved came back to Kotlin as the one Kotlin had just sent, so `applySettings` saw no change, raised no `onSettingsChange`, and `App` never recomputed `gamepadUiActive`. Hence a switch you can flip with nothing behind it. It hit **every** Android-only row, not just this one: low latency, phone rumble, phone gyro, SC2 capture, DualSense capture, and the `Show it` mode picker. None of them round-tripped from the console. A store written by the nesting build carries the dead wrapper; it is dropped on the next write rather than echoed for the life of the install. ### Why nothing caught it Both halves agreed on the same wrong nesting, so a write-then-read round-trip passes while the feature is broken — `theRoundTripKeepsEveryAndroidRow` in the new test still passes with the fix reverted. The other three cases assert the **shape** (`android.…` present at the top level, no `"extra"` wrapper, a console-shaped save read back) and all three fail without it. ## The layer under the cards Also reported: the layer behind every card, the one that reads as an outline, does not follow the card's corners. That is `theme::focus_halo`. It grows the card's rect by 4 design units on every side but drew it with the card's *own* radius. A shape grown by `d` keeps its corners parallel to the original's only if its radius grows by `d` too — otherwise the two arcs stop sharing a centre. So the halo came out squarer than the card: clean along the edges, visibly off at the four corners. Same rule applied to `panel_highlight`, which pulls in half a unit and kept the full radius. `drop_shadow` only offsets, so its geometry was already right, and the collections plate uses `RRect::with_outset`, which adjusts the radii itself. Every card in the console goes through these two helpers — home tiles, library grid, coverflow, collections deck. ## Verification - 98 app unit tests green; the new `ConsoleSettingsExtraTest` fails 3 of 4 with the fix stashed. - `cargo fmt --check -p pf-console-ui` clean. - Native compiles for arm64-v8a, armeabi-v7a and x86_64 (`:kit:cargoNdkRelease`). ⏳ **On glass owed** — nobody has yet watched the console actually drop to the touch UI, and the halo change is a visual call worth eyeballing.
enricobuehler added 2 commits 2026-08-20 19:52:39 +00:00
Turning "Controller-optimized UI" off in the console did nothing: the console
stayed up, because the setting never left the console.

`trust::Settings::extra` is `#[serde(flatten)]`, so the `android.*` keys are
TOP-LEVEL keys of the settings document, beside `width` and `codec`.
`ConsoleJson` wrote and read them nested under an `"extra"` object instead.
Serde put that whole object into the map under the literal key `"extra"`, so
no console row ever found `android.gamepad_ui_enabled` — the row showed its
own default, and the value the console saved came back to Kotlin as the one
Kotlin had just sent. `applySettings` then saw no change, raised no callback,
and `App` never recomputed `gamepadUiActive`.

Every Android-only row rode the same broken path: low latency, phone
rumble/gyro, SC2 and DualSense capture, and the console-UI mode picker.

A store written by the nesting build carries the dead wrapper; it is dropped
on the next write rather than echoed for the life of the install.

The new test pins the shape from both sides. A round-trip alone could not have
caught this — both halves agreed on the same wrong nesting, which is exactly
how it survived review.
fix(console-ui): the layer under a card takes the card's corner plus its own outset
ci / bun-nix (pull_request) Successful in 24s
ci / docs-drift (pull_request) Successful in 42s
ci / web (pull_request) Successful in 1m6s
ci / docs-site (pull_request) Successful in 1m15s
ci / rust-arm64 (pull_request) Successful in 2m3s
android / android (pull_request) Successful in 5m34s
ci / rust (pull_request) Successful in 5m45s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m48s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 3m9s
c9a76287d8
The focus halo grows the card's rect by 4 design units on every side but drew
it with the card's own corner radius. A shape grown by `d` keeps its corners
parallel to the original's only if its radius grows by `d` too — otherwise the
two arcs stop sharing a centre. So the halo came out squarer than the card it
sits under: clean along the edges, visibly misaligned at the four corners,
where it read as a badly drawn outline rather than as light spilling out.

Same rule applied to `panel_highlight`, which pulls in half a unit and kept the
full radius. `drop_shadow` only offsets, so its geometry was already right, and
the collections plate uses `RRect::with_outset`, which adjusts the radii itself.

Every card in the console goes through these two helpers — the home tiles, the
library grid, the coverflow, the collections deck.
enricobuehler merged commit 1c60e641b3 into main 2026-08-20 20:03:48 +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#362