fix(widget): scale the agenda widget with its size (#51) #88

Merged
makiolaj merged 2 commits from fix/agenda-widget-size-scaling into release/v2.16.0 2026-07-20 13:11:18 +00:00
Owner

Closes Codeberg #51.

What

#51 asked for a widget font-size setting. It's a bug, not a missing
preference: AgendaWidget declared no sizeMode, so Glance used
SizeMode.Single — the widget was composed once at the provider minimum and the
launcher simply stretched the result. Type stayed small-widget-sized no matter
how large you made it. The reporter confirmed the default size looks fine and
it's only wrong when enlarged.

So there is no new setting. The widget follows the size you already chose.

How

  • AgendaWidget declares SizeMode.Exact (as MonthWidget already does), so
    the composition sees the live size via LocalSize.current.
  • New shared widget/WidgetScale.kt buckets a DpSize into
    COMPACT / REGULAR / LARGE / XLARGE. Width picks the tier — it governs how
    much of a title fits per row — and height only ever caps it, so a tall,
    narrow widget shows more events rather than bigger text.
  • widget/agenda/AgendaScale.kt maps each tier to an AgendaMetrics table
    (type sizes, stripe, icons, row padding). Both files are Glance-free, so the
    bucketing and the metrics are covered by plain JVM tests.
  • COMPACT reproduces the widget's original constants verbatim, and a test
    pins that — a default-sized widget looks exactly as it did.

Type sizes are anchored to Material 3 type-scale roles; the two off-scale values
are marked and justified inline (notably COMPACT's 13sp day header, held off the
scale on purpose so the default stays unchanged).

Notes for review

Second commit is a review pass over the first. Two of its fixes are
load-bearing and worth a look:

  • The original height cap defeated the feature. It required ~320dp of height
    before LARGE, so widening a widget without also making it unusually tall —
    the exact resize #51 reports — stayed at REGULAR. Caps now work on usable
    height (gross minus header chrome), and a 378x270 widget reaches LARGE.
  • SizeMode.Exact still emits one RemoteViews per host size (usually
    portrait + landscape) where Single emitted one, so the payload roughly
    doubles. With the range reaching AgendaRange.MAX_CUSTOM_DAYS (365) an
    uncapped list could pass the binder transaction limit and the host would show
    "Problem loading widget". Rows are capped at 100, dropping a day header the
    cut leaves stranded.

MonthWidget keeps its own type sizes for now — it can adopt WidgetScale
later; its type couples to the grid's lane height, which is a separate rework.

Testing

testDebugUnitTest and lintDebug green. AgendaScaleTest / WidgetScaleTest
cover the tier bucketing (including monotonicity across both axes), the
"default unchanged" baseline, the derived text indent, and font-scale handling.

Installed on device; on-device review of the tiers is not yet signed off
please don't merge to release before that.

Closes Codeberg #51. ## What #51 asked for a widget **font-size setting**. It's a bug, not a missing preference: `AgendaWidget` declared no `sizeMode`, so Glance used `SizeMode.Single` — the widget was composed once at the provider minimum and the launcher simply stretched the result. Type stayed small-widget-sized no matter how large you made it. The reporter confirmed the default size looks fine and it's only wrong when enlarged. So there is **no new setting**. The widget follows the size you already chose. ## How - `AgendaWidget` declares `SizeMode.Exact` (as `MonthWidget` already does), so the composition sees the live size via `LocalSize.current`. - New shared `widget/WidgetScale.kt` buckets a `DpSize` into `COMPACT / REGULAR / LARGE / XLARGE`. Width picks the tier — it governs how much of a title fits per row — and height only ever *caps* it, so a tall, narrow widget shows more events rather than bigger text. - `widget/agenda/AgendaScale.kt` maps each tier to an `AgendaMetrics` table (type sizes, stripe, icons, row padding). Both files are Glance-free, so the bucketing and the metrics are covered by plain JVM tests. - `COMPACT` reproduces the widget's original constants **verbatim**, and a test pins that — a default-sized widget looks exactly as it did. Type sizes are anchored to Material 3 type-scale roles; the two off-scale values are marked and justified inline (notably COMPACT's 13sp day header, held off the scale on purpose so the default stays unchanged). ## Notes for review Second commit is a review pass over the first. Two of its fixes are load-bearing and worth a look: - **The original height cap defeated the feature.** It required ~320dp of height before `LARGE`, so widening a widget without also making it unusually tall — the exact resize #51 reports — stayed at `REGULAR`. Caps now work on *usable* height (gross minus header chrome), and a 378x270 widget reaches `LARGE`. - **`SizeMode.Exact` still emits one RemoteViews per host size** (usually portrait + landscape) where `Single` emitted one, so the payload roughly doubles. With the range reaching `AgendaRange.MAX_CUSTOM_DAYS` (365) an uncapped list could pass the binder transaction limit and the host would show "Problem loading widget". Rows are capped at 100, dropping a day header the cut leaves stranded. `MonthWidget` keeps its own type sizes for now — it can adopt `WidgetScale` later; its type couples to the grid's lane height, which is a separate rework. ## Testing `testDebugUnitTest` and `lintDebug` green. `AgendaScaleTest` / `WidgetScaleTest` cover the tier bucketing (including monotonicity across both axes), the "default unchanged" baseline, the derived text indent, and font-scale handling. Installed on device; **on-device review of the tiers is not yet signed off** — please don't merge to release before that.
makiolaj added 2 commits 2026-07-20 12:57:33 +00:00
The "Upcoming" agenda widget used Glance's default SizeMode.Single: it was
composed once at the minimum size and the launcher stretched that single
RemoteViews when enlarged, so the text stayed small-widget-sized no matter how
big the widget grew. Reported as a "font size" request (#51), but it's really a
missing size-response.

Switch to SizeMode.Exact (like MonthWidget) and read LocalSize.current to pick
one of four tiers (COMPACT/REGULAR/LARGE/XLARGE), scaling type and row metrics.
Exact over Responsive so the ~30-day LazyColumn isn't replicated per tier.

Width picks the tier, height can only lower it. Width governs how much of a
title fits on a row, so it's what should drive type size; height only decides
how many rows are visible, so a tall narrow widget shows more events rather than
bigger text. Height does act as a cap, though, or a squashed widget would keep
the large type its width earned in a sliver of space. Thresholds are spread over
the width range a phone actually produces (measured on a Pixel/Nova: a compact
widget is 222dp wide, a large one 378dp) rather than a theoretical range, so the
tiers are reachable in practice; XLARGE is reserved for tablets/foldables.

COMPACT reproduces the original constants verbatim, so an existing widget is
visually unchanged. The tier logic lives in a pure, Glance-free AgendaScale.kt
(compose.ui.unit only) and is JVM-tested: the COMPACT baseline, the width
buckets, the height cap stepping a squashed widget down, and that height never
raises the tier.

No new setting: the widget follows the size the launcher/user already chose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(widget): address review of the agenda size scaling (#51)
All checks were successful
Translations / check (pull_request) Successful in 5s
CI / ci (pull_request) Successful in 5m52s
a34f29bcf8
Follow-up to 30fcbfa, fixing eight issues found in review:

- Cap the agenda row list at 100. SizeMode.Exact asks Glance for one
  RemoteViews per host size where SizeMode.Single produced exactly one,
  roughly doubling the payload; with the range reaching
  AgendaRange.MAX_CUSTOM_DAYS (365) an uncapped list could push past the
  binder transaction limit and the host would show "Problem loading
  widget". A trailing day header stranded by the cut is dropped.

- Loosen the height cap so it only catches genuinely squashed widgets.
  It previously required ~320dp of height for LARGE, so widening a widget
  without also making it unusually tall — the exact resize #51 reports —
  stayed REGULAR or COMPACT and the feature was near a no-op for it.
  Thresholds now work on height minus header chrome.

- Lock the event stripe to the system font scale. It is a Dp beside sp
  text, so at large accessibility settings the text outgrew it and it
  under-ran the row it marks.

- Route the day-header and placeholder padding through the metrics table
  so vertical rhythm holds at the larger tiers, and derive the text
  indent from the row constants instead of a hardcoded 19dp.

- Share the bucketing as widget/WidgetScale.kt so MonthWidget (already
  SizeMode.Exact) can adopt one rule rather than growing a parallel copy.

- Anchor the type ramp to Material 3 type-scale roles per CLAUDE.md, with
  the two off-scale values marked and justified inline. COMPACT is
  unchanged, so a default-sized widget still looks exactly as before.

- Tie the "default size unchanged" test to the provider XML's declared
  3-cell band rather than one measured 222dp point.

- Hang the metrics off an ordinal-indexed table so lookup allocates
  nothing per recomposition.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
makiolaj merged commit 20e18768eb into release/v2.16.0 2026-07-20 13:11:18 +00:00
makiolaj deleted branch fix/agenda-widget-size-scaling 2026-07-20 13:11:18 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: makiolaj/calendula#88