Visual system¶
| Garmin | Wear OS | Apple Watch | |
|---|---|---|---|
| Status | ✅ | ✅ | ⏳ |
| Code | source/Theme.mc, source/VesselState.mc, tools/genfont.py, resources-round-<w>x<h>/, manifest.xml |
core/…/VesselState.kt (Palette), wear/…/presentation/gfx/Dc.kt, wear/…/presentation/views/Theme.kt (Layout), wear/…/presentation/GarminCanvas.kt, wear/src/main/res/font/ |
— |
Verified against code: 2026-10-13
Purpose¶
The colours, fonts, layout rules and screen sizes every WatchApp screen is built from. Garmin is the reference: Wear OS draws the same screens with the same numbers through a Connect IQ-style drawing layer, so one set of fractions and colours holds on both. Screen-specific colours stay in their screen's spec; this file lists the shared ones.
Layout / behaviour¶
Palette¶
Colours are opaque 0xRRGGBB. Two sets are used: the app's own theme colours and Connect IQ's named colours. Wear OS holds both in Palette with the same values.
Theme colours (Theme.mc, Palette):
| Name | Hex | Used for |
|---|---|---|
SAIL |
Sail accent (vessel-state.md) | Sailing accent; also Paired, sync success, progress bar, zoom-in badge |
ENGINE |
Engine accent (vessel-state.md) | Motoring accent; also errors (sync failure), discard confirm, zoom-out badge |
MOTORSAIL |
Motorsail accent (vessel-state.md) | Motorsailing accent |
IDLE |
Stationary accent (vessel-state.md) | Accent of every stationary state |
BG |
0x000000 |
Background of the black screens |
FG |
0xFFFFFF |
Main text on black |
DIM |
0xAAAAAA |
Captions and secondary text on black |
FAINT |
0x555555 |
Hints, empty progress bar |
RULE |
0x555555 |
Divider lines (auto-stats overlay) |
The vessel state accent is given by the state itself (VesselState.color()); Theme.accentFor(state) returns IDLE when there is no state.
Connect IQ named colours (Graphics.COLOR_*, Wear OS Palette.*):
| Name | Hex | Used for |
|---|---|---|
WHITE |
0xFFFFFF |
Dashboard background, SOG digits, overlay text |
BLACK |
0x000000 |
Dashboard text, rules and SOG block, pause view background |
LT_GRAY |
0xAAAAAA |
Unlit GPS bars, battery outlines, GPS off, page indicators |
DK_GRAY |
0x555555 |
Wear OS only (defined, same as FAINT) |
ORANGE |
0xFF5500 |
Units and labels on the dashboard, max-speed ▲, turn angle and turn segments, paused, Wear OS touch-lock badge |
YELLOW |
0xFFAA00 |
Baro arrow, rising or falling |
RED |
0xFF0000 |
Baro arrow (strong trend), alert overlay background, battery fill below 20 % on the pause view |
BLUE |
0x00AAFF |
Lit GPS bars, battery fill, baro arrow (steady), speed chart fallback |
ORANGE (0xFF5500) and the Motorsail accent are different colours; YELLOW has the same value as the Motorsail accent, and BLUE the same as the Sail accent.
Screen-local colours (listed in their screen spec): Environment view card, arc and tide colours (environment.md); the brand colours navy 0x0B1B2B, brand orange 0xED6017 and slate 0x94A3B8 on the splash and the auto-stats overlay (lifecycle.md, alerts-and-auto-stats.md).
Rule for saturated colours: use the Connect IQ named colour, not a custom hex, where a colour must stay strong on MIP screens. MIP displays show 64 colours, and a custom red like 0xD32F2F turns pale salmon there; the alert overlay therefore uses RED.
Fonts¶
Three custom fonts carry the numbers; Garmin's system fonts carry text.
| Font | Face | Digit height (8) |
Glyph set | Used for |
|---|---|---|---|---|
| sog (hero) | Roboto Condensed Bold | 0.180 × screen width | 0123456789.: |
Dashboard SOG, pause-view clock, Wear OS ambient SOG |
| numerals | Roboto Condensed Bold | 0.090 × width | 0123456789.:-°+Ø |
Clock, COG, distance, timer, pairing code, auto-stats values |
| header | Roboto Condensed Bold | 0.080 × width | 0123456789.:-°+\| SABCDGM acefghilnorstuy (note the space) |
Max and average speed, pressure, CoG / nM labels, vessel state line 1, SOG tens digit, turn angle |
A text drawn in a custom font may only use characters from its glyph set. Anything else draws as an empty box on Garmin. The header set covers exactly the eight -ing state labels and the dashboard labels.
System fonts, smallest to largest: XTINY, TINY, SMALL, MEDIUM, LARGE. The theme names two of them: the label font (XTINY) for captions and the small font (TINY).
Garmin¶
- The custom fonts are BMFont bitmap sheets (
.fnt+ greyscale anti-aliased.png), one set per screen family inresources-round-<w>x<h>/fonts/with a generatedfonts.xml(idsSogFont,NumeralsFont,HeaderFont). The SDK picks the family's folder at build time, so a build carries only its own sheets;monkey.jungleneeds no entry. tools/genfont.pygenerates every sheet fromresources/fonts/RobotoCondensed-Bold.ttf: for each family it picks the TTF pixel size whose8is closest to the target digit height, packs the glyphs into a sheet at most 256 px wide, and rebases the metrics on the glyph ink so that the line height is the drawn height and vertical centring lands on the digits. Edit the script, never the sheets.- Bitmap fonts work on every target; vector fonts (
Graphics.getVectorFont) are not used because older targets lack them. The glyph sets stay small because sheet size costs memory on 128 KB watches. - Line heights of the sheets (px), from the generated
.fntfiles:
| Family | sog | numerals | header |
|---|---|---|---|
| 218×218 | 39 | 22 | 23 |
| 240×240 | 43 | 25 | 24 |
| 260×260 | 47 | 25 | 28 |
| 280×280 | 50 | 28 | 30 |
| 360×360 | 64 | 35 | 39 |
| 390×390 | 69 | 38 | 42 |
| 416×416 | 75 | 39 | 45 |
| 454×454 | 82 | 43 | 49 |
| 466×466 | 83 | 44 | 49 |
- System fonts are the device's
Graphics.FONT_XTINY…FONT_LARGE; their size differs per watch model.
Wear OS¶
- The same face ships as
res/font/roboto_condensed_bold.ttf(and_regular.ttf).Fonts.forWidthbuilds the three custom fonts by scaling the face so its8is 18 / 9 / 8 % of the screen width, and copies the line height and baseline of the BMFont sheets as ratios of the digit height (sog 1.003 / 0.991, numerals 1.051 / 0.978, header 1.350 / 1.047). Vertically centred text therefore lands where it does on Garmin. Sizes scale continuously with the width; there are no families. Fonts are cached per width. - The TTF has every glyph, so the glyph-set rule doesn't bite on Wear OS; ported strings must still follow it so Garmin renders them.
- System fonts are Roboto Condensed at fixed fractions of the width: XTINY 7.5 % bold, TINY 8.5 % bold, SMALL 9.5 % bold, MEDIUM 11 % bold, LARGE 13 % bold; the label font is 7.5 % regular.
- Menus and lists (start list, picker, Controls, settings) are Wear Material 3 components with their own typography (menus.md); only the canvas screens use these fonts.
Apple Watch¶
⏳ Not decided in the port plan.
Layout rules¶
- Positions are fractions of the screen, never fixed pixels:
Theme.x(f)= width × f,Theme.y(f)= height × f (truncated). The dashboard uses its own grid of quarters and fifths (dashboard.md). - Round screens:
Theme.chordHalfWidth(y)gives the half-width of the visible circle at height y, for rows that must fit inside the bezel (used by the Environment view). - Fixed pixel minimums only where a shape would vanish on small screens (e.g. the max-speed ▲ at least 6 px, the zoom badges at least 8 px).
- Text is vertically centred on its y (
TEXT_JUSTIFY_VCENTER) unless a spec says otherwise; horizontal justification is left, centre or right at the given x. - One
Themeis created per track from the first screen's size and shared by every view. - Strings are English literals; the UI is not localised.
Wear OS: Connect IQ drawing on Android¶
All instrument screens, overlays, the pause, sync, pairing and splash screens are drawn on a full-screen Compose canvas (GarminCanvas) through Dc, a port of Connect IQ's Graphics.Dc. The view code is a line-by-line port of the Monkey C view with the same fractions, colours, fonts and justification. Dc keeps Connect IQ semantics:
| Topic | Connect IQ behaviour kept by Dc |
|---|---|
| Coordinates | Pixels from the top-left, same as Garmin |
| Colours | setColor(0xRRGGBB), always opaque; there is no transparent background colour |
| Justification | Flags RIGHT 0, CENTER 1, LEFT 2, VCENTER 4. VCENTER centres the font's line box on y; without it y is the top of the line box |
drawRectangle |
1 px outline drawn inside the box |
| Arcs | Degrees counter-clockwise from 3 o'clock; a clockwise arc runs from start down to end |
getTextWidthInPixels / getFontHeight |
Measured width rounded to whole px / the font's line height |
| Redraw | The canvas redraws when the engine's tick counter changes (1 Hz while recording) |
Wear OS draws everything anti-aliased; Garmin draws lines and polygons without anti-aliasing (only the font sheets are smoothed).
Supported screens¶
Garmin¶
- Round screens only, 9 families from 218×218 to 466×466. The manifest lists 103 products (minimum API level 3.1.0). Base fenix 5 / 5S are not targets (128 KB memory limit); fenix 5 Plus / 5X are.
- Families and display type (from the SDK device definitions):
| Family | Display | Products | Examples |
|---|---|---|---|
| 218×218 | MIP | 5 | Forerunner 255S, vívoactive 4S |
| 240×240 | MIP | 28 | fenix 5 Plus / 5X, fenix 6S / 7S, Forerunner 945, MARQ Gen 1 |
| 260×260 | MIP | 13 | fenix 6 / 7, Forerunner 955 / 255, fenix 8 Solar 47 mm |
| 280×280 | MIP | 9 | fenix 6X / 7X, Enduro, fenix 8 Solar 51 mm |
| 360×360 | AMOLED | 2 | Forerunner 265S, Venu 2S |
| 390×390 | AMOLED | 19 | Venu / Venu 3S, Forerunner 165 / 570 42 mm, Instinct 3 AMOLED 45 mm |
| 416×416 | AMOLED | 12 | epix 2, fenix 8 43 mm, Forerunner 265, Venu 2 |
| 454×454 | AMOLED | 14 | fenix 8 47 mm, Forerunner 965 / 970, Venu 3 |
| 466×466 | AMOLED | 1 | fenix 9 Pro 51 mm |
- MIP (55 products): sunlight-readable, 64 colours; see the rule for saturated colours above.
- AMOLED (48 products): the same screens are drawn; there is no separate AMOLED layout, no burn-in shifting and no low-power layout. The white dashboard lights the whole screen; the other instrument screens and the pause view are black.
Wear OS¶
- Any round Wear OS watch from Wear OS 3 (
minSdk 30); checked on the 384 px emulator. Layout and fonts scale with the screen width. - All Wear OS screens are OLED. While recording, the screen stays on and the low-power look is the low-power layout (live-surfaces.md).
- Watches without a barometer or stem buttons still run the app; the barometer values show
--and the stem shortcuts are absent.
Apple Watch¶
⏳ Planned: watchOS 11+, usable on 41 mm and 49 mm (apple_watch_port_plan.md §2–3).
States and edge cases¶
- Text outside a glyph set (Garmin): empty boxes. Known case:
PAUSEDon the dashboard timer (dashboard.md). - Smallest screen (218 px): the reference for collisions. Every screen spec names its tightest spots for this size.
- Unknown future Garmin family: the base
resources/folder declares no fonts, so without a matchingresources-round-<w>x<h>/folderRez.Fonts.*is missing and the build fails. A new family needs an entry intools/genfont.pyand a manifest product.
Known deviations¶
None.