Commit Graph
136 Commits
Author SHA1 Message Date
JakubandClaude Opus 5.5 c9877e0ab1 feat: a QR code of the Solo manual on every screen
ui-new: Status › System, Enter shows it full screen. L2: Settings › About.
One precomputed 25 x 25 matrix (ui-core/SiteQr.h) for both, so no QR
library; the URL in upper case keeps it at version 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:38:29 +02:00
JakubandClaude Opus 5.5 57cf58424d feat: a node map and a calendar on ui-new, a banner switch on the L2
- Nearby › Map (ui-new): the positioned nodes and you on one view, scaled
  to fit, with north, a scale bar and the selected node's name and
  distance; Left / Right picks a node, Up / Down zooms on it, Enter opens it.
- Clock tools › Calendar (ui-new): the month with today and the alarm's
  days; a tight grid on small screens, Left / Right pages the months.
- L2 Settings: a Message banner switch (on / off) in place of ui-new's
  three-way alert setting.
- Simulator: sim_nodes() puts eight positioned contacts around Kraków.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:38:29 +02:00
JakubandClaude Opus 5.5 d901a58424 refactor: one place each for "in view" and the keyboard's backspace
ui-new's messagesInView() and ui-lvgl's threadInView() hold the
lit-and-unlocked test the viewing checks repeated; the keyboard's three
copies of the backspace call deleteBeforeCursor(). A stale comment on the
cursor-mode hint goes, onDisplayOn() sits after the view helpers it calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:58:52 +02:00
JakubandClaude Opus 5.5 e09a90b325 fix: messages that arrive while asleep or locked stay unread
A conversation left open under the lock screen or a dark display no longer
counts as read: the counters and the lock screen's tally go up, and on
waking (L2: on unlocking) the conversation reopens on its first unread
under the "New" line, a typed draft kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:41:40 +02:00
JakubandClaude Opus 5.5 9ef7d5d981 fix: no flash writes while typing, and a clock that survives a power loss
Contacts and paths are saved a while after the change and never while the
user is active, so a send no longer stutters on the 53 KB contact rewrite
(a path that came back unchanged isn't written at all). The clock is saved
every 30 minutes and restored no earlier than the firmware's build day.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:41:32 +02:00
JakubandClaude Opus 5.5 aaf35351d7 feat: open a conversation on its first unread message, under a "New" line
Both UIs: a channel, DM or room with unread messages opens on the oldest of
them (MessageHistory::firstUnreadChannel / firstUnreadDm, own messages not
counted), a "New" line above it, so it reads down to the newest. Nothing
unread: the newest, as before.

L1: the first unread is selected and scrolled up top with the line in view;
the channel's unread count now counts down as you read towards the newest.
L2: the page holding it, scrolled to the line; an incoming message scrolls
down only when the list is already at its end. Sim hooks: sim_unread (L1),
sim_open("unread") (L2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:20:04 +02:00
JakubandClaude Opus 5.5 38925dde65 fix(l2): a dot per DM send, lists that notice every change, the LED lit only on TX
- A pending DM shows a dot per send, as on the L1. The thread is read from
  the SD archive, which a resend never updated: it does now, and the
  thread's signature and patch state count the attempt.
- Nearby redraws when a node goes live, changes type or the units change;
  the scan popup when a name or the remote SNR arrives.
- The user LED is active high: it was lit all the time and went dark
  while transmitting.
- Sim hooks "dm" and "send" for testing a DM thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:32:54 +02:00
JakubandClaude Opus 5.5 8324d0c6d1 feat(ui-new): message alert setting -- banner, corner envelope or none
Display > Message alert: Normal keeps the sliding banner; Compact shows an
envelope with the unread count beside the battery (in the header line where
there's no status row), blinking for 2 s; Off shows nothing. The blink only
ever brings the next frame earlier, so it no longer holds back the UI's
animations while it runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:32:54 +02:00
JakubandClaude Opus 5.5 6c9186dc54 feat: a message's bytes left, counted as it will be sent, on every screen
The limit is what the send leaves of MAX_TEXT_LEN: a channel post carries
"<node name>: ", a direct message resent more than three times keeps two
bytes spare, and placeholders count as they expand (msgtext::limit/sentLen).
Bytes, not characters. The path and packet header sit outside the payload;
static_asserts keep a full message within the radio frame on the longest path.

From 40 bytes left the count shows: on the L2 as a tag on the field's top
edge, on OLED/e-ink at the right of the preview's last line with the text
wrapped short of it. Input stops at the limit; a send still over (a
placeholder grew) is held. Sim hooks to open a compose with text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:58:01 +02:00
JakubandClaude Sonnet 5.5 cf086678aa fix(ui-lvgl): the USB drive offer waits for the lock screen to go, PIN or not
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 22:47:26 +02:00
JakubandClaude Opus 5.5 b63af9fee1 fix(ui-lvgl): GPS signal chart spans the box from the first bar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:25:27 +02:00
JakubandClaude Opus 5.5 c4217d8632 feat: a channel's scope can follow the default -- Default is the new start value
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:09:22 +02:00
JakubandClaude Opus 5.5 44ccd47a12 feat(ui-lvgl): GPS on three tabs -- Sky, Signal, Details
Each tab a screenful: the sky plot beside the fix; the bars with a fixed
dB-Hz scale and the constellations; and Details with whatever else the
receiver reports (correction, locator, course, DOPs with their rating,
signal averages, NMEA counts). Sim: sim_open("gps").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:44 +02:00
JakubandClaude Opus 5.5 ab767e7ae1 feat(ui-lvgl): charts with a value axis, grid and a finger's readout
Chart.h draws Diagnostics > History, the noise sweep and the trail's height
profile: round values up the left, labels along the bottom, a solid fill
(no transparency) and, under a finger, the sample's value and when / where.
Sim: sim_open("histdemo") / ("profdemo") fill them with made-up data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:44 +02:00
JakubandClaude Opus 5.5 1f2cb301e6 feat(ui-lvgl): the big clock in the splash's lettering, as on L1
Home and the lock screen draw HH:MM in ui-core/Lettering.h's block digits,
the seconds smaller on the same baseline, leaning smoothly per screen row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:22:43 +02:00
JakubandClaude Opus 5.5 1b7402a33e style(ui-lvgl): selected / on in the full accent, no ACCENT_DIM
ACCENT_DIM (the accent mixed toward black) came out brown on selected
tabs, chips, segments and own bubbles. They take the accent with dark
text now, as primary actions and the quick panel tiles do:
styleSelected() sets the fill, the pressed shade and every label under
it. Own bubbles: dark text, age and delivery mark (green or red can't be
read on amber); the melody editor's selected note is outlined, the
sounding one filled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:35:46 +02:00
JakubandClaude Opus 5.5 a5956d79ae fix(ui-lvgl): quick panel over a frozen screen; slider not taken by Home's swipe
- Quick panel: the screen under it is frozen and dimmed in one snapshot
  (freeze::early), so the slide redraws an image and the panel; a tap or
  swipe up under it closes it and nothing under it takes a touch. Tiles are
  two pages of four with dots; tiles restyle only when their state changes.
- Home: a touch that starts on the top layer (the quick panel, the status
  bar) is not a page swipe -- a sideways drag of the brightness slider over
  Home ended the drag and turned the page under it.
- Keyboard: OK and hide don't put the cursor back on a field whose keyboard
  is gone.
- BotScreen: adminRowBox -> centerRow (the sim build broke in 3293ac09).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:17:32 +02:00
Claude bac4c12467 refactor(ui-lvgl): text-measure counters go with the perf tools
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:28:15 +00:00
Claude 2ea0ac4416 refactor(ui-lvgl): the perf tools move out of UITask.cpp into PerfTest.h
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:27:54 +00:00
Claude 8df1abd79b refactor(ui-lvgl): quick-message editor uses navTextEntry (bare mode)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:26:39 +00:00
Claude 3293ac09e3 refactor(ui-lvgl): centerRow and pillButton helpers; document the shared text-entry popup and bubble row
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:24:44 +00:00
Claude 84fd5013b5 refactor: shared text-entry popup (ui-lvgl) and chat bubble row (ui-new); GPS search clock keeps a found fix
- ui-lvgl: navTextEntry()/TextEntry replace eight copies of popup + field +
  keyboard wiring; one byte-capped insert callback instead of two.
- ui-new: drawHeadedBubble()/BubbleMark replace the duplicated DM and channel
  bubble drawing in MessagesScreen.
- GpsSky::halt(): stopping the receiver ends an unfinished search but keeps
  the first-fix time; keyboard cursor blink restarts after a key.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:23:56 +00:00
Claude 997da95ecf fix: GPS search clock stops with the receiver; L2 cursor survives accented keys; lighter, scrolling quick panel
- GPS: stop() resets the sky state so the 'Searching' timer does not run
  while the receiver is off; the status page shows it only when GPS is on.
- L2 keyboard: put the FOCUSED state back after typing a key or picking a
  variant, so the cursor is drawn again after characters like a-ogonek.
- Quick panel: drop the full-screen dim (a blend per frame), so the screen
  below stays interactive; tiles are one sideways-scrolling, snapping row;
  the brightness slider no longer animates and only updates the backlight
  when the value changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeBrxKyHHxWsmttVkUdBrj
2026-10-05 07:15:32 +00:00
JakubandClaude Opus 5.5 b5e4845dae feat(ui-lvgl): quick panel from the status bar; L2 fixes from rc1
The quick panel, as a phone's: pulled down from the status bar (or a tap
on it). Bluetooth, WiFi, GPS, sound, trail and live share switch at a
tap; Advert opens the advert popup (moved here from the Nodes header);
Lock locks the screen; a brightness slider. Holding a tile opens its
settings. Lock and sun glyphs added to the fonts.

Fixes from rc1 testing on the L2:
- GPS status icon: none when GPS is off (the module kept its last fix
  valid, so it stayed green); grey on, green with a fix. ensureGps()
  turns GPS on before trusting that stale fix.
- Screen wake: the panel wakes dark, a frame is drawn, then the
  backlight fades in -- no white flash. Off fades out first.
- Power off / flat battery: the chip wakes every 30 s to look for USB
  power and starts when plugged in (it slept with no wake source before,
  until the reset button). Plugged in when switched off: stays off until
  re-plugged.
- Message ages in a conversation refresh every 2 s, as on ui-new; the
  message popup keeps its age, repeaters heard and delivery up to date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:10:06 +02:00
JakubandClaude Opus 5.5 d66f04ba94 fix(ui-lvgl): OTA sees a release as newer than its release candidates
versionNum() read "v2.0-rc1" as plain 2.0, so a device on a release
candidate never offered the release itself (nor a later candidate). A
"-rcN" suffix now orders before the bare version: v2.0-rc1 < v2.0-rc2 <
v2.0 < v2.0.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:26:06 +02:00
JakubandClaude Opus 5.5 c3782cb8e5 feat(prefs): v2.0 settings file -- the struct as it is, behind a CRC
/prefs holds a header (magic, format version, size, CRC-32), the node's
position and NodePrefs as it is in memory, in place of /new_prefs' two
hand-kept field lists and 0xC0DE00xx schema bumps. A new field goes at the
end of the struct; moving one bumps PREFS_VERSION. sanitize() keeps every
value in range.

The first boot after an update from before 2.0 imports only the first
block of the old file, laid out the same in upstream MeshCore and Solo
(name, position, radio, BLE PIN, scope), then deletes it; everything else
starts from the defaults. Contacts, channels, scopes, waypoints and trails
are untouched.

Gone: the page-order and GPS-interval migrations, the unused
trail_interval_idx / trail_units_idx / repeat_extra_scopes; fav_sort_off
and msg_wake_screen_off become fav_sort and msg_wake (on by default).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:12:18 +02:00
JakubandClaude Opus 5.5 9597e1e2a4 feat(ui-lvgl): e-ink graphics in colour on the L2
Node detail: a card with a compass rose and the hop chain over the info;
the bar keeps Message / Admin / Add, Ping and Navigate, the rest goes to a
"More" group under the info (favourite toggled in place, delete by a second
tap). Nodes: an arrow by each distance. A trail's height profile in Map
tools and a saved trail's popup. Diagnostics > History: battery, noise,
SNR and traffic charts. Clock > Calendar: today, the weekend and alarm days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:26:29 +02:00
JakubandClaude Opus 5.5 0bbf2fa2b3 feat(trail): record altitude in trail points
Each point keeps the GPS altitude in what was padding, so a point stays
16 bytes. The save format goes to version 2; version 1 files still load,
with the altitude unknown. GPX export writes <ele>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:24:36 +02:00
JakubandClaude Opus 5.5 3631e0ab85 fix(l2): no Lock clock row in Settings
The Big / Compact lock look is ui-new's; the shared schema row did nothing
on the L2's slide-to-unlock card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:23:15 +02:00
JakubandClaude Opus 5.5 687a31e657 refactor(ui): remove dead code and fix stale comments after the review
- ui-core: Diagnostics::systemLines, NearbyModel::typeShort,
  rptctl::currentPresetName, RoomSessions::pendingKey, settings::isSwitch,
  UiCore::reconcileDMUnread.
- ui-new: the retired Recent page, fmtAlt, the battMvToPercent wrapper,
  setFavouriteChannelSlot, AccordionList onHeader/visibleCount, drawBoxedIcon
  and seven unused icons; info::row folded into info::valueRow.
- ui-lvgl: onOpenNav, TileDownloader::withTrails; the trail header size is
  one constant (TRAIL_HDR) for live and saved trails.
- Home quick panels save prefs only when a value changed.
- Comments brought in line with the code (status bar reserve, settings rows,
  page order, SH1106 frame skip, UiCore header).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:49:26 +02:00
JakubandClaude Opus 5.5 096b8e2b1d feat(ui): drop echo toasts for actions the screen already shows
L1: Counters reset, Trail reset, Contact added/deleted, Logged in (admin),
Logged out, Channel deleted, Share target / Bot channel / Bot room set,
PIN set/cleared, Preset saved/deleted, and Unpinned on the favourites grid.
L2: Channel deleted, Counters reset, Region/Preset/Scope/Waypoint deleted,
Target cleared, Trail cleared, Message history deleted, Contact added/deleted.
Failures and Unpinned where the item leaves the screen stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:49:26 +02:00
JakubandClaude Opus 5.5 61c75946f6 feat(l2,core): battery curve editor and an L2 feedback round
- Battery curve: NodePrefs batt_curve_mv[11] (voltage at 0..100 %, all
  zeros = built-in LiPo curve, sentinel 0xC0DE0032). ui-core/Battery.h
  uses it for every percentage. L1: Settings > System > Batt curve list
  (Left/Right 10 mV, Reset); L2: Settings > Power > Battery curve chart
  with draggable points and a line at the cell's voltage.
- L2: the Nodes map is the full navigation map plus the nodes, Back
  returns to Nodes; the favourite button is lit while it is one; pinning
  to the Home dial uses a thumbtack (fonts regenerated); minimap dots
  follow the accent; arranging tiles shows where they land while dragging;
  emoji row in Diagnostics > Font.
- Sim hooks: sim_open nodesmap / accent@N / homeedit / battcurve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:25:58 +02:00
JakubandClaude Opus 5.5 608231ed6b feat(l1): L2 splash, hovering Home icons, soft selections, two-line DM bubbles
- Splash: the L2 wordmark and lettering, shared through ui-core/Lettering.h,
  rise in with loading dots under the version line (static frame on e-ink).
- Home: the header shows the time once the clock is set; icon pages carry
  only their title. One 32 px icon family (3 px strokes) bobs 2 px over a
  dithered blob shadow that shrinks as it rises; the unread badge rides on
  the Messages bubble; Bluetooth off is the icon at half density.
- Soft corners on every selection, the page pill, tabs and popups;
  dotted scrollbar track with a rounded thumb; the tab bar lays out whole
  tabs and shows edge arrows only in free space.
- DM bubbles drop the sender row and wrap to a second line, with the
  delivery mark and age closing it.
- Keyboards name what is typed (channel, contact, Password, Scope...)
  and show characters left near the limit; PIN entry has its own preview.
- Loading dots for waits (login, scan, GPS) and signal bars in place of
  raw RSSI in Nearby.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:59:44 +02:00
JakubandClaude Opus 5.5 c8e1f4a604 perf(l2): long lists build only the rows on screen
New message, Nodes (and its Scan popup), Admin and Messages show their
items through one virtual list: a screenful of rows, handed to the next
item as one scrolls out, the scroll extent from the items' heights. 256
contacts: 522 objects and ~950 ms of filling in (frames up to 96 ms)
before, 19 objects at once now; a scrolled frame 28.5 -> 11.7 ms. Nodes
257 -> 49 objects. A new message rewrites Messages' rows in place
(46 -> 20 ms) instead of building the screen again; it's rebuilt only
when "Read all" comes or goes. A held row keeps what it shows until let
go. Perf tools: -D PERF_LISTS, -D PERF_LIST_MUL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:56:14 +02:00
JakubandClaude Opus 5.5 71e3dd8a8b perf(l2): Nodes slides in smoothly, Diagnostics opens in half the time
Rows still to build wait while a screen slides in, so its frames aren't
a fill budget apart (Nodes: 12 frames of 24 ms instead of 7 of 33, up to
65). The crash dump in flash is read at boot, not on the first
Diagnostics (build 54 -> 20 ms). The perf tools time Nodes too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:56:14 +02:00
JakubandClaude Opus 5.5 0a07dc3deb perf(l2): screen sent by DMA while the next part is drawn
The panel takes each 24-line band by DMA while LVGL draws the next one
into the other buffer (two 15 KB buffers in internal RAM; LovyanGFX's
4092-byte DMA descriptors leave PSRAM unaligned for the S3's DMA, which
showed as shifted and smeared bands). LVGL draws in the panel's byte
order, and so do map tiles and the frozen screen under a popup, so
nothing is swapped on the way out. Full frames ~10-25% faster (page1
19.9 -> 14.7 ms, Home 21.4 -> 18.2 ms). Without the internal RAM the
screen is sent as before. The arduino2 env keeps the old way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:56:13 +02:00
JakubandClaude Opus 5.5 ca5e66a4a1 feat(l2): tidier map overlays: activities top centre, credit + scale + zoom in one pill
Recording / live share and the download stack in a column at the top
centre, empty when nothing runs; the recording pill is plain text with
only its mark coloured (red stop, accent pause / live) -- all-red text
was hard to read over the map. The credit, scale bar and zoom share one
pill bottom left (a tap shows the credit). Every map pill is 22 px with
round ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:40:15 +02:00
JakubandClaude Opus 5.5 1125e9a13e perf(l2): choice popups open smoothly, switches never cut short by a rebuild
A popup over a still screen is frozen and dimmed as it opens, so the
panel's rise redraws only the panel: 6-8 frames of 19-25 ms instead of
3-4 of 45-65 ms. Picker options are one label each (time zone list
97 -> 52 ms to build). The bottom popup no longer animates a dim of 0.

Repeater updates its hint in place (130 ms rebuild gone); Auto power,
Imperial units and Relay on rebuild the screen after the knob has
moved (rebuildSoon). -D PERF_CONTROLS times every control on the
settings screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:27:48 +02:00
JakubandClaude Opus 5.5 910a7c0197 fix(l2): map download no longer stalls on busy tile servers
A 404/503 answer (OpenTopoMap renders on demand) went to an immediate
2 s retry and counted as network loss, so each tile then waited up to
60 s. Answered errors now go straight to the retry list; long pauses
only when there is no answer at all. Three retry rounds (20/60/120 s)
for the tiles still missing, transfers cut after 30 s, failures logged
on serial.

Download popup tidied: title + floating close, percentage instead of
tile counts (also on the map pill), no dead zoom row, no finish toast
after Stop or while the popup shows the result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:54:09 +02:00
JakubandClaude Opus 5.5 f77c7ea901 perf(l2): screens lay out in up to half the time: row widths known before the first layout
A group row's text column (and an info row's key) grows into what the
controls on its right leave, and LVGL lays out children before their
parent, so its labels were first measured 1 px wide; LVGL also works out a
new label's height at its old width (0) before the real one. Each height
that changed then took another pass up the screen: 4-6 passes per screen.

rowsFit(), run before a frame's layout (LV_EVENT_REFR_START) and by
layoutNow() in place of every explicit lv_obj_update_layout(), gives each
new row's growing child the width the flex will give it, worked out from
the other children's styles and text, and labelFit() measures wrapping
labels at their real width up front. The grow stays, so a control that
can't be sized ahead only falls back to the old passes.

Layout on the device: Display page 34 -> 24 ms, Bot 33 -> 18, Quick
37 -> 22, Radio 26 -> 18, Diagnostics 20 -> 12. Sim screenshots of 26
screens unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:30:27 +02:00
JakubandClaude Opus 5.5 634294ee3a perf(l2): Clock opens in half the time: own number wheel instead of lv_roller
An infinite lv_roller measures its whole option text (repeated three
times) on every style change, layout pass and frame. The hand-drawn wheel
draws only the visible rows, with the same look, drag, throw, tap and
wrap-around. Clock first frame 99 -> 54 ms (layout 42 -> 12 ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:55:24 +02:00
JakubandClaude Opus 5.5 2f3b95adb4 fix(l2): the GPS switch no longer jumps
UiCore::setGpsEnabled() wrote the prefs to flash at once, which stalled
the loop mid-animation. It takes save = false now (default true, the L1
unchanged); the L2 marks the prefs changed and writes them on leaving
the screen, as its other switches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:23:04 +02:00
JakubandClaude Opus 5.5 b53b0e1349 feat(l2): vector map off unless built with -D MAP_VECTOR
The vector map isn't finished: without the flag it stays off whatever
was saved, and its switch and the Vector regions row are gone from the
map tools. The code stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:23:04 +02:00
JakubandClaude Opus 5.5 1ff58d4a81 perf(l2): no redraws while nothing changes
Found by counting flushes on idle screens in the sim:
- The compose field's cursor blinked (a redraw every 400 ms) as soon as
  a conversation opened, the keyboard hidden: lv_keyboard_set_textarea()
  focuses the field, and LVGL only stops a blink when the cursor moves.
  fieldFocus() sets the state and nudges the blink; also used for the
  WiFi and channel fields, which left theirs running.
- lv_obj_align_to() restyles on every call, so the status bar's icons
  redrew every second; placeLeftOf() moves them only when they move.
- Text colours set every second (nav bar, GPS status, Diag errors) go
  through setTextColor(), which skips an unchanged colour.
- A LV_LABEL_LONG_DOT label writes its "..." into its own text, so
  setText() never matched and rewrote it every refresh (the nav bar's
  hint); labelShows() reads past the dots.
- The map area, the area-outline, trail and vector-label layers were
  invalidated on every map refresh (every 3 s, a full-map redraw); now
  only when the view or what they draw changed.

Idle map: 17 -> 1 flush in 5 s; list and settings screens: 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:22:31 +02:00
JakubandClaude Opus 5.5 74efb6addd perf(l2): the Quick-message editor freezes the screen under it
The editor has its own 80 % dim layer, not a popupOpen() one, so every
key press redrew the list under it and blended the dim over it. It now
freezes the screen like the popups (freeze::now); the frozen image takes
the dim layer's own level instead of assuming 60 %.

Keyboard redraw 38.7 -> 29.7 ms, full frame 46.3 -> 39.7 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:21:59 +02:00
JakubandClaude Opus 5.5 01f35afa38 perf(l2): nothing drawn under the map tiles
- The map screen's own fill is only painted where the map area doesn't
  cover it (the strip it uncovers as it drifts in); the area and the
  opaque status bar hide the rest. ~2 ms a frame.
- With every visible cell holding a tile, the map area drops its fill and
  grid, which lie under the tiles anyway, and still reports that it
  covers what's below (coversOwnArea, shared with the frozen popup
  image). The Home minimap drops them the same way. ~5 ms a frame.

Full map frame ~50 -> 46-48 ms on the L2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:21:26 +02:00
JakubandClaude Opus 5.5 31cbb1a91a perf(l2): UI_PERF_TEST draw profiles and layout counters
Kept for the optimisation pass before a release:
- perfDrawProfile(): a full redraw timed per object (its own drawing),
  totals by kind and the costliest listed -- on settings pages, Contacts,
  Home, the map and under a frozen popup; the screen's entrance frames too.
- Per screen: build, layout, first draw and redraw apart.
- A popup over Home: a full frame dimmed live and frozen.
- The reset reason and a core dump summary at start.
- -D PERF_WRAP_TEXT with -Wl,--wrap= of lv_text_get_size_attributes,
  lv_layout_apply, lv_obj_get_style_prop_internal and lv_obj_send_event:
  text measured, flex runs, style reads and events per screen.
- -D PERF_KEEP_USB leaves the USB popup up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:33:32 +02:00
JakubandClaude Opus 5.5 f70019f920 perf(l2): the screen under a popup is frozen, not redrawn
Once a popup's dim is up, the screen below is drawn once into an image
(LV_USE_SNAPSHOT), darkened as the dim layer darkens it, and shown opaque
over the screen with the dim layer cleared. LVGL then starts drawing from
that image; an lv_image with no background never reports that it covers,
so it answers the cover check itself. A full frame under a popup: 45.7 ->
32.6 ms; freezing costs ~29 ms once, as the popup settles.

One popup at a time (a second one dims live), a strip keeps the status
bar dimmed under top-layer popups, the image goes with its popup, and the
popup dims live again if its screen goes first. POP_BOTTOM (over a live
map) isn't frozen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:33:32 +02:00
JakubandClaude Opus 5.5 36c647cd05 perf(l2): cards, rows and notes get pixel widths up front
LVGL lays out children before their parent, and a new box's size is only
known after a pass, so every level sized in % of the one above cost a whole
extra layout pass over the screen. innerW() works the width out from the
styles instead; fillWidth() sets it on infoCard, infoLine (every row),
noteLabel and scrollList, and the screen body is px-wide.

Display & power page: 101 -> 76 flex runs, layout 68 -> 54 ms, first
frame 161 -> 146 ms; the other settings pages and Radio / Bot / Quick
alike. Four passes remain: a row's text column grows beside its control,
so its width is only known after the row's own layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:32:33 +02:00
JakubandClaude Opus 5.5 abbbdc5ae5 perf(l2): cheaper frames -- less overdraw and blending
Measured on the L2 (steady full frames): Settings page 37.7 -> 31.4 ms,
Display 41.5 -> 37.9, Contacts 34.6 -> 32.9, Home 37.3 -> 32.4.

- styleSurface() leaves boxes in the page colour unpainted; styleOpaque()
  for the screen and the status bar, which things slide under.
- group() cards clip their corners only while a row is held
  (rowPressable(), shared by the tappable rows).
- rowSlider(): the track an opaque tint (theme::mix(), also behind
  ACCENT_DIM) instead of 20 % accent blended every frame.
- Map buttons, pills, scale, nav bar and the Home minimap labels opaque.
- Screens drift in without the fading cover: its full-screen blend was in
  every entrance frame (page 39 -> 29 ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:30:45 +02:00