Skip to content

fix: size Today tiles to their content so no card can clip - #49

Open
foureight84 wants to merge 1 commit into
mainfrom
fix/today-tiles-no-clip
Open

fix: size Today tiles to their content so no card can clip#49
foureight84 wants to merge 1 commit into
mainfrom
fix/today-tiles-no-clip

Conversation

@foureight84

Copy link
Copy Markdown
Owner

Follow-up to #47. The Sleep tile clips its score on a Pixel 10 Pro XL — the same class of bug as the Activity/calories clip, swept across the whole Today grid.

The font-scale fix could not have covered this

#47 grew the tile height by the user's font scale. That works when content fits at 1.0 and only overflows once text grows — which is the Activity tile. The Sleep tile never fit at all: its column needs ~135dp (30sp duration + 10dp + a 28dp stage bar + 10dp + a 32sp score row) and a 168dp tile leaves ~111dp after the 16dp padding, the eyebrow row and the 8dp spacer.

I reproduced it on the emulator at Pixel 10 Pro XL geometry at font scale 1.0 — the reported clip, with no font scaling involved:

Before (font scale 1.0) After (font scale 1.0)
82 sliced off at the tile's bottom edge 82 SCORE + DEEP / LIGHT / REM all visible

Scaling a box that is already too small just scales the overflow.

Content-driven height

  • TodayTile uses heightIn(min = 168.dp) — a floor, not a fixed height.
  • Each grid row is measured with IntrinsicSize.Min and its tiles get fillMaxHeight(), so a row is exactly as tall as its tallest tile needs and both tiles in it still match.

The grid stays uniform and nothing can clip, at any font scale, for any value a tile shows. This subsumes the font-scale multiplier, which is deleted — sp content grows, the intrinsic measurement sees it, the tile grows. No sampling, no 1.6 clamp, no breaking point to move.

Gauge rings — the gap #47 flagged but couldn't close

GaugeTile and BpRingColumn pinned VitalRingGauge to a dp literal while sizing its centre text off that same value (size.value * 0.30f.sp) inside a Box(modifier.size(size)), so the value overflowed its own ring as text grew. The rings now scale with the font scale, keeping ring and text proportional; the tile grows to fit the bigger ring on its own.

Stage-bar labels are measured, not guessed

The bar showed a label whenever its segment was ≥10% of the width — a proxy that breaks as text grows relative to the bar. REM rendered as "RE" at 1.3 (visible in the original report) and DEEP as "DEE" at 2.0. A truncated stage name reads as a different stage, so the label is now measured against its segment's real width and dropped when it doesn't fit; the colored segment still carries the meaning.

Measured via onSizeChanged rather than BoxWithConstraints — subcomposition cannot answer the IntrinsicSize.Min query the grid row now makes.

Verification

Runtime-verified on emulator-5554 at Pixel 10 Pro XL geometry (1344×2992 @ 480dpi), before/after on the same seeded data:

font scale before after
1.0 score clipped (reproduces the report) 82 SCORE, DEEP/LIGHT/REM all visible
1.3 no clip; REM dropped rather than truncated to "RE"
2.0 no clip; only LIGHT fits, nothing truncated

Activity keeps all three values at every scale. 875 unit tests green, assembleDebug green.

Note the tile fix has no automated coverage — the repo has no androidTest source set, so this rests on the emulator matrix above. Adding Compose UI tests would need ui-test-junit4 and a new source set; happy to do that separately if you want it locked down.

The Sleep tile clipped its score on a Pixel 10 Pro XL — reported right after the
Activity/calories fix shipped, and the same class of bug across the whole grid.

**The font-scale fix could not have covered this.** It grew the tile height by
the user's font scale, which works when the content fits at 1.0 and only
overflows once text grows. The Sleep tile never fit: its column needs ~135dp
(30sp duration + 10dp + a 28dp stage bar + 10dp + a 32sp score row) and a 168dp
tile leaves ~111dp after the 16dp padding, the eyebrow row and the 8dp spacer.
Reproduced on the emulator at Pixel 10 Pro XL geometry at **font scale 1.0** —
the reported clip, with no font scaling involved at all. Scaling a box that is
already too small just scales the overflow.

So height is now driven by content instead of approximated:

  - `TodayTile` uses `heightIn(min = 168.dp)` — a floor, not a fixed height.
  - Each grid row is measured with `IntrinsicSize.Min` and its tiles get
    `fillMaxHeight()`, so a row is exactly as tall as its tallest tile needs and
    both tiles in it still match. The grid stays uniform; nothing clips.

This subsumes the font-scale multiplier, which is deleted: sp content grows, the
intrinsic measurement sees it, the tile grows. No sampling, no 1.6 clamp, no
breaking point to move.

**The gauge rings are fixed by the sweep too**, which the previous change
explicitly could not do. `GaugeTile` and `BpRingColumn` pin `VitalRingGauge` to a
dp literal and size its centre text off that same value (`size.value * 0.30f`.sp)
inside a `Box(modifier.size(size))`, so the value overflowed its own ring as text
grew. The rings now scale with the font scale, keeping ring and text
proportional; the tile grows to fit the bigger ring on its own.

**Stage-bar labels are measured, not guessed.** The bar showed a label whenever a
segment was >=10% of the width, a proxy that broke as text grew relative to the
bar: REM rendered as "RE" at 1.3 (visible in the report) and DEEP as "DEE" at
2.0. A truncated stage name reads as a different stage, so the label is now
measured against its segment's real width and dropped when it doesn't fit — the
colored segment still carries the meaning. Measured via onSizeChanged rather
than BoxWithConstraints, because subcomposition cannot answer the
IntrinsicSize.Min query the grid row now makes.

Verified on emulator-5554 at Pixel 10 Pro XL geometry (1344x2992 @ 480dpi):

  | font scale | before          | after                                  |
  |------------|-----------------|----------------------------------------|
  | 1.0        | score clipped   | 82 SCORE + DEEP/LIGHT/REM all visible  |
  | 1.3        | -               | no clip; REM dropped, not truncated    |
  | 2.0        | -               | no clip; only LIGHT fits, none clipped  |

Activity keeps all three values at every scale. 875 unit tests green.
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