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>
60 lines
2.3 KiB
MonkeyC
60 lines
2.3 KiB
MonkeyC
import Toybox.Lang;
|
|
import Toybox.Application;
|
|
|
|
// Every stored preference in one place, with the defaults that reproduce the
|
|
// design comps. Keys match resources/settings/properties.xml.
|
|
//
|
|
// Properties.getValue returns null for a key the watch has not stored yet --
|
|
// a fresh install, or a setting added by an update -- so every read falls back
|
|
// to the default rather than trusting the stored value.
|
|
module Settings {
|
|
|
|
// Field ids follow https://watchface.io/docs/datafields.
|
|
const DEF_LEFT_TOP = 712; // sunrise
|
|
const DEF_RIGHT_TOP = 639; // forecast high / low
|
|
const DEF_BOTTOM_LEFT = 3; // distance
|
|
const DEF_BOTTOM_RIGHT = 1; // steps
|
|
|
|
function number(key as String, def as Number) as Number {
|
|
var v = Application.Properties.getValue(key);
|
|
if (v == null) { return def; }
|
|
return v as Number;
|
|
}
|
|
|
|
function bool(key as String, def as Boolean) as Boolean {
|
|
var v = Application.Properties.getValue(key);
|
|
if (v == null) { return def; }
|
|
return v as Boolean;
|
|
}
|
|
|
|
function text(key as String) as String {
|
|
var v = Application.Properties.getValue(key);
|
|
if (v == null) { return ""; }
|
|
return v.toString();
|
|
}
|
|
|
|
// Theme index, clamped into range in case a stored value outlives a
|
|
// release that removed a theme.
|
|
function themeIndex(count as Number) as Number {
|
|
var i = number("Theme", 1) - 1;
|
|
if (i < 0 || i >= count) { return 0; }
|
|
return i;
|
|
}
|
|
|
|
function leftTop() as Number { return number("LeftTop", DEF_LEFT_TOP); }
|
|
function rightTop() as Number { return number("RightTop", DEF_RIGHT_TOP); }
|
|
function bottomLeft() as Number { return number("BottomLeft", DEF_BOTTOM_LEFT); }
|
|
function bottomRight() as Number { return number("BottomRight", DEF_BOTTOM_RIGHT); }
|
|
|
|
function ringTR() as Number { return number("RingTR", 0); }
|
|
function ringTL() as Number { return number("RingTL", 1); }
|
|
function ringBR() as Number { return number("RingBR", 4); }
|
|
function ringBL() as Number { return number("RingBL", 3); }
|
|
|
|
function tempUnit() as Number { return number("TempUnit", 0); }
|
|
|
|
function showTop() as Boolean { return bool("ShowTop", true); }
|
|
function showBand() as Boolean { return bool("ShowBand", true); }
|
|
function showBottom() as Boolean { return bool("ShowBottom", true); }
|
|
}
|