13 Commits
Author SHA1 Message Date
Jakub 6d1e71cd3f fix(ui): one rule for value rows, one meaning for Hold Enter
Three interaction inconsistencies found while auditing the favourites work,
all of the same shape: the same gesture meaning different things depending
on which screen you were on.

Value rows in popup menus. Rows like "Notif: ON" or "Sort: Dist" show a
value the user steps through with LEFT/RIGHT, but Enter treated them as
ordinary menu picks and closed the popup, so changing two of them meant
reopening the menu in between. Trail's settings submenu was the lone
exception, working around it by rebuilding and re-selecting after each
Enter. PopupMenu now knows the difference: addValueItem() marks a row, and
Enter on it returns the new VALUE_NEXT instead of SELECTED, leaving the
menu open. Only Back closes a menu now. Applied to the Messages
contact/room/channel menus, Nodes, the Ringtone editor and Trail, which
drops its reopenSettingsAt() workaround. The LEFT/RIGHT cycling bodies
moved into one helper per menu, since Enter and RIGHT now share them.

Nodes' Fav row was the worst case: LEFT/RIGHT did nothing there at all, so
the only way to toggle a favourite was an Enter that dismissed the menu on
every flip. Its label moved to a member buffer (as the Pin row already had)
so it can be retitled in place.

Settings rows Auto-off, Low battery, GPS pwr and Battery ignored Enter,
though their options wrap exactly like the melody/keyboard/clock rows
beside them, where Enter has always stepped forward. They accept it now.
Rows that ramp between fixed ends (Brightness, Volume, TX Pwr, Timezone,
SF/BW/CR) stay LEFT/RIGHT-only -- there is nothing to wrap to.

Hold Enter no longer doubles as Back. It quietly meant "go back" on Tools,
Locator, Live Share, Repeater, Bot, Auto-Advert, GPIO, Compass, the
Dashboard config and the Messages navigate view, while elsewhere the same
long press opens a context menu. It now only ever opens a menu, or does
nothing where there is none. Same for dismissing an open popup, which it
used to do. Checked that this strands nobody: every board that can reach
these screens has a real Back key (back_btn on joystick boards, Esc on
CardKB/TCA8418/T-Deck). Single-button boards produce no KEY_ENTER at all,
so they never leave the home pages in the first place.
2026-08-31 15:43:10 +02:00
MarekZegare4andClaude Opus 4.8 eedd47d1e1 refactor(companion): hoist screen entry into virtual UIScreen::onShow()
Replace the ad-hoc enter()/markClean() entry methods (which lived outside
the UIScreen interface and were invoked via casts from each gotoX) with a
virtual onShow() lifecycle hook called centrally by setCurrScreen().

This removes the "forgot to call enter() in a new navigator" footgun and
the unchecked cast smell: 12 navigators collapse to one-line
setCurrScreen(x) calls, and override enforces signature match. Two entries
that carry a parameter/variant keep an explicit typed call after
setCurrScreen(): RingtoneEditor::selectSlot(slot) and TrailScreen::showMapView().

Behaviour-preserving: only screens that previously had enter() get an
onShow() override; Splash/Home/QuickMsg/Diag keep no reset as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 18:37:04 +02:00
JakubandClaude Sonnet 4.6 2560ec4cef fix(ui): scale compass tape ticks/pointer for e-ink
Tick marks, the scale baseline and the travel-direction pointer triangle
were hardcoded pixel constants, so they shrank to near-invisible slivers
next to the 2x cardinal labels and numeric readout on landscape e-ink.
Same fix pattern as the trail map / keyboard separator: derive sizes from
miniIconScale()/sepH() instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:00:14 +02:00
JakubandClaude Opus 4.8 2136e959d4 fix(ui): gap below header separator, one method everywhere
Content drawn under the title separator touched the line on every standard
screen, because listStart() == headerH() (the row right after the separator).
Graphical screens worked around it with a hand-rolled hdr+2.

Bake a 2px breathing gap into listStart() so every list gains it at once, and
switch the screens that hand-rolled the offset (Compass, Nav, Nearby detail) to
listStart() so the content top is computed the same way everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:35:51 +02:00
JakubandClaude Opus 4.8 9da730e3b6 refactor(ui): shared drawList + header + key-decode helpers
- drawList(): scrollable list skeleton (visible window, keep-sel-in-view,
  scroll-column reserve, per-row callback, indicator). Migrate Tools, Bot,
  Nearby, Waypoints list and the three QuickMsg lists onto it; centralises the
  scroll-clamp/reserve/indicator that each had open-coded (and inconsistently).
- drawCenteredHeader(): centred title + separator, replacing the duplicated
  two-liner across ~10 screens (incl. QuickMsg, which used width()/2 spacing and
  was missed before); unifies the separator at headerH()-sepH().
- keyIsPrev()/keyIsNext() in UIScreen.h for the LEFT||PREV / RIGHT||NEXT idiom;
  applied to 6 screens, behaviour-preserving. Cancel/context-menu stay
  per-screen (they mean different things on different screens).
- rotLabel() dedups the 0/90/180/270 array in Settings.
- Unify selection-row height to lineStep()-1 (Tools/Bot matched the rest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:23:00 +02:00
JakubandClaude Opus 4.8 ad89104599 feat(compass): replace dial with a horizontal heading tape
The shrunken ring left only a few-pixel needle in the OLED's short vertical
space. Switch to a linear heading tape: a fixed travel-direction pointer at
centre over a scrolling N..E..S..W scale (30° ticks, N/E/S/W labelled),
with the large degrees+cardinal readout below. The tape is purely linear
(no trig) and uses the available width, so it stays readable on the small
panel. Update the Compass docs to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 10:28:53 +02:00
JakubandClaude Opus 4.8 0f2399009b fix(compass): shrink ring so rotating cardinals sit outside it
The N/E/S/W letters rode on the ring radius, so they overlapped the circle
as the card rotated. Place the letters on an outer radius and draw the ring
a glyph-height smaller, leaving a clear gap between them. The forward index
now sits in that gap, just outside the ring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 09:57:03 +02:00
JakubandClaude Opus 4.8 3bcb94ec10 fix(compass): move "move to set heading" hint below the ring
The no-heading hint was centred on the compass circle, overlapping the
ring. Draw it in the bottom readout band instead so the ring stays clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 09:37:37 +02:00
JakubandClaude Opus 4.8 a5d55a38b9 refactor(ui): share Bresenham line + midpoint circle in GfxUtils.h
drawLine was duplicated verbatim in TrailScreen and CompassScreen, and
drawCircle lived only in CompassScreen. Lift both into a gfx:: namespace
header so the map and compass share one copy of each 1-px primitive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:13:44 +02:00
JakubandClaude Opus 4.8 55d164c074 refactor(nav): single GPS-position accessor + reset COG window after a gap
UITask::currentLocation() becomes the one place that reads the
LocationProvider fix. TrailScreen::ownPos, CompassScreen::gpsValid and
NearbyScreen::refresh now delegate to it instead of each duplicating the
getLocationProvider/isValid dance (and the ENV_INCLUDE_GPS guard).

pushCogFix now restarts the course-over-ground ring when the gap since the
previous fix exceeds 15 s: after losing GPS for a while, a window spanning
the stale old fixes would compute a bogus "teleport" heading. The
last-good heading is kept for display continuity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:12:15 +02:00
JakubandClaude Opus 4.8 288a6195da feat(ui/compass): draw the ring as a circle, not a square
Replace the bounding-box stand-in with a midpoint-circle ring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 16:54:30 +02:00
JakubandClaude Opus 4.8 98ccf13336 feat(nav): heads-up compass + always-available Trail-start waypoint
Compass: switch to a heads-up card. The direction of travel is now the
fixed "up" index at the top and the compass card rotates underneath it —
a North needle plus N/E/S/W letters placed at screen angle (bearing - cog),
so North spins as you turn rather than the arrow pointing at your course.

Trail waypoints: the nav list now always starts with a synthetic
"Trail start" row whenever a trail exists, so you can navigate back to
where you began without having marked it. The "Waypoints" action appears
when there are saved waypoints OR a trail to backtrack to. Rename/Delete
apply only to saved waypoints; the start row is navigate-only. List/nav
indexing goes through rowTarget()/wpIndex() helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 16:51:40 +02:00
JakubandClaude Opus 4.8 8bf050ddb4 feat(nav): standalone Compass in Tools
Tools › Compass shows the device's course over ground (from the GPS COG
ring — no magnetometer) as a rotating arrow in a ring with a north tick,
plus a large degrees + cardinal readout. "No GPS fix" without a fix;
"move to set heading" when stationary (course undefined). Reuses
UITask::currentCourse, so it works without recording a trail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:07:53 +02:00