Architecture
Fenix8V3View had grown to 632 lines mixing layout constants, font
metrics, icon drawing, data lookup and orchestration. Split into:
Layout the 500-canvas constants, scaling, font choice and text drawing
Icons the three bitmap icons plus the primitive glyphs
Settings every stored preference and its default, in one place
Fields unchanged in scope, but value() is now seven category
functions that each return null for ids they do not own --
the ids interleave, so dispatching on ranges does not work
View what is left: the drawing order
The view is down to 359 lines and no longer reaches for a property or a
font metric directly.
Verification
Added a harness that walks all 75 ids in the simulator and reports each
rendered value with its pixel width. It caught a real one: pushDistance
and pushes compile on every device but throw "Symbol Not Found" at
runtime on a fenix 8 -- they are wheelchair counters that only some
firmware carries. Every optional ActivityMonitor, UserProfile, Weather
and Activity member is now behind a `has` check.
The same run showed two date fields overflowing their slot, so long
values now step down a font size rather than running into the ring.
Always-on mode checked against the simulator's Always-On display mode:
band fill gone, unlit ticks gone, slots hidden, time and date dimmed.
Also: Chinese "Day Date" was missing its 日, since it built the string by
hand instead of going through the localised date format.
All 17 devices build.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fields
75 selectable fields across date/time, activity, body, system,
environment, weather and custom text, numbered to match
watchface.io/docs/datafields so the two catalogues line up. All four
slots (both top, both bottom) pick from the same table and the bottom
captions follow the selection. tools/gen_fields.py owns the table and
emits FieldTable.mc, settings.xml and both languages' strings, so those
cannot drift apart.
Fields.Ctx fetches each API at most once per draw, and only if some slot
asks -- the forecast, user profile and sensor history lookups are lazy.
The rest of that catalogue is deliberately absent: OpenWeatherMap and
StormGlass need network calls and user API keys, the per-sport weekly and
28-day aggregates come from a phone-side Garmin Connect integration rather
than any on-device API, and the app complications need those apps
installed. README section 7 lists what was left out and why.
Permissions
UserProfile and SensorHistory are now declared; without them resting heart
rate, VO2 max, pulse ox and body battery throw at runtime. Sensor turns
out to be rejected outright for type="watchface", so altitude and pressure
read from Activity.Info instead.
Always-on display
AMOLED devices need the lit-pixel count cut while asleep. drawAmbient
drops the band fill and the unlit ticks, dims what remains, hides the
complications, and walks the layout within +/-4 px per minute so nothing
burns in. Gated on requiresBurnInProtection, so MIP devices are untouched.
Icons
The comps' sunrise and weather icons have two- and three-pixel features.
Rebuilding them from circles and lines looked right at 454 px and turned to
mush at 280. They are now rasterised from the design paths once per screen
width and tinted with drawBitmap2(:tintColor) -- which needs
packingFormat="png", since a palettised source is refused at runtime.
Launcher icons are generated at the five sizes the devices ask for.
All 17 devices build; checked on fenix847mm and enduro3 in the simulator.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>