feat(ui-lvgl): navigation map, clock tools, settings pages, radio

- Home > Map is the navigation map: waypoints (list, add at GPS / by
  coordinates / long-press spot), live shares, trail recording + tools
  (save, load, GPX export, track back), live share controls, target bar
  with ETA; the nodes map opens from Nearby.
- Map: smooth panning (no decode during a drag, idle read-ahead, cached
  coarser stand-ins), overzoom limited to 2 levels, resumable downloads,
  map credit behind a "©" and in Settings > About; target ring and trail
  computed in double.
- Home in swipeable pages; Clock (alarm, timer, stopwatch, ring card).
- Settings rendered from the Core schema (display & power with a
  brightness slider, messages & contacts, trail / live share / alerts),
  GPS switch, Radio screen; low-battery shutdown.
- WioTrackerL2Display: honour the 0-4 brightness level.
- docs: L2 hardware checklist, ui-core notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-25 19:20:42 +02:00
co-authored by Claude Opus 5.5
parent 1cc6a61747
commit 84d8a4df2a
19 changed files with 3510 additions and 399 deletions
+113
View File
@@ -0,0 +1,113 @@
# Wio Tracker L2 (ui-lvgl) — hardware checklist
Things verified only in the browser simulator so far. Tick on the device;
note anything odd next to the item. Branch `wio-tracker-l2`.
## Feedback round 1 (2026-09-25) — fixed, check on the device
Confirmed on the device: live share sends and receives positions.
- [ ] Map: ☰ tools moved to the left column (back, pin, ☰); nothing overlaps the crosshair
- [ ] Target ring sits exactly on the marker (all markers were drawn ~3 px low)
- [ ] A person sharing on a channel, picked as target, is followed as they move (was a fixed point where they were); bar shows the person icon
- [ ] Settings: "Trail, live share, alerts" subtitle no longer covers the title (all list rows)
- [ ] Map credit is a small "©" (tap: full line in a toast); full text also in Settings > About; long toasts wrap
- [ ] Home: Messages, Nearby, Map, Settings — "Map" is the navigation map; the nodes map opens from Nearby's header (map icon), back returns to Nearby
- [ ] Home tiles are wider; more apps will go on further swipeable pages (dots appear once there are two)
- [ ] Overzoom limited to 2 levels (x4); past that "No map detail here at this zoom" — download more
- [ ] Panning: no tile decoding while the finger moves (smooth drag, tiles fill in once you stop); tiles around the view are decoded ahead while idle; newly revealed area shows the coarser tile until the sharp one is ready
- [ ] Note how panning feels now: ______ (next step if still slow: decode on the second core / JPEG tiles)
## Feedback round 2 (2026-09-25) — fixed, check on the device
Confirmed: settings rows OK, map pans much faster, ring centred.
- [ ] Target ring is wider (30 px) so the green / flag marker shows inside it; a target with no marker on it (map point, message position, stale live share) gets an orange centre dot
- [ ] "↓ Resume?" pill sits top right beside +, clear of the zoom pill; tapping it opens the download popup
- [ ] Pin button → Waypoints list (distance, tap → Go / Name / Share / Delete) with "Here (GPS)" and "Coordinates"
- [ ] Coordinates: digits keyboard, "50.06142, 19.93721 Name" or with a space instead of the comma; bad input keeps the field with a hint; the map centres on the new waypoint
- [ ] Holding the map no longer adds a waypoint straight away: "This spot" popup with Add waypoint / Go here
## Feedback round 3 (2026-09-25) — fixed, check on the device
Confirmed: green dot inside the ring, waypoints much better, download works, settings OK.
- [ ] Ring exactly on the marker at every zoom (it was computed in float: pixels off at z16+). The trail line had the same error and is fixed too
- [ ] Home: swipe left / right anywhere (clock or tiles) turns the page; a swipe starting on a tile doesn't open it; tapping a dot also switches
- [ ] Settings > Display & power: Brightness is a slider (5-100 %), changes live while dragging, kept after a reboot and after the screen sleeps / wakes
- [ ] After the update: prefs load normally (new field, schema sentinel 0xC0DE002F) — brightness starts from the old level, nothing else reset
## New: Clock, settings pages, radio (2026-09-25)
- [ ] Home has two pages now: swipe left → Clock (dots under the tiles follow; Home remembers the page)
- [ ] Clock > Alarm: hour / minute rollers (setting a time arms it), On switch, Repeat (Once / Daily / Weekdays / Weekends); rings at the local time
- [ ] Clock > Timer: H / M / S rollers, Start → big countdown, Stop; when it ends a full-screen "Timer done" card with Dismiss appears on any screen (also wakes the display; the user button dismisses too). Silent: no speaker driver yet
- [ ] Clock > Stopwatch: Start / Stop / Reset with tenths
- [ ] Settings > Display & power: Screen off after; Wake on message
- [ ] Settings > Display & power: Battery shutdown (not while on USB), GPS power saving, Time zone (clock + alarm follow)
- [ ] Settings > Messages & contacts: Resend direct messages, Contact expiry + "Remove inactive contacts now" (first tap shows the count, second removes; favourites kept), Favourites first
- [ ] Settings > Radio: Preset list (built-ins + your saved ones), Frequency (tap, type), SF, Bandwidth, Coding rate, TX power, Auto power; changes apply at once — check messages still flow after switching back to your usual preset
- [ ] L1 (ui-new): Settings > Radio TX power / Auto power / presets still apply (now through the shared RadioControl)
## Basics after the_mesh moved to PSRAM (`MESH_IN_PSRAM`)
- [ ] Boots normally, contacts and channels are all there
- [ ] Messages arrive and send (channel + DM), delivery ticks work
- [ ] Companion app over BLE connects and syncs
- [ ] A few hours of uptime without a reboot
## WiFi map download
- [ ] Settings > WiFi: Scan lists networks, pick one, password, Save
- [ ] Map > download button: popup shows tile count / size, Download starts
- [ ] Pill on the map: "Connecting..." then "↓ n / total"
- [ ] Popup while downloading: IP, dBm, heap (internal free / largest block) — note the numbers: ______
- [ ] Mesh messages still arrive during a download; BLE stays connected
- [ ] Stop → popup shows "Unfinished: z…-…, N tiles" with Resume / trash
- [ ] Power off mid-download → power on → map shows "↓ Resume?" (top right; tap it) → Resume continues (count jumps past the tiles already on the card)
- [ ] Trash discards the unfinished job
- [ ] Download finishes: toast "Map: Done, N new tiles", new tiles appear on the map
## Overzoom (map past the downloaded detail)
- [ ] Zoom in beyond the highest downloaded level: map stays visible (magnified), zoom pill reads e.g. "z17 (map z15)"
- [ ] Panning over magnified tiles is smooth enough (note if it stutters)
- [ ] From a magnified view, download a higher zoom for that area — the sharp tiles replace the magnified ones
## GPS
- [ ] Settings > Navigation shows a "GPS" switch; toggling it turns the module on/off (and survives a reboot)
- [ ] Status bar: GPS icon grey while searching, green with a fix, gone when off
- [ ] Map crosshair with GPS off: turns GPS on ("GPS on, waiting for a fix"), centres once a fix arrives
- [ ] Bot `!gps on` / `!gps off` works on L2
## Two maps
- [ ] Nodes map (Nearby > map icon): contacts with a position as markers, tap → node detail, back → map
- [ ] Map: hold the map → "This spot" → Add waypoint → "WPn" at the finger
- [ ] Map: pin button → Waypoints → Here (GPS) → waypoint at the GPS position
- [ ] Bar (bottom) → "Navigate to" list: waypoints with distance, Trail start (if a trail exists), people sharing live
- [ ] Tap a waypoint → target: ring, dashed line from you, bar with distance / bearing / your course / ETA; view frames you + target
- [ ] ✕ on the bar clears the target
- [ ] Waypoint menu (pencil): Go / Name (Polish letters, 11-byte limit) / Share / Delete (second tap confirms)
- [ ] Share → Messages → pick a conversation → compose holds `[WAY]lat,lon name` → send; a Solo L1 receiving it can save it
- [ ] Settings > Trail, live share, alerts > "Show others' positions" on → someone's `[LOC]` share appears on the Map and in the list; tap → navigate to them
- [ ] Node detail (a node with a position) has the compass button → Map with that node as target
- [ ] Settings > … > "Arrival alert" on + target set → toast "Arrived: …" when you get within the radius
## Positions in messages
- [ ] A received message with `[WAY]lat,lon name`, `[LOC]lat,lon` or plain "lat, lon" shows Go / Save under the bubble
- [ ] Save → waypoint named from the [WAY] label (else the sender), Polish letters not cut in half
- [ ] Go → Map with that spot as the target
## Trail & live share (Map > ☰ tools button, left column)
- [ ] Record → pill "REC 0 m" at the top; walk: blue trail line follows, distance grows
- [ ] Auto-pause (Settings > Trail, live share, alerts): standing still → "PAUSED", walking resumes
- [ ] Stop / Save / Load / Reset (second tap confirms) behave; a trail saved on L2 loads (same /trail file as L1)
- [ ] Export GPX → toast "Saved trails/trail-YYYYMMDD-HHMM.gpx"; the file opens in a GPX viewer (track + waypoints)
- [ ] Live share: pick "Send to" (channel or favourite), Share live → pill "LIVE 1h00"; another node sees the [LOC] updates while you move; stops by itself after the chosen time
- [ ] Send once: with sharing on → "Position sent"; with it off → Messages with `[LOC]lat,lon` waiting in the compose field
- [ ] Track back (with a recorded trail): bar "Back: n pt" with distance / bearing, ring on the breadcrumb; walking advances it; at the start toast "Back at the trail start"; ✕ stops it
- [ ] ☰ > Download this area opens the download popup
- [ ] Waypoint averaging 5 s/10 s/30 s: pin → "Averaging GPS… n s" pill (tap cancels) → waypoint saved at the mean
## Settings > Trail, live share, alerts (schema-driven)
- [ ] Every row changes and survives a reboot; "Imperial units" relabels point spacing and distances
- [ ] Changing "Stop sharing after" during a session restarts its clock
- [ ] Radius / Alert on (arrive, leave, both) change when the arrival toast fires
## Same data on L1 (ui-new) after the Core changes
- [ ] L1: waypoints saved before the update are still listed (Tools › Trail › Waypoints)
- [ ] L1: add / rename / delete waypoint, navigate, ETA line still shows
- [ ] L1: Home GPS toggle still works
- [ ] L1: Tools › Trail Save / Load / Reset still work (now via TrailEngine)
- [ ] L1: Mark with averaging (Trail › Settings › Mark avg) still counts down and marks
- [ ] L1: Track back still advances along the trail and ends at the start
+6 -4
View File
@@ -29,18 +29,18 @@ interleaved. Classified:
**Models — already UI-free, move as-is**
- `MessageHistory.h` — channel (48) / DM (32) rings, unread counters + overflow flags. ✅ moved to `ui-core/`.
- `Trail.h` / `TrailStore`, waypoints (`WaypointsView` storage half), `ScopeList.h`.
- `Trail.h` / `TrailStore`, `ScopeList.h`. Waypoints ✅ `ui-core/WaypointModel.h` (store + `/waypoints` persistence + "delete clears the Locator target"; ui-new's `UITask::waypoints()` forwards to it).
**Engines — logic living inside `UITask.cpp` / `UITask.h`**
- Unread tracking — DM unread table ✅ (`ui-core/DmUnreadTable.h`), room unread (still in `UITask`).
- Notifications ✅ split: the Core decides *what* happened (`MessageArrived` with kind / DM sender / channel slot, `AdvertHeard`); the frontend decides *how* to show it (`showAlert`, `SoundNotifier`, vibration, LED, wake-on-message) — those are platform services.
- Live share ✅ `ui-core/LiveShareEngine.h` — session timer, movement/heartbeat gate, send + scope guard, peers' `LiveTrackStore` + expiry.
- Locator ✅ `ui-core/LocatorEngine.h` — active target, geofence state machine, proximity beeper.
- Trail ✅ `ui-core/TrailEngine.h` — store, sampling, auto-pause, low-battery auto-save.
- Trail ✅ `ui-core/TrailEngine.h` — store, sampling, auto-pause, low-battery auto-save, recording actions (start/stop, reset, save/load `/trail`) used by both frontends. GPS averaging for marks ✅ `ui-core/GpsAverager.h`. Track back ✅ `ui-core/TrackBack.h`.
- Course over ground + current position ✅ `ui-core/CourseEngine.h`.
- Clock tools — alarm / countdown / ring ✅ `ui-core/ClockEngine.h`.
- Ping ✅ `ui-core/PingEngine.h` (timeout still decided by `NearbyScreen`).
- Device controls — GPS on/off, GPIO (`setGpioMode`, bot GPIO), buzzer mode/volume, brightness, radio apply (`applyTxPower`, `applyApc`, `applyRadioParams`, …).
- Device controls — GPS on/off ✅ (`UiCore::setGpsEnabled`, used by ui-new's toggle and ui-lvgl's Settings / map crosshair), GPIO (`setGpioMode`, bot GPIO), buzzer mode/volume, brightness, radio apply (`applyTxPower`, `applyApc`, `applyRadioParams`, …).
- Bot hooks — `botSetGPS`, `botBuzz`, `botSetGPIO`, …
**Screen-embedded logic (harder to extract)**
@@ -110,6 +110,8 @@ call the same ones, so e.g. "marking a channel read" has one implementation.
### 5. Declarative settings schema
`ui-core/SettingsSchema.h` holds three pages -- navigation (trail, live share, arrival alert, units), display & power (brightness, screen off, wake on message, battery shutdown, GPS power saving, time zone) and messages & contacts (DM resends, contact expiry, favourites first). An entry is a one-byte index by default, or a `values` table over a 1/2/4-byte field (seconds, millivolts, an hour offset, `{1, 0}` for a field stored inverted); side effects run through `UiCore` (`applyGpsInterval()`, `host()->applyDisplayPrefs()`). ui-lvgl renders each page; ui-new still uses its own screens. Radio settings are not schema rows (preset list, typed frequency, radio side effects): `ui-core/RadioControl.h` applies them (`applyParams`, `applyTxPower`, `applyApc`, presets = built-ins + user slots) for both ui-new's Settings > Radio and ui-lvgl's Radio screen.
Replaces the per-item code in `SettingsScreen`:
```cpp
@@ -147,7 +149,7 @@ consumes keys only.
- Theme tokens (colours, spacing, typography) in one place — the first chance to realise the "Amber Trace" direction in colour.
- Fonts ✅ Noto Sans generated by `ui-lvgl/fonts/generate.sh` (lv_font_conv): European Latin (Latin-1, Extended-A/B), Greek, Cyrillic + supplement, typographic punctuation, currency, LVGL symbols; 12/14/16/20 px, clock 40 px digits only. On-screen keyboard (`ui-lvgl/Keyboard.h`), phone-style: one layout per script (Latin QWERTY / Cyrillic ЙЦУКЕН / Greek) chosen by the same `keyboard_main_alphabet` / `keyboard_alt_alphabet` prefs as `ui-new` (globe key switches), hold a key for its variants; compose limit counted in UTF-8 bytes. Long-press variants and UTF-8 case mapping are shared with `ui-new` in `ui-core/KeyboardData.h`.
- Simulator ✅: `SIM_UI=lvgl variants/sim/build_wasm.sh` builds the same `ui-lvgl` code for the browser (`SimLcdDisplay` in `variants/sim/SimDisplayDriver.h` blits LVGL's RGB565 flush to a 320×240 canvas; the `SIM_PLATFORM` branch of `ui-lvgl/LvglPort.h` feeds the mouse in as touch). LVGL comes from the L2 env's PlatformIO libdeps; its objects are cached in `web/build/obj_lvgl/`. `web/lvgl.html` runs it next to a `ui-new` peer on a direct link (buttons: advert, peer DM, peer → Public, the board button) and exposes `window.sim` (`tap`, `hold`, `touch`, `peerDM`, `peerChannel`) for headless checks.
- Map (in progress): offline Web-Mercator raster tiles on the microSD (`/maps/{z}/{x}/{y}.png`, the Meshtastic MUI layout; optional per-column `.pak` from PR #3381's packer), SDMMC 1-bit mount in `LvglPort.h`. `ui-lvgl/map/TileProvider.h` is the swap point: the screen and `map/TileCache.h` (12 decoded RGB565 tiles in PSRAM, LRU) only call `renderTile(z, x, y, rgb565)`, so a vector renderer can replace the PNG provider later. `MapScreen.h`: pan by drag, +/-, re-centre/follow GPS, Nearby nodes with a position as markers (tap = node detail), one tile decoded per loop pass. `maps/attribution.txt` is shown on the map. Planned split (user, 2026-09-25): a Nodes map (every contact with a position) and a separate Navigation map (waypoints + live shares only, for walking to a target). Tiles come from `tools/maps/fetch_tiles.py` (no default server: tile.openstreetmap.org blocks offline use) or on the device: the map's download button fetches the visible area (a few overview levels up to a chosen zoom) over WiFi (`map/TileDownloader.h`; skips tiles on the card, rejects non-PNG and placeholder answers; server from `maps/source.txt`, default OpenTopoMap). The HTTP GET runs on a worker task (`LvglPort.h`, keep-alive TLS); WiFi credentials (Settings > WiFi, with scan) live in NVS, and WiFi is on only during a download. Internal RAM is the constraint (Arduino-ESP32 2.0.17 / IDF 4.4: task stacks and, by default, mbedTLS buffers must be internal): with WiFi + Bluedroid up only ~13 KB was left, so the L2 LVGL env sets `MESH_IN_PSRAM` (`the_mesh`, ~120 KB, placement-new into PSRAM in `main.cpp`) and the fetch task points mbedTLS at PSRAM. The download popup shows IP, RSSI, free/largest internal heap and the last TLS/HTTP error. Arduino-ESP32 3.x (IDF 5: PSRAM stacks, NimBLE) is to be evaluated in the polishing phase. In the simulator the same code downloads through emscripten fetch.
- Map (in progress): offline Web-Mercator raster tiles on the microSD (`/maps/{z}/{x}/{y}.png`, the Meshtastic MUI layout; optional per-column `.pak` from PR #3381's packer), SDMMC 1-bit mount in `LvglPort.h`. `ui-lvgl/map/TileProvider.h` is the swap point: the screen and `map/TileCache.h` (24 decoded RGB565 tiles in PSRAM, LRU) only call `renderTile(z, x, y, rgb565)`, so a vector renderer can replace the PNG provider later. `MapScreen.h`: pan by drag, +/-, re-centre/follow GPS, Nearby nodes with a position as markers (tap = node detail), one tile decoded per loop pass, never during a drag (the view fills in once the finger rests; an idle map decodes the ring around the view ahead, and a not-yet-decoded tile shows a cached coarser one meanwhile). `maps/attribution.txt` is behind a "©" on the map and in Settings > About. Two maps over the same tile view: Nearby's header opens the Nodes map (every node Nearby knows a position for), Home > Map the Navigation map (`NavMap.h`): saved waypoints, live shares, the recorded trail and the Locator target with a dashed line to it and a bar with distance / bearing / own course / ETA (`ui-core/EtaTracker.h`, shared with ui-new's NavView). Hold the map to drop a waypoint, the pin marks the GPS position; the bar opens the target list and the waypoint menu (go, rename, share as `[WAY]` into a conversation, delete). Node detail has Navigate (target followed by key); a person sharing on a channel is followed by sender name (Locator target kind 2). Settings > Navigation carries the `track_shared_loc` / `locator_enabled` switches ui-new has on its Live share / Locator screens. Tiles come from `tools/maps/fetch_tiles.py` (no default server: tile.openstreetmap.org blocks offline use) or on the device: the map's download button fetches the visible area (a few overview levels up to a chosen zoom) over WiFi (`map/TileDownloader.h`; skips tiles on the card, rejects non-PNG and placeholder answers; server from `maps/source.txt`, default OpenTopoMap). The HTTP GET runs on a worker task (`LvglPort.h`, keep-alive TLS); WiFi credentials (Settings > WiFi, with scan) live in NVS, and WiFi is on only during a download. The job (area + zoom range) stays in `maps/.job` until it completes, so a download cut short by power-off, lost WiFi or Stop is offered for Resume (tiles already on the card are skipped). Past the downloaded detail the map magnifies the nearest coarser tile it has (up to 2 levels -- more is just big pixels; zoom pill shows e.g. `z17 (map z15)`); known-missing tiles are remembered without a pixel buffer. Internal RAM is the constraint (Arduino-ESP32 2.0.17 / IDF 4.4: task stacks and, by default, mbedTLS buffers must be internal): with WiFi + Bluedroid up only ~13 KB was left, so the L2 LVGL env sets `MESH_IN_PSRAM` (`the_mesh`, ~120 KB, placement-new into PSRAM in `main.cpp`) and the fetch task points mbedTLS at PSRAM. The download popup shows IP, RSSI, free/largest internal heap and the last TLS/HTTP error. Arduino-ESP32 3.x (IDF 5: PSRAM stacks, NimBLE) is to be evaluated in the polishing phase. In the simulator the same code downloads through emscripten fetch.
- PR #3381's LVGL UI is a reference for platform glue (flush, GT911, PSRAM pool) only; its screens are not adopted.
## Migration plan
@@ -0,0 +1,308 @@
#pragma once
// Clock tools (Home > Clock): alarm, countdown timer and stopwatch -- ui-new's
// Tools > Clock on a touch screen. The alarm (NodePrefs alarm_*) and the
// countdown run in the Core's ClockEngine, so they fire on any screen and while
// the display sleeps; the stopwatch is view state here, as in ui-new. Whatever
// fires brings up a full-screen card with Dismiss (silent: the L2 speaker has
// no driver yet).
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp after NavMap.h.
namespace clockview {
enum : uint8_t { TAB_ALARM, TAB_TIMER, TAB_STOPWATCH, TAB_COUNT };
enum : uint8_t { ACT_START_STOP, ACT_RESET };
static uint8_t s_tab = TAB_ALARM;
static uint8_t s_timer_h = 0, s_timer_m = 5, s_timer_s = 0; // countdown to start
static bool s_sw_running = false;
static uint32_t s_sw_start = 0, s_sw_accum = 0;
static bool s_shown_running = false; // timer state the screen was built for
static lv_obj_t* s_big = nullptr; // timer remaining / stopwatch reading
static lv_obj_t* s_go_lbl = nullptr; // Start / Stop
static lv_obj_t* s_ring = nullptr; // "Timer done" card on the top layer
static lv_obj_t* s_ring_lbl = nullptr;
static lv_obj_t* s_alarm_sw = nullptr; // follows a time change (setting it arms the alarm)
static char s_opts24[24 * 3], s_opts60[60 * 3]; // "00\n01\n..." for the rollers
static void buildOptions() {
if (s_opts24[0]) return;
int o = 0;
for (int i = 0; i < 24; i++) o += snprintf(s_opts24 + o, sizeof(s_opts24) - o, i ? "\n%02d" : "%02d", i);
o = 0;
for (int i = 0; i < 60; i++) o += snprintf(s_opts60 + o, sizeof(s_opts60) - o, i ? "\n%02d" : "%02d", i);
}
static uint32_t swElapsed() { return s_sw_accum + (s_sw_running ? millis() - s_sw_start : 0); }
// Countdown: rounds up, so "00:01" shows until it actually fires.
static void fmtRemaining(char* b, int n, uint32_t ms) {
uint32_t t = (ms + 999) / 1000;
if (t >= 3600) snprintf(b, n, "%lu:%02lu:%02lu", (unsigned long)(t / 3600), (unsigned long)(t / 60 % 60), (unsigned long)(t % 60));
else snprintf(b, n, "%02lu:%02lu", (unsigned long)(t / 60), (unsigned long)(t % 60));
}
static void fmtStopwatch(char* b, int n, uint32_t ms) {
uint32_t t = ms / 1000;
if (t >= 3600) snprintf(b, n, "%lu:%02lu:%02lu", (unsigned long)(t / 3600), (unsigned long)(t / 60 % 60), (unsigned long)(t % 60));
else snprintf(b, n, "%02lu:%02lu.%lu", (unsigned long)(t / 60), (unsigned long)(t % 60), (unsigned long)(ms / 100 % 10));
}
static lv_obj_t* roller(lv_obj_t* parent, const char* opts, int sel, lv_event_cb_t cb, uintptr_t which) {
lv_obj_t* r = lv_roller_create(parent);
lv_roller_set_options(r, opts, LV_ROLLER_MODE_INFINITE);
lv_roller_set_selected(r, sel, LV_ANIM_OFF);
lv_obj_set_size(r, 60, 84); // three rows; leaves room for the button below
lv_obj_set_style_text_font(r, THEME_FONT_TITLE, 0);
lv_obj_set_style_text_font(r, THEME_FONT_LARGE, LV_PART_SELECTED);
lv_obj_set_style_text_line_space(r, 4, 0);
lv_obj_set_style_text_color(r, lv_color_hex(theme::TEXT_MUTED), 0);
lv_obj_set_style_bg_color(r, lv_color_hex(theme::SURFACE), 0);
lv_obj_set_style_border_width(r, 0, 0);
lv_obj_set_style_bg_color(r, lv_color_hex(theme::SURFACE_2), LV_PART_SELECTED);
lv_obj_set_style_text_color(r, lv_color_hex(theme::ACCENT), LV_PART_SELECTED);
lv_obj_add_event_cb(r, cb, LV_EVENT_VALUE_CHANGED, (void*)which);
return r;
}
static lv_obj_t* colon(lv_obj_t* parent) { return label(parent, ":", THEME_FONT_LARGE, theme::TEXT_MUTED); }
static lv_obj_t* row(lv_obj_t* parent) {
lv_obj_t* r = lv_obj_create(parent);
styleSurface(r, theme::BG);
lv_obj_remove_flag(r, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(r, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(r, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(r, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(r, theme::GAP, 0);
return r;
}
} // namespace clockview
static void onOpenClock(lv_event_t* e) { (void)e; s_ui->showClock(); }
static void onClockTab(lv_event_t* e) { s_ui->clockTab((int)(uintptr_t)lv_event_get_user_data(e)); }
static void onClockAct(lv_event_t* e) { s_ui->clockAction((uint8_t)(uintptr_t)lv_event_get_user_data(e)); }
static void onRingDismiss(lv_event_t* e) { (void)e; s_ui->dismissRing(); }
// Alarm: hour / minute rollers, on switch, repeat. Saved on every change.
static void onAlarmRoller(lv_event_t* e) {
s_ui->setAlarm((int)(uintptr_t)lv_event_get_user_data(e), (int)lv_roller_get_selected((lv_obj_t*)lv_event_get_target(e)));
}
static void onAlarmSwitch(lv_event_t* e) {
s_ui->setAlarm(2, lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED) ? 1 : 0);
}
static void onAlarmRepeat(lv_event_t* e) {
s_ui->setAlarm(3, (int)lv_dropdown_get_selected((lv_obj_t*)lv_event_get_target(e)));
}
static void onTimerRoller(lv_event_t* e) {
int v = (int)lv_roller_get_selected((lv_obj_t*)lv_event_get_target(e));
switch ((uintptr_t)lv_event_get_user_data(e)) {
case 0: clockview::s_timer_h = (uint8_t)v; break;
case 1: clockview::s_timer_m = (uint8_t)v; break;
default: clockview::s_timer_s = (uint8_t)v; break;
}
}
void UITask::showClock() {
_screen = SCR_CLOCK;
buildClock();
}
void UITask::clockTab(int tab) {
if (tab < 0 || tab >= clockview::TAB_COUNT) return;
clockview::s_tab = (uint8_t)tab;
buildClock();
}
void UITask::buildClock() {
using namespace clockview;
buildOptions();
lv_obj_t* body = newScreen("Clock", true);
lv_obj_set_style_pad_row(body, 6, 0);
lv_obj_remove_flag(body, LV_OBJ_FLAG_SCROLLABLE);
s_big = s_go_lbl = s_alarm_sw = nullptr;
// Tabs
lv_obj_t* tabs = row(body);
static const char* const NAMES[TAB_COUNT] = { LV_SYMBOL_BELL " Alarm", UI_SYMBOL_CLOCK " Timer",
UI_SYMBOL_STOPWATCH " Stopwatch" };
for (int t = 0; t < TAB_COUNT; t++) {
lv_obj_t* b = lv_button_create(tabs);
lv_obj_set_height(b, 32);
lv_obj_set_flex_grow(b, 1);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(t == s_tab ? theme::ACCENT_DIM : theme::SURFACE), 0);
lv_obj_add_event_cb(b, onClockTab, LV_EVENT_CLICKED, (void*)(uintptr_t)t);
lv_obj_center(label(b, NAMES[t], THEME_FONT_SMALL, theme::TEXT));
}
if (s_tab == TAB_ALARM) {
lv_obj_t* r = row(body);
roller(r, s_opts24, _prefs->alarm_hour % 24, onAlarmRoller, 0);
colon(r);
roller(r, s_opts60, _prefs->alarm_min % 60, onAlarmRoller, 1);
lv_obj_t* col = lv_obj_create(r);
styleSurface(col, theme::BG);
lv_obj_remove_flag(col, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(col, 120, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(col, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(col, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_set_style_pad_row(col, 6, 0);
lv_obj_set_style_pad_left(col, 8, 0);
label(col, "Alarm on", THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_obj_t* sw = s_alarm_sw = lv_switch_create(col);
lv_obj_set_size(sw, 50, 26);
if (_prefs->alarm_on) lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_add_event_cb(sw, onAlarmSwitch, LV_EVENT_VALUE_CHANGED, NULL);
label(col, "Repeat", THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_obj_t* dd = lv_dropdown_create(col);
char opts[48];
int o = 0;
for (uint8_t i = 0; i < NodePrefs::ALARM_REPEAT_COUNT; i++)
o += snprintf(opts + o, sizeof(opts) - o, "%s%s", i ? "\n" : "", i ? NodePrefs::alarmRepeatLabel(i) : "Once");
lv_dropdown_set_options(dd, opts);
lv_dropdown_set_selected(dd, NodePrefs::alarmRepeatIdxForMask(_prefs->alarm_repeat_mask));
lv_obj_set_width(dd, 112);
lv_obj_add_event_cb(dd, onAlarmRepeat, LV_EVENT_VALUE_CHANGED, NULL);
bool synced = rtc_clock.getCurrentTime() > 1000000000UL;
label(body, synced ? "Rings at this local time (Settings time zone)." : "The clock isn't set yet: the alarm waits for a time sync.",
THEME_FONT_SMALL, theme::TEXT_MUTED);
return;
}
if (s_tab == TAB_TIMER) {
s_shown_running = _core->clock.isTimerRunning();
if (s_shown_running) {
s_big = label(body, "", THEME_FONT_CLOCK, theme::TEXT);
lv_obj_set_width(s_big, LV_PCT(100));
lv_obj_set_style_text_align(s_big, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_pad_ver(s_big, 18, 0);
} else {
lv_obj_t* r = row(body);
roller(r, s_opts24, s_timer_h, onTimerRoller, 0);
colon(r);
roller(r, s_opts60, s_timer_m, onTimerRoller, 1);
colon(r);
roller(r, s_opts60, s_timer_s, onTimerRoller, 2);
}
} else { // stopwatch
s_big = label(body, "", THEME_FONT_CLOCK, theme::TEXT);
lv_obj_set_width(s_big, LV_PCT(100));
lv_obj_set_style_text_align(s_big, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_pad_ver(s_big, 18, 0);
}
lv_obj_t* acts = row(body);
lv_obj_t* go = lv_button_create(acts);
lv_obj_set_size(go, 150, 40);
lv_obj_set_style_shadow_width(go, 0, 0);
lv_obj_set_style_radius(go, theme::RADIUS, 0);
lv_obj_set_style_bg_color(go, lv_color_hex(theme::ACCENT_DIM), 0);
lv_obj_add_event_cb(go, onClockAct, LV_EVENT_CLICKED, (void*)(uintptr_t)ACT_START_STOP);
s_go_lbl = label(go, "", THEME_FONT_BODY, theme::TEXT);
lv_obj_center(s_go_lbl);
if (s_tab == TAB_STOPWATCH) {
lv_obj_t* rs = lv_button_create(acts);
lv_obj_set_size(rs, 100, 40);
lv_obj_set_style_shadow_width(rs, 0, 0);
lv_obj_set_style_radius(rs, theme::RADIUS, 0);
lv_obj_set_style_bg_color(rs, lv_color_hex(theme::SURFACE), 0);
lv_obj_add_event_cb(rs, onClockAct, LV_EVENT_CLICKED, (void*)(uintptr_t)ACT_RESET);
lv_obj_center(label(rs, LV_SYMBOL_REFRESH " Reset", THEME_FONT_BODY, theme::TEXT));
}
refreshClock();
}
// From loop() while SCR_CLOCK is shown: the running readouts.
void UITask::refreshClock() {
using namespace clockview;
if (s_tab == TAB_TIMER) {
bool running = _core->clock.isTimerRunning();
if (running != s_shown_running) { buildClock(); return; } // started elsewhere / just fired
if (s_big) {
char b[16];
fmtRemaining(b, sizeof(b), _core->clock.timerRemainingMs());
lv_label_set_text(s_big, b);
}
if (s_go_lbl) lv_label_set_text(s_go_lbl, running ? LV_SYMBOL_STOP " Stop" : LV_SYMBOL_PLAY " Start");
} else if (s_tab == TAB_STOPWATCH) {
if (s_big) {
char b[16];
fmtStopwatch(b, sizeof(b), swElapsed());
lv_label_set_text(s_big, b);
}
if (s_go_lbl) lv_label_set_text(s_go_lbl, s_sw_running ? LV_SYMBOL_PAUSE " Stop" : LV_SYMBOL_PLAY " Start");
}
}
void UITask::clockAction(uint8_t act) {
using namespace clockview;
if (s_tab == TAB_TIMER) {
if (_core->clock.isTimerRunning()) _core->clock.stopTimer();
else {
uint32_t ms = ((uint32_t)s_timer_h * 3600 + s_timer_m * 60 + s_timer_s) * 1000UL;
if (ms == 0) { showToast("Set a time first"); return; }
_core->clock.startTimer(ms);
}
buildClock();
} else if (s_tab == TAB_STOPWATCH) {
if (act == ACT_RESET) { s_sw_accum = 0; s_sw_start = millis(); }
else if (s_sw_running) { s_sw_accum += millis() - s_sw_start; s_sw_running = false; }
else { s_sw_start = millis(); s_sw_running = true; }
refreshClock();
}
}
// which: 0 hour, 1 minute, 2 on/off, 3 repeat preset.
void UITask::setAlarm(int which, int v) {
if (!_prefs) return;
switch (which) {
case 0: _prefs->alarm_hour = (uint8_t)v; _prefs->alarm_on = 1; break; // setting a time arms it
case 1: _prefs->alarm_min = (uint8_t)v; _prefs->alarm_on = 1; break;
case 2: _prefs->alarm_on = (uint8_t)v; break;
case 3: _prefs->alarm_repeat_mask = NodePrefs::alarmRepeatMaskForIdx((uint8_t)v); break;
}
_core->clock.onAlarmChanged();
the_mesh.savePrefs();
if (which < 2 && clockview::s_alarm_sw) lv_obj_add_state(clockview::s_alarm_sw, LV_STATE_CHECKED);
}
// ── Ring card (any screen) ────────────────────────────────────────────────────
void UITask::showRing(const char* text) {
using namespace clockview;
wake();
if (!s_ring) {
s_ring = lv_obj_create(lv_layer_top());
lv_obj_remove_style_all(s_ring);
lv_obj_set_size(s_ring, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_color(s_ring, lv_color_hex(theme::BG), 0);
lv_obj_set_style_bg_opa(s_ring, LV_OPA_COVER, 0);
lv_obj_add_flag(s_ring, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_flex_flow(s_ring, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(s_ring, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_row(s_ring, 14, 0);
label(s_ring, LV_SYMBOL_BELL, THEME_FONT_LARGE, theme::ACCENT);
s_ring_lbl = label(s_ring, "", THEME_FONT_LARGE, theme::TEXT);
lv_obj_t* b = lv_button_create(s_ring);
lv_obj_set_size(b, 200, 56);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::ACCENT_DIM), 0);
lv_obj_add_event_cb(b, onRingDismiss, LV_EVENT_CLICKED, NULL);
lv_obj_center(label(b, "Dismiss", THEME_FONT_LARGE, theme::TEXT));
}
lv_label_set_text(s_ring_lbl, text);
lv_obj_remove_flag(s_ring, LV_OBJ_FLAG_HIDDEN);
}
void UITask::dismissRing() {
_core->clock.dismissRing();
hideRing();
}
void UITask::hideRing() {
if (clockview::s_ring) lv_obj_add_flag(clockview::s_ring, LV_OBJ_FLAG_HIDDEN);
if (_screen == SCR_CLOCK) buildClock(); // e.g. the timer's Start again
}
@@ -87,6 +87,12 @@ static bool touched() {
static void swallowTouch() { s_swallow = true; }
// Backlight 1-100 % (the brightness slider), on the LP5814 PWM.
static void setBacklightPct(uint8_t pct) {
if (pct > 100) pct = 100;
s_gfx->setBrightness((uint8_t)(8 + (uint16_t)pct * 247 / 100)); // never fully dark
}
// microSD over SDMMC, 1-bit (CLK 2, CMD 3, D0 1); its power rail (expander
// P14) is switched on in WioTrackerL2Board::begin(). Retried on every call
// until it works, so a card inserted later is picked up the next time.
@@ -328,6 +334,7 @@ static bool begin() {
static bool touched() { return SimLcdDisplay::touchState().down; }
static void swallowTouch() { s_swallow = true; }
static void setBacklightPct(uint8_t pct) { (void)pct; } // the browser canvas has no backlight
// The host page preloads map tiles into the in-memory FS under /sdcard/maps.
static bool mountStorage() { return true; }
+322 -68
View File
@@ -1,12 +1,16 @@
#pragma once
// Map screen: offline Web-Mercator tiles from the card (map/TileProvider.h,
// cached by map/TileCache.h) under own position and the nodes Nearby knows a
// position for. Drag to pan, +/- to zoom, the crosshair re-centres and
// follows the GPS again; tap a marker for its node detail. The download
// button fetches the visible area over WiFi (map/TileDownloader.h).
// cached by map/TileCache.h) under own position. Two modes over the same tile
// view: the Navigation map (Home > Map, NavMap.h) shows waypoints, live
// shares, the trail and the active target; the Nodes map (Nearby > map
// button) marks every node Nearby knows a position for, tap one for its
// detail. Drag to pan, +/- to zoom, the crosshair re-centres and follows the
// GPS again. The download button fetches the visible area over WiFi
// (map/TileDownloader.h).
//
// Tiles decode one per loop pass (a PNG takes tens of ms), so panning stays
// responsive and the radio keeps being serviced while a view fills in.
// Tiles decode one per loop pass (a PNG takes tens of ms), never while a drag
// is moving the map, so panning stays smooth and the radio keeps being
// serviced; an idle map decodes the tiles around the view ahead of time.
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp after the Nearby section.
@@ -36,15 +40,89 @@ static double latToTileY(double lat, int z) {
return (1.0 - asinh(tan(r)) / M_PI) / 2.0 * (double)(1 << z);
}
// Markers: one per positioned Nearby row, remembered so layout can reposition them.
struct Mark { int32_t lat_e6, lon_e6; int row; lv_obj_t* obj; };
static Mark s_marks[NearbyModel::MAX_NEARBY];
// Markers, remembered so layout can reposition them. `idx` is the Nearby row
// (MK_NODE), the waypoint index (MK_WAYPOINT) or the live-share slot (MK_LIVE).
enum : uint8_t { MK_NODE, MK_WAYPOINT, MK_LIVE };
struct Mark { int32_t lat_e6, lon_e6; int idx; uint8_t kind; lv_obj_t* obj; lv_obj_t* dot; };
static const int MAX_MARKS = 48; // >= MAX_NEARBY, and >= waypoints + live shares
static Mark s_marks[MAX_MARKS];
static int s_mark_count = 0;
static int s_drag = 0; // px moved in the current press: a drag isn't a tap
static bool s_available = false; // provider has data; checked when the map opens, not per frame
static double s_left = 0, s_top = 0; // world px (at the current zoom) of the view's top-left
static lv_obj_t* s_cells[GRID_COLS * GRID_ROWS]; // clip each grid tile (a magnified parent overflows)
// Overzoom: a tile the card doesn't have at this zoom is drawn as the matching
// part of the nearest coarser tile it does have, magnified -- so the map stays
// usable a little past the downloaded detail. Only two levels (x4): beyond
// that a raster tile is just big pixels, and the map says to download more.
// Returns that tile (k = levels up) or nullptr; want_z/x/y is the nearest
// coarser tile not looked up yet (decode it next), want_z = -1 if none.
static const int OVERZOOM = 2;
static TileCache::Slot* ancestorFor(int z, int x, int y, int& k, int& want_z, int& want_x, int& want_y) {
want_z = -1;
for (int d = 1; d <= OVERZOOM && z - d >= 0; d++) {
int ax = x >> d, ay = y >> d;
TileCache::Slot* s = s_cache.find(z - d, ax, ay);
if (!s) { want_z = z - d; want_x = ax; want_y = ay; return nullptr; }
if (s->present) { k = d; return s; }
}
return nullptr;
}
// Stand-in while z/x/y hasn't been decoded yet: a coarser tile already in the
// cache (typically the view you just zoomed in from), so a newly revealed part
// of the map shows something at once instead of a blank. Never decodes.
static TileCache::Slot* cachedAncestor(int z, int x, int y, int& k) {
for (int d = 1; d <= 3 && z - d >= 0; d++) {
TileCache::Slot* s = s_cache.find(z - d, x >> d, y >> d);
if (s && s->present) { k = d; return s; }
}
return nullptr;
}
// Decoding a tile holds the loop for tens of ms, which makes a drag stutter.
// So nothing is decoded while the finger is moving the map; the view fills in
// once it rests, and when idle the ring of tiles around the view is decoded
// ahead of the next pan.
static uint32_t s_last_pan_ms = 0;
static const uint32_t PAN_SETTLE_MS = 150, PREFETCH_IDLE_MS = 400;
// Idle read-ahead: decode the nearest not-yet-looked-up tile in the ring just
// outside the view (one per call). False when the ring is complete.
static bool prefetchOne(double left, double top, int w, int h, int z) {
int tx0 = (int)floor(left / TILE_PX), ty0 = (int)floor(top / TILE_PX);
int n = 1 << z;
int bx = 0, by = 0;
double best = 1e18;
for (int j = -1; j <= GRID_ROWS; j++) {
for (int i = -1; i <= GRID_COLS; i++) {
int tx = tx0 + i, ty = ty0 + j;
double px = tx * (double)TILE_PX - left, py = ty * (double)TILE_PX - top;
if (px < w && py < h && px + TILE_PX > 0 && py + TILE_PX > 0) continue; // on screen: not ours
if (ty < 0 || ty >= n) continue;
int wx = ((tx % n) + n) % n;
if (s_cache.find(z, wx, ty)) continue; // also keeps a decoded neighbour from being evicted
double d = fabs(px + TILE_PX / 2 - w / 2.0) + fabs(py + TILE_PX / 2 - h / 2.0);
if (d < best) { best = d; bx = wx; by = ty; }
}
}
if (best >= 1e18) return false;
s_cache.load(*s_provider, z, bx, by);
return true;
}
static const int MARK_D = 12; // marker dot diameter
} // namespace mapview
namespace navmap { // shared with NavMap.h (included after this file)
static const int BAR_H = 44; // target bar along the bottom of the Navigation map
// Navigation targets as one int (event user data): type << 8 | index.
enum : uint8_t { T_CLEAR, T_WAYPOINT, T_TRAILSTART, T_LIVE };
static int code(uint8_t type, int idx) { return (type << 8) | (idx & 0xFF); }
} // namespace navmap
static void onMapPress(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSED) { mapview::s_drag = 0; return; }
@@ -52,8 +130,17 @@ static void onMapPress(lv_event_t* e) {
lv_indev_get_vect(lv_indev_active(), &v);
if (v.x == 0 && v.y == 0) return;
mapview::s_drag += abs(v.x) + abs(v.y);
mapview::s_last_pan_ms = millis();
s_ui->mapPan(v.x, v.y);
}
static void onMapLongPress(lv_event_t* e) {
if (mapview::s_drag > 8) return; // held after a pan: not a long-press on a spot
lv_point_t p;
lv_indev_get_point(lv_indev_active(), &p);
lv_area_t a;
lv_obj_get_coords((lv_obj_t*)lv_event_get_target(e), &a);
s_ui->mapLongPress(p.x - a.x1, p.y - a.y1);
}
static void onMapZoomIn(lv_event_t* e) { (void)e; s_ui->mapZoom(+1); }
static void onMapZoomOut(lv_event_t* e) { (void)e; s_ui->mapZoom(-1); }
static void onMapCenter(lv_event_t* e) { (void)e; s_ui->mapCenterOnMe(); }
@@ -63,6 +150,10 @@ static void onDlStart(lv_event_t* e) { (void)e; s_ui->mapDownloadStart(); }
static void onDlZoomMinus(lv_event_t* e) { (void)e; s_ui->mapDownloadZmax(-1); }
static void onDlZoomPlus(lv_event_t* e) { (void)e; s_ui->mapDownloadZmax(+1); }
static void onDlWifi(lv_event_t* e) { (void)e; s_ui->showWifi(true); }
static void onDlResume(lv_event_t* e) { (void)e; s_ui->mapDownloadResume(); }
static void onDlDiscard(lv_event_t* e) { (void)e; s_ui->mapDownloadDiscard(); }
static void onNavTools(lv_event_t* e); // NavMap.h
static void onMapCredits(lv_event_t* e) { (void)e; s_ui->showToast(mapview::s_provider->attribution(), 4000); }
static void onMapMarker(lv_event_t* e) {
if (mapview::s_drag > 8) return; // the press was a pan that ended on a marker
s_ui->mapOpenMarker((int)(uintptr_t)lv_event_get_user_data(e));
@@ -93,6 +184,17 @@ static lv_obj_t* mapPill(lv_obj_t* parent, const char* text) {
return l;
}
// Centring on a point puts it mid-way down the visible map: above the nav bar
// on the Navigation map (the bias is in tiles, added to the view centre).
double UITask::mapCenterBias() const {
return _map_nav ? navmap::BAR_H / 2.0 / mapview::TILE_PX : 0.0;
}
void UITask::openMap(bool nav) {
_map_nav = nav;
showMap();
}
void UITask::showMap() {
_screen = SCR_MAP;
mapview::s_available = lvport::mountStorage() && mapview::s_provider->available();
@@ -109,6 +211,7 @@ void UITask::showMap() {
_map_cy = mapview::latToTileY(la, z);
}
buildMap();
if (_map_nav && _prefs && _prefs->locator_has_target) navFrameTarget(); // show where we're going
}
void UITask::buildMap() {
@@ -120,11 +223,19 @@ void UITask::buildMap() {
lv_obj_add_flag(body, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(body, onMapPress, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(body, onMapPress, LV_EVENT_PRESSING, NULL);
lv_obj_add_event_cb(body, onMapLongPress, LV_EVENT_LONG_PRESSED, NULL);
_map_area = body;
for (int i = 0; i < mapview::GRID_COLS * mapview::GRID_ROWS; i++) {
_map_tiles[i] = lv_image_create(body);
lv_obj_add_flag(_map_tiles[i], LV_OBJ_FLAG_HIDDEN);
lv_obj_t* cell = lv_obj_create(body);
lv_obj_remove_style_all(cell);
lv_obj_set_size(cell, mapview::TILE_PX, mapview::TILE_PX);
lv_obj_remove_flag(cell, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_flag(cell, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(cell, LV_OBJ_FLAG_HIDDEN);
mapview::s_cells[i] = cell;
_map_tiles[i] = lv_image_create(cell);
lv_image_set_pivot(_map_tiles[i], 0, 0); // magnify from the top-left (overzoom)
}
// Marker layer: same size as the map, lets presses through to it.
@@ -135,6 +246,8 @@ void UITask::buildMap() {
lv_obj_remove_flag(_map_marks, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(_map_marks, LV_OBJ_FLAG_EVENT_BUBBLE);
if (_map_nav) buildNavLayers(); // trail + line to the target, under the markers
_map_me = lv_obj_create(_map_marks);
lv_obj_remove_style_all(_map_me);
lv_obj_set_size(_map_me, 16, 16);
@@ -151,25 +264,38 @@ void UITask::buildMap() {
lv_obj_align(back, LV_ALIGN_TOP_LEFT, 6, 6);
lv_obj_align(mapButton(body, LV_SYMBOL_PLUS, onMapZoomIn), LV_ALIGN_TOP_RIGHT, -6, 6);
lv_obj_align(mapButton(body, LV_SYMBOL_MINUS, onMapZoomOut), LV_ALIGN_TOP_RIGHT, -6, 52);
lv_obj_align(mapButton(body, LV_SYMBOL_DOWNLOAD, onMapDownload), LV_ALIGN_TOP_RIGHT, -6, 98);
lv_obj_align(mapButton(body, LV_SYMBOL_GPS, onMapCenter), LV_ALIGN_BOTTOM_RIGHT, -6, -6);
_map_dl_pill = mapPill(body, "");
// Nav map: trail / live share / download live in its tools panel, in the
// left column (back, pin, tools) -- the right one has no room above the bar.
if (_map_nav) lv_obj_align(mapButton(body, LV_SYMBOL_BARS, onNavTools), LV_ALIGN_TOP_LEFT, 6, 98);
else lv_obj_align(mapButton(body, LV_SYMBOL_DOWNLOAD, onMapDownload), LV_ALIGN_TOP_RIGHT, -6, 98);
int bottom = _map_nav ? navmap::BAR_H : 0; // the nav bar takes the bottom edge
lv_obj_align(mapButton(body, LV_SYMBOL_GPS, onMapCenter), LV_ALIGN_BOTTOM_RIGHT, -6, -6 - bottom);
_map_dl_pill = mapPill(body, ""); // tap: the download popup
lv_obj_set_style_text_color(_map_dl_pill, lv_color_hex(theme::ACCENT), 0);
lv_obj_align(_map_dl_pill, LV_ALIGN_TOP_MID, 0, 16);
lv_obj_set_style_pad_ver(_map_dl_pill, 3, 0);
lv_obj_align(_map_dl_pill, LV_ALIGN_TOP_RIGHT, -52, 14); // beside +, clear of the zoom pill on the left
lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_ext_click_area(_map_dl_pill, 6);
lv_obj_add_event_cb(_map_dl_pill, onMapDownload, LV_EVENT_CLICKED, NULL);
lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_HIDDEN);
_map_zoom_lbl = mapPill(body, "");
lv_obj_align(_map_zoom_lbl, LV_ALIGN_TOP_LEFT, 52, 16);
lv_obj_t* attr = mapPill(body, mapview::s_provider->attribution()); // required by the data licences
lv_label_set_long_mode(attr, LV_LABEL_LONG_WRAP);
lv_obj_set_style_max_width(attr, 250, 0);
lv_obj_set_width(attr, LV_SIZE_CONTENT);
lv_obj_align(attr, LV_ALIGN_BOTTOM_LEFT, 6, -6);
// Map data credit (the licences require it on the map): a small "©" that
// shows the full line when tapped; it is also in Settings > About.
lv_obj_t* attr = mapPill(body, "\xC2\xA9");
lv_obj_set_style_pad_hor(attr, 7, 0);
lv_obj_set_style_pad_ver(attr, 3, 0);
lv_obj_add_flag(attr, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_ext_click_area(attr, 8);
lv_obj_add_event_cb(attr, onMapCredits, LV_EVENT_CLICKED, NULL);
lv_obj_align(attr, LV_ALIGN_BOTTOM_LEFT, 6, -6 - bottom);
_map_hint = mapPill(body, "");
lv_label_set_long_mode(_map_hint, LV_LABEL_LONG_WRAP);
lv_obj_set_width(_map_hint, 200);
lv_obj_set_style_text_align(_map_hint, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_center(_map_hint);
lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN);
if (_map_nav) buildNavControls(body);
lv_obj_update_layout(body);
mapview::s_mark_count = 0; // the previous markers went with the previous screen
@@ -184,32 +310,49 @@ void UITask::layoutMap() {
int w = lv_obj_get_width(_map_area), h = lv_obj_get_height(_map_area);
double left = _map_cx * mapview::TILE_PX - w / 2.0; // world px of the view's top-left
double top = _map_cy * mapview::TILE_PX - h / 2.0;
mapview::s_left = left; mapview::s_top = top;
int tx0 = (int)floor(left / mapview::TILE_PX), ty0 = (int)floor(top / mapview::TILE_PX);
int n = 1 << _map_z;
bool have_provider = mapview::s_available;
int shown = 0, missing = 0;
int shown = 0, missing = 0, over = 0;
_map_pending = false;
for (int j = 0; j < mapview::GRID_ROWS; j++) {
for (int i = 0; i < mapview::GRID_COLS; i++) {
lv_obj_t* cell = mapview::s_cells[j * mapview::GRID_COLS + i];
lv_obj_t* img = _map_tiles[j * mapview::GRID_COLS + i];
int tx = tx0 + i, ty = ty0 + j;
int tx = tx0 + i, ty = ty0 + j, wx = ((tx % n) + n) % n;
int px = (int)lround(tx * (double)mapview::TILE_PX - left);
int py = (int)lround(ty * (double)mapview::TILE_PX - top);
bool on_screen = px < w && py < h && px + mapview::TILE_PX > 0 && py + mapview::TILE_PX > 0;
mapview::TileCache::Slot* s = nullptr;
if (have_provider && on_screen && ty >= 0 && ty < n) {
s = mapview::s_cache.find(_map_z, ((tx % n) + n) % n, ty);
s = mapview::s_cache.find(_map_z, wx, ty);
if (!s) _map_pending = true;
}
if (s && s->present) {
if (lv_image_get_src(img) != &s->dsc) lv_image_set_src(img, &s->dsc);
lv_obj_set_pos(img, px, py);
lv_obj_remove_flag(img, LV_OBJ_FLAG_HIDDEN);
mapview::TileCache::Slot* show = (s && s->present) ? s : nullptr;
int k = 0;
bool stand_in = false;
if (s && !s->present) { // none at this zoom: magnify a coarser one
missing++;
int wz, ax, ay;
show = mapview::ancestorFor(_map_z, wx, ty, k, wz, ax, ay);
if (!show && wz >= 0) _map_pending = true;
} else if (!s && have_provider && on_screen && ty >= 0 && ty < n) { // not decoded yet
show = mapview::cachedAncestor(_map_z, wx, ty, k);
stand_in = show != nullptr;
}
if (show) {
if (lv_image_get_src(img) != &show->dsc) lv_image_set_src(img, &show->dsc);
lv_image_set_scale(img, LV_SCALE_NONE << k);
int m = (1 << k) - 1; // which part of the magnified tile this cell is
lv_obj_set_pos(img, -(wx & m) * mapview::TILE_PX, -(ty & m) * mapview::TILE_PX);
lv_obj_set_pos(cell, px, py);
lv_obj_remove_flag(cell, LV_OBJ_FLAG_HIDDEN);
shown++;
if (k > over && !stand_in) over = k;
} else {
lv_obj_add_flag(img, LV_OBJ_FLAG_HIDDEN);
if (s) missing++;
lv_obj_add_flag(cell, LV_OBJ_FLAG_HIDDEN);
}
}
}
@@ -224,16 +367,21 @@ void UITask::layoutMap() {
} else {
lv_obj_add_flag(_map_me, LV_OBJ_FLAG_HIDDEN);
}
const int half = mapview::MARK_D / 2;
for (int k = 0; k < mapview::s_mark_count; k++) {
const mapview::Mark& m = mapview::s_marks[k];
int dy = lv_obj_get_y(m.dot); // the dot within the marker (vertically centred on the name)
double x = mapview::lonToTileX(m.lon_e6 / 1e6, _map_z) * mapview::TILE_PX - left;
double y = mapview::latToTileY(m.lat_e6 / 1e6, _map_z) * mapview::TILE_PX - top;
lv_obj_set_pos(m.obj, (int)x - 6, (int)y - 6); // the dot's centre on the spot; name to its right
lv_obj_set_pos(m.obj, (int)lround(x) - half, (int)lround(y) - half - dy); // the dot's centre on the spot
}
if (_map_nav) layoutNav();
lv_label_set_text_fmt(_map_zoom_lbl, "z%d", _map_z);
if (over) lv_label_set_text_fmt(_map_zoom_lbl, "z%d (map z%d)", _map_z, _map_z - over); // magnified
else lv_label_set_text_fmt(_map_zoom_lbl, "z%d", _map_z);
const char* hint = !have_provider ? "No map on the SD card.\nPut tiles in /maps (tools/maps)."
: (!_map_pending && shown == 0 && missing > 0) ? "No tiles here at this zoom." : nullptr;
: (!_map_pending && shown == 0 && missing > 0)
? "No map detail here at this zoom.\nZoom out, or download this area." : nullptr;
if (hint) { lv_label_set_text(_map_hint, hint); lv_obj_remove_flag(_map_hint, LV_OBJ_FLAG_HIDDEN); }
else lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN);
}
@@ -246,15 +394,27 @@ void UITask::mapLoop() {
int32_t lat, lon;
if (_core->course.currentLocation(lat, lon)) {
_map_cx = mapview::lonToTileX(lon / 1e6, _map_z);
_map_cy = mapview::latToTileY(lat / 1e6, _map_z);
_map_cy = mapview::latToTileY(lat / 1e6, _map_z) + mapCenterBias();
}
}
rebuildMapMarkers();
layoutMap();
}
if (!_map_pending || !_map_area) return;
if (_map_nav) navPollAveraging();
if (_map_nav && (int32_t)(millis() - _next_nav_bar_ms) >= 0) {
_next_nav_bar_ms = millis() + 1000;
refreshNavBar();
}
if (!_map_area) return;
uint32_t since_pan = millis() - mapview::s_last_pan_ms;
if (since_pan < mapview::PAN_SETTLE_MS) return; // mid-drag: keep it smooth, decode after
int w = lv_obj_get_width(_map_area), h = lv_obj_get_height(_map_area);
double left = _map_cx * mapview::TILE_PX - w / 2.0, top = _map_cy * mapview::TILE_PX - h / 2.0;
if (!_map_pending) { // view complete: when idle, decode the next tile a pan would reveal
if (since_pan >= mapview::PREFETCH_IDLE_MS && mapview::s_available)
mapview::prefetchOne(left, top, w, h, _map_z);
return;
}
int tx0 = (int)floor(left / mapview::TILE_PX), ty0 = (int)floor(top / mapview::TILE_PX);
int n = 1 << _map_z;
int best_x = 0, best_y = 0;
@@ -271,7 +431,28 @@ void UITask::mapLoop() {
if (d < best_d) { best_d = d; best_x = wx; best_y = ty; }
}
}
if (best_d < 1e18) mapview::s_cache.load(*mapview::s_provider, _map_z, best_x, best_y);
if (best_d < 1e18) {
mapview::s_cache.load(*mapview::s_provider, _map_z, best_x, best_y);
} else { // every tile at this zoom looked up: decode a coarser one for a missing tile
best_d = 1e18;
int bz = -1;
for (int j = 0; j < mapview::GRID_ROWS; j++) {
for (int i = 0; i < mapview::GRID_COLS; i++) {
int tx = tx0 + i, ty = ty0 + j;
double px = tx * (double)mapview::TILE_PX - left, py = ty * (double)mapview::TILE_PX - top;
if (px >= w || py >= h || px + mapview::TILE_PX <= 0 || py + mapview::TILE_PX <= 0) continue;
if (ty < 0 || ty >= n) continue;
int wx = ((tx % n) + n) % n;
mapview::TileCache::Slot* s = mapview::s_cache.find(_map_z, wx, ty);
if (!s || s->present) continue;
int k, wz, ax, ay;
if (mapview::ancestorFor(_map_z, wx, ty, k, wz, ax, ay) || wz < 0) continue;
double d = fabs(px + 128 - w / 2.0) + fabs(py + 128 - h / 2.0);
if (d < best_d) { best_d = d; bz = wz; best_x = ax; best_y = ay; }
}
}
if (bz >= 0) mapview::s_cache.load(*mapview::s_provider, bz, best_x, best_y);
}
layoutMap();
}
@@ -279,38 +460,50 @@ void UITask::rebuildMapMarkers() {
if (!_map_marks) return;
for (int k = 0; k < mapview::s_mark_count; k++) lv_obj_delete(mapview::s_marks[k].obj);
mapview::s_mark_count = 0;
if (_map_nav) rebuildNavMarkers();
else rebuildNodeMarkers();
lv_obj_update_layout(_map_marks); // so layoutMap() knows where each dot sits in its marker
}
void UITask::rebuildNodeMarkers() {
_nearby->refreshStored();
for (int i = 0; i < _nearby->count() && mapview::s_mark_count < NearbyModel::MAX_NEARBY; i++) {
for (int i = 0; i < _nearby->count() && mapview::s_mark_count < mapview::MAX_MARKS; i++) {
const NearbyModel::Entry& e = _nearby->at(i);
if (e.lat_e6 == 0 && e.lon_e6 == 0) continue;
uint32_t col = e.is_live ? theme::OK : e.type == ADV_TYPE_CHAT ? theme::TEXT : theme::TEXT_MUTED;
// One clickable object per marker: a dot plus the name beside it.
lv_obj_t* m = lv_obj_create(_map_marks);
lv_obj_remove_style_all(m);
lv_obj_set_size(m, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(m, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(m, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(m, 3, 0);
lv_obj_add_flag(m, LV_OBJ_FLAG_EVENT_BUBBLE); // a drag starting here still pans
lv_obj_add_event_cb(m, onMapMarker, LV_EVENT_CLICKED, (void*)(uintptr_t)mapview::s_mark_count);
lv_obj_t* dot = lv_obj_create(m);
lv_obj_remove_style_all(dot);
lv_obj_set_size(dot, 12, 12);
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_color(dot, lv_color_hex(col), 0);
lv_obj_set_style_bg_opa(dot, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(dot, lv_color_hex(theme::BG), 0);
lv_obj_set_style_border_width(dot, 2, 0);
lv_obj_remove_flag(dot, LV_OBJ_FLAG_CLICKABLE);
lv_obj_t* name = mapPill(m, e.name[0] ? e.name : "?");
lv_obj_remove_flag(name, LV_OBJ_FLAG_CLICKABLE);
mapview::Mark& mk = mapview::s_marks[mapview::s_mark_count++];
mk.lat_e6 = e.lat_e6; mk.lon_e6 = e.lon_e6; mk.row = i; mk.obj = m;
addMapMark(mapview::MK_NODE, i, e.lat_e6, e.lon_e6, col, e.name[0] ? e.name : "?");
}
}
// One clickable object per marker: a dot plus the name beside it. The object
// is placed so the dot's centre is on the spot, from where layout actually put
// the dot inside it (Mark::dot) -- the name may be taller than the dot.
void UITask::addMapMark(uint8_t kind, int idx, int32_t lat_e6, int32_t lon_e6, uint32_t col, const char* text) {
if (!_map_marks || mapview::s_mark_count >= mapview::MAX_MARKS) return;
lv_obj_t* m = lv_obj_create(_map_marks);
lv_obj_remove_style_all(m);
lv_obj_set_size(m, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(m, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(m, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(m, 3, 0);
lv_obj_add_flag(m, LV_OBJ_FLAG_EVENT_BUBBLE); // a drag starting here still pans
lv_obj_add_event_cb(m, onMapMarker, LV_EVENT_CLICKED, (void*)(uintptr_t)mapview::s_mark_count);
lv_obj_t* dot = lv_obj_create(m);
lv_obj_remove_style_all(dot);
lv_obj_set_size(dot, mapview::MARK_D, mapview::MARK_D);
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_color(dot, lv_color_hex(col), 0);
lv_obj_set_style_bg_opa(dot, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(dot, lv_color_hex(theme::BG), 0);
lv_obj_set_style_border_width(dot, 2, 0);
lv_obj_remove_flag(dot, LV_OBJ_FLAG_CLICKABLE);
lv_obj_t* name = mapPill(m, text);
lv_obj_remove_flag(name, LV_OBJ_FLAG_CLICKABLE);
mapview::Mark& mk = mapview::s_marks[mapview::s_mark_count++];
mk.lat_e6 = lat_e6; mk.lon_e6 = lon_e6; mk.idx = idx; mk.kind = kind; mk.obj = m; mk.dot = dot;
}
void UITask::mapPan(int dx, int dy) {
_map_cx -= dx / (double)mapview::TILE_PX;
_map_cy -= dy / (double)mapview::TILE_PX;
@@ -332,19 +525,36 @@ void UITask::mapZoom(int delta) {
void UITask::mapCenterOnMe() {
int32_t lat, lon;
if (!_core->course.currentLocation(lat, lon)) { showToast("No GPS fix"); return; }
if (!ensureGps() || !_core->course.currentLocation(lat, lon)) { _map_follow = true; return; } // centres once a fix comes
_map_follow = true;
_map_cx = mapview::lonToTileX(lon / 1e6, _map_z);
_map_cy = mapview::latToTileY(lat / 1e6, _map_z);
_map_cy = mapview::latToTileY(lat / 1e6, _map_z) + mapCenterBias();
layoutMap();
}
void UITask::mapOpenMarker(int idx) {
if (idx < 0 || idx >= mapview::s_mark_count) return;
openNode(mapview::s_marks[idx].row);
const mapview::Mark& m = mapview::s_marks[idx];
if (m.kind == mapview::MK_WAYPOINT) { navWaypointMenu(m.idx); return; }
if (m.kind == mapview::MK_LIVE) { navPick(navmap::code(navmap::T_LIVE, m.idx)); return; }
openNode(m.idx);
_node_from_map = true;
}
// Screen point (relative to the map area) -> lat/lon at the current view.
void UITask::mapPointToLatLon(int x, int y, int32_t& lat_e6, int32_t& lon_e6) const {
double n = (double)(1 << _map_z);
double tx = (mapview::s_left + x) / mapview::TILE_PX, ty = (mapview::s_top + y) / mapview::TILE_PX;
double lon = tx / n * 360.0 - 180.0;
double lat = atan(sinh(M_PI * (1.0 - 2.0 * ty / n))) * 180.0 / M_PI;
lat_e6 = (int32_t)lround(lat * 1e6);
lon_e6 = (int32_t)lround(lon * 1e6);
}
void UITask::mapLongPress(int x, int y) {
if (_map_nav) navDropAt(x, y);
}
// ── Area download ─────────────────────────────────────────────────────────────
@@ -405,6 +615,21 @@ void UITask::mapDownloadPopup() {
lv_obj_align(_dl_zoom_lbl, LV_ALIGN_RIGHT_MID, -56, 0);
headerButton(zr, LV_SYMBOL_MINUS, onDlZoomMinus, 96, NULL);
// Unfinished job (power-off, lost WiFi, Stop): resume or drop it.
_dl_job_row = lv_obj_create(panel);
styleSurface(_dl_job_row, theme::SURFACE);
lv_obj_remove_flag(_dl_job_row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(_dl_job_row, LV_PCT(100), 40);
lv_obj_set_style_radius(_dl_job_row, theme::RADIUS, 0);
_dl_job_lbl = label(_dl_job_row, "", THEME_FONT_SMALL, theme::TEXT);
lv_obj_set_width(_dl_job_lbl, 170);
lv_label_set_long_mode(_dl_job_lbl, LV_LABEL_LONG_WRAP);
lv_obj_align(_dl_job_lbl, LV_ALIGN_LEFT_MID, theme::PAD, 0);
lv_obj_t* res = headerButton(_dl_job_row, LV_SYMBOL_PLAY " Resume", onDlResume, 48, NULL);
lv_obj_set_style_bg_color(res, lv_color_hex(theme::ACCENT_DIM), 0);
headerButton(_dl_job_row, LV_SYMBOL_TRASH, onDlDiscard, 4, NULL);
lv_obj_add_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN);
_dl_info = label(panel, "", THEME_FONT_SMALL, theme::TEXT);
lv_label_set_long_mode(_dl_info, LV_LABEL_LONG_WRAP);
lv_obj_set_width(_dl_info, LV_PCT(100));
@@ -439,7 +664,7 @@ void UITask::mapDownloadPopup() {
void UITask::mapDownloadClose() {
if (_dl_overlay) lv_obj_delete_async(_dl_overlay); // may be closing from its own button
_dl_overlay = _dl_info = _dl_zoom_lbl = _dl_start_lbl = nullptr;
_dl_overlay = _dl_info = _dl_zoom_lbl = _dl_start_lbl = _dl_job_row = _dl_job_lbl = nullptr;
}
void UITask::mapDownloadZmax(int delta) {
@@ -468,23 +693,32 @@ void UITask::refreshDownloadPopup() {
(unsigned long)dl.processed(), (unsigned long)dl.total(),
(unsigned long)dl.downloaded(), dl.sourceHost(), net);
lv_label_set_text(_dl_start_lbl, LV_SYMBOL_STOP " Stop");
if (_dl_job_row) lv_obj_add_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN);
return;
}
mapview::TileArea job;
if (_dl_job_row && dl.savedJob(job)) {
lv_label_set_text_fmt(_dl_job_lbl, "Unfinished: z%d-%d,\n%lu tiles", job.zmin, job.zmax,
(unsigned long)mapview::countTiles(job));
lv_obj_remove_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN);
} else if (_dl_job_row) {
lv_obj_add_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN);
}
int w = _map_area ? lv_obj_get_width(_map_area) : 320, h = _map_area ? lv_obj_get_height(_map_area) : 218;
mapview::TileArea a = visibleArea(_map_cx, _map_cy, _map_z, w, h, dlZmin(_map_z), _dl_zmax);
uint32_t n = mapview::countTiles(a);
char ssid[33], pass[65];
bool have_wifi = lvport::loadWifi(ssid, sizeof(ssid), pass, sizeof(pass));
char last[64] = "";
if (dl.state() == mapview::TileDownloader::DONE || dl.state() == mapview::TileDownloader::FAILED ||
dl.state() == mapview::TileDownloader::CANCELLED)
bool job_shown = _dl_job_row && !lv_obj_has_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN); // it says enough
if (!job_shown && (dl.state() == mapview::TileDownloader::DONE || dl.state() == mapview::TileDownloader::FAILED ||
dl.state() == mapview::TileDownloader::CANCELLED))
snprintf(last, sizeof(last), "\nLast: %s (%lu new)", dl.message(), (unsigned long)dl.downloaded());
char size[16];
uint64_t bytes = (uint64_t)n * mapview::AVG_TILE_BYTES;
if (bytes < 1024 * 1024) snprintf(size, sizeof(size), "%lu KB", (unsigned long)(bytes / 1024));
else snprintf(size, sizeof(size), "%lu MB", (unsigned long)((bytes + 512 * 1024) / (1024 * 1024)));
lv_label_set_text_fmt(_dl_info, "z%d-%d: %lu tiles, about %s (tiles on the card are skipped)\n"
"From %s\nWiFi: %s%s",
lv_label_set_text_fmt(_dl_info, "z%d-%d: %lu tiles, about %s - from %s\nWiFi: %s%s",
a.zmin, a.zmax, (unsigned long)n, size,
dl.sourceHost(), have_wifi ? ssid : "not set - tap WiFi", last);
lv_label_set_text(_dl_start_lbl, n > mapview::TileDownloader::MAX_TILES ? "Too large - zoom in"
@@ -502,11 +736,28 @@ void UITask::mapDownloadStart() {
refreshDownloadPopup();
}
void UITask::mapDownloadResume() {
mapview::TileArea a;
if (mapview::s_dl.active() || !mapview::s_dl.savedJob(a)) return;
char ssid[33], pass[65];
if (!lvport::loadWifi(ssid, sizeof(ssid), pass, sizeof(pass))) { showWifi(true); return; }
if (!lvport::mountStorage()) { showToast("No SD card"); return; }
if (!mapview::s_dl.start(a, ssid, pass)) { showToast(mapview::s_dl.message()); return; }
refreshDownloadPopup();
}
void UITask::mapDownloadDiscard() {
mapview::s_dl.discardJob();
refreshDownloadPopup();
}
void UITask::mapDownloadStop() {
mapview::s_dl.cancel();
refreshDownloadPopup();
}
static mapview::TileArea s_job_tmp;
// Every UI loop pass, whatever the screen: a download keeps going in the background.
void UITask::mapDownloadTick() {
mapview::TileDownloader& dl = mapview::s_dl;
@@ -539,6 +790,9 @@ void UITask::mapDownloadTick() {
lv_label_set_text_fmt(_map_dl_pill, LV_SYMBOL_DOWNLOAD " %lu / %lu", (unsigned long)dl.processed(),
(unsigned long)dl.total());
lv_obj_remove_flag(_map_dl_pill, LV_OBJ_FLAG_HIDDEN);
} else if (mapview::s_available && dl.savedJob(s_job_tmp)) {
lv_label_set_text(_map_dl_pill, LV_SYMBOL_DOWNLOAD " Resume?");
lv_obj_remove_flag(_map_dl_pill, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_HIDDEN);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,220 @@
#pragma once
// Settings > Radio: preset, frequency, SF / bandwidth / coding rate, TX power
// and Adaptive Power Control -- ui-new's Settings > Radio. Every change is
// applied to the radio at once and saved (ui-core/RadioControl.h does the
// applying for both frontends).
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp after ClockScreen.h.
namespace radioview {
enum : uint8_t { R_PRESET, R_SF, R_BW, R_CR, R_TX, R_APC };
static const int TX_MIN = 2;
#ifdef LORA_TX_POWER
static const int TX_MAX = LORA_TX_POWER;
#else
static const int TX_MAX = 22;
#endif
static lv_obj_t* s_overlay = nullptr; // frequency entry
static lv_obj_t* s_ta = nullptr;
static char s_opts[1024];
// Label + optional hint on the left of a settings row; the control goes on the right.
static lv_obj_t* settingRow(lv_obj_t* parent, const char* text, const char* hint) {
lv_obj_t* row = lv_obj_create(parent);
styleSurface(row, theme::SURFACE);
lv_obj_remove_flag(row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(row, LV_PCT(100), theme::ROW_H);
lv_obj_set_style_radius(row, theme::RADIUS, 0);
lv_obj_align(label(row, text, THEME_FONT_BODY, theme::TEXT), LV_ALIGN_TOP_LEFT, theme::PAD, hint ? 5 : 13);
if (hint) {
lv_obj_t* h = label(row, hint, THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_label_set_long_mode(h, LV_LABEL_LONG_DOT);
lv_obj_set_size(h, 150, 15);
lv_obj_align(h, LV_ALIGN_BOTTOM_LEFT, theme::PAD, -5);
}
return row;
}
static lv_obj_t* rowDropdown(lv_obj_t* row, const char* opts, int sel, int width, lv_event_cb_t cb, uintptr_t which) {
lv_obj_t* dd = lv_dropdown_create(row);
lv_dropdown_set_options(dd, opts);
if (sel >= 0) lv_dropdown_set_selected(dd, sel);
lv_obj_set_width(dd, width);
lv_obj_align(dd, LV_ALIGN_RIGHT_MID, -4, 0);
lv_obj_add_event_cb(dd, cb, LV_EVENT_VALUE_CHANGED, (void*)which);
return dd;
}
} // namespace radioview
static void onRadioDropdown(lv_event_t* e) {
s_ui->radioSet((int)(uintptr_t)lv_event_get_user_data(e), (int)lv_dropdown_get_selected((lv_obj_t*)lv_event_get_target(e)));
}
static void onRadioSwitch(lv_event_t* e) {
s_ui->radioSet((int)(uintptr_t)lv_event_get_user_data(e),
lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED) ? 1 : 0);
}
static void onRadioFreq(lv_event_t* e) { (void)e; s_ui->radioFreqPopup(); }
static void onRadioFreqKb(lv_event_t* e) { s_ui->radioFreqDone(lv_event_get_code(e) == LV_EVENT_READY); }
static void onOpenRadio(lv_event_t* e) { (void)e; s_ui->showRadio(); }
void UITask::showRadio() {
_screen = SCR_RADIO;
buildRadio();
}
void UITask::buildRadio() {
using namespace radioview;
lv_obj_t* body = newScreen("Radio", true);
s_overlay = s_ta = nullptr;
NodePrefs* p = _prefs;
// Preset: "Custom" first (current params match none), then the list.
int o = snprintf(s_opts, sizeof(s_opts), "Custom");
const char* name; float f, b; uint8_t sf, cr;
for (int i = 0; radioctl::presetAt(p, i, name, f, b, sf, cr) && o < (int)sizeof(s_opts) - 24; i++)
o += snprintf(s_opts + o, sizeof(s_opts) - o, "\n%s", name);
lv_obj_t* row = settingRow(body, "Preset", nullptr);
lv_obj_t* dd = rowDropdown(row, s_opts, radioctl::currentPreset(p) + 1, 200, onRadioDropdown, R_PRESET);
lv_dropdown_set_dir(dd, LV_DIR_BOTTOM);
// Frequency: tap to type it.
row = settingRow(body, "Frequency", "MHz");
lv_obj_t* fb = lv_button_create(row);
lv_obj_set_size(fb, 120, 34);
lv_obj_align(fb, LV_ALIGN_RIGHT_MID, -4, 0);
lv_obj_set_style_shadow_width(fb, 0, 0);
lv_obj_set_style_radius(fb, theme::RADIUS, 0);
lv_obj_set_style_bg_color(fb, lv_color_hex(theme::SURFACE_2), 0);
lv_obj_add_event_cb(fb, onRadioFreq, LV_EVENT_CLICKED, NULL);
char fs[16];
snprintf(fs, sizeof(fs), "%.3f", p->freq);
lv_obj_center(label(fb, fs, THEME_FONT_BODY, theme::TEXT));
o = 0;
for (int v = 5; v <= 12; v++) o += snprintf(s_opts + o, sizeof(s_opts) - o, v > 5 ? "\n%d" : "%d", v);
rowDropdown(settingRow(body, "Spreading factor", "Higher = longer range"), s_opts,
p->sf >= 5 && p->sf <= 12 ? p->sf - 5 : 0, 90, onRadioDropdown, R_SF);
o = 0;
for (int i = 0; i < LORA_BW_OPT_COUNT; i++)
o += snprintf(s_opts + o, sizeof(s_opts) - o, "%s%g kHz", i ? "\n" : "", (double)LORA_BW_OPTS[i]);
rowDropdown(settingRow(body, "Bandwidth", nullptr), s_opts, nearestBwIndex(p->bw), 130, onRadioDropdown, R_BW);
o = 0;
for (int v = 5; v <= 8; v++) o += snprintf(s_opts + o, sizeof(s_opts) - o, v > 5 ? "\n4/%d" : "4/%d", v);
rowDropdown(settingRow(body, "Coding rate", nullptr), s_opts, p->cr >= 5 && p->cr <= 8 ? p->cr - 5 : 0, 90,
onRadioDropdown, R_CR);
sectionTitle(body, "TRANSMIT");
o = 0;
for (int v = TX_MIN; v <= TX_MAX; v++) o += snprintf(s_opts + o, sizeof(s_opts) - o, v > TX_MIN ? "\n%d dBm" : "%d dBm", v);
int tx = p->tx_power_dbm < TX_MIN ? TX_MIN : p->tx_power_dbm > TX_MAX ? TX_MAX : p->tx_power_dbm;
rowDropdown(settingRow(body, "TX power", p->tx_apc ? "Ceiling for auto power" : nullptr), s_opts, tx - TX_MIN, 110,
onRadioDropdown, R_TX);
row = settingRow(body, "Auto power", p->client_repeat ? "Off while repeating" : "Lowers power on good links");
lv_obj_t* sw = lv_switch_create(row);
lv_obj_set_size(sw, 46, 24);
lv_obj_align(sw, LV_ALIGN_RIGHT_MID, -theme::PAD, 0);
if (p->tx_apc) lv_obj_add_state(sw, LV_STATE_CHECKED);
if (p->client_repeat) lv_obj_add_state(sw, LV_STATE_DISABLED);
lv_obj_add_event_cb(sw, onRadioSwitch, LV_EVENT_VALUE_CHANGED, (void*)(uintptr_t)R_APC);
lv_obj_t* note = label(body, "Everyone you talk to must use the same frequency, SF, bandwidth and coding rate.",
THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_label_set_long_mode(note, LV_LABEL_LONG_WRAP);
lv_obj_set_width(note, LV_PCT(100));
}
void UITask::radioSet(int which, int v) {
using namespace radioview;
NodePrefs* p = _prefs;
if (!p) return;
switch (which) {
case R_PRESET:
if (v == 0) return; // "Custom": nothing to take
radioctl::choosePreset(p, v - 1);
break;
case R_SF: p->sf = (uint8_t)(5 + v); radioctl::applyParams(); break;
case R_BW: if (v >= 0 && v < LORA_BW_OPT_COUNT) { p->bw = LORA_BW_OPTS[v]; radioctl::applyParams(); } break;
case R_CR: p->cr = (uint8_t)(5 + v); radioctl::applyParams(); break;
case R_TX: p->tx_power_dbm = (int8_t)(TX_MIN + v); radioctl::applyTxPower(p); break;
case R_APC: p->tx_apc = (uint8_t)v; radioctl::applyApc(); break;
}
the_mesh.savePrefs();
lv_obj_t* body = _body;
int32_t y = body ? lv_obj_get_scroll_y(body) : 0;
buildRadio(); // preset name / hints follow
if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); }
}
void UITask::radioFreqPopup() {
using namespace radioview;
if (s_overlay) return;
s_overlay = lv_obj_create(lv_screen_active());
lv_obj_remove_style_all(s_overlay);
lv_obj_set_size(s_overlay, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_color(s_overlay, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(s_overlay, LV_OPA_60, 0);
lv_obj_add_flag(s_overlay, LV_OBJ_FLAG_CLICKABLE);
lv_obj_t* panel = lv_obj_create(s_overlay);
lv_obj_set_size(panel, lv_display_get_horizontal_resolution(NULL) - 16, LV_SIZE_CONTENT);
lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4);
lv_obj_set_style_bg_color(panel, lv_color_hex(theme::BG), 0);
lv_obj_set_style_border_color(panel, lv_color_hex(theme::ACCENT), 0);
lv_obj_set_style_border_width(panel, 1, 0);
lv_obj_set_style_radius(panel, theme::RADIUS, 0);
lv_obj_set_style_pad_all(panel, theme::PAD, 0);
lv_obj_set_style_pad_row(panel, 6, 0);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN);
lv_obj_remove_flag(panel, LV_OBJ_FLAG_SCROLLABLE);
float lo, hi;
radio_driver.getFreqBounds(lo, hi);
char t[48];
snprintf(t, sizeof(t), "Frequency, MHz (%.0f - %.0f)", lo, hi);
label(panel, t, THEME_FONT_BODY, theme::TEXT);
s_ta = lv_textarea_create(panel);
lv_textarea_set_one_line(s_ta, true);
lv_textarea_set_accepted_chars(s_ta, "0123456789.");
lv_textarea_set_max_length(s_ta, 10);
char fs[16];
snprintf(fs, sizeof(fs), "%.3f", _prefs->freq);
lv_textarea_set_text(s_ta, fs);
lv_obj_set_size(s_ta, LV_PCT(100), 34);
lv_obj_set_style_border_width(s_ta, 1, 0);
lv_obj_set_style_pad_ver(s_ta, 6, 0);
lv_obj_set_style_pad_hor(s_ta, 10, 0);
lv_obj_set_scrollbar_mode(s_ta, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_style_border_color(s_ta, lv_color_hex(theme::ACCENT), LV_PART_CURSOR | LV_STATE_FOCUSED);
lv_obj_set_style_border_width(s_ta, 2, LV_PART_CURSOR | LV_STATE_FOCUSED);
lv_obj_add_state(s_ta, LV_STATE_FOCUSED);
lv_obj_t* kbd = kb::create(s_overlay, _prefs);
lv_obj_set_size(kbd, LV_PCT(100), 124);
lv_obj_align(kbd, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_keyboard_set_textarea(kbd, s_ta);
kb::apply(kbd, kb::L_SYM);
lv_obj_add_event_cb(kbd, onRadioFreqKb, LV_EVENT_READY, NULL);
lv_obj_add_event_cb(kbd, onRadioFreqKb, LV_EVENT_CANCEL, NULL);
}
void UITask::radioFreqDone(bool ok) {
using namespace radioview;
if (ok && s_ta) {
float lo, hi;
radio_driver.getFreqBounds(lo, hi);
float f = strtof(lv_textarea_get_text(s_ta), nullptr);
if (f < lo || f > hi) { showToast("Out of the radio's range"); return; }
_prefs->freq = f;
radioctl::applyParams();
the_mesh.savePrefs();
}
radioCloseFreq();
if (ok) buildRadio();
}
void UITask::radioCloseFreq() {
if (radioview::s_overlay) lv_obj_delete_async(radioview::s_overlay);
radioview::s_overlay = radioview::s_ta = nullptr;
}
bool UITask::radioPopupOpen() const { return radioview::s_overlay != nullptr; }
+7 -1
View File
@@ -22,7 +22,7 @@ namespace theme {
static const int ROW_H = 44; // list row (touch target)
// Type -- Noto Sans, generated by fonts/generate.sh (declared in lv_conf.h).
// ui_font_40 holds digits / ':' / '-' only (clock).
// ui_font_40 holds digits / ':' / '-' / '.' only (clock, timer, stopwatch).
#define THEME_FONT_SMALL (&ui_font_12)
#define THEME_FONT_BODY (&ui_font_14)
#define THEME_FONT_TITLE (&ui_font_16)
@@ -33,4 +33,10 @@ namespace theme {
#define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite
#define UI_SYMBOL_MAP "\xEF\x89\xB9" // U+F279
#define UI_SYMBOL_USERS "\xEF\x83\x80" // U+F0C0, Nearby
#define UI_SYMBOL_COMPASS "\xEF\x85\x8E" // U+F14E, Navigate
#define UI_SYMBOL_FLAG "\xEF\x80\xA4" // U+F024, waypoint
#define UI_SYMBOL_PIN "\xEF\x8F\x85" // U+F3C5, mark here
#define UI_SYMBOL_CLOCK "\xEF\x80\x97" // U+F017, Clock tools
#define UI_SYMBOL_STOPWATCH "\xEF\x8B\xB2" // U+F2F2
#define UI_SYMBOL_RADIO "\xEF\x94\x99" // U+F519, broadcast tower
}
+500 -35
View File
@@ -8,6 +8,11 @@
#include "../ui-core/UiCore.h" // shared UI Core (header-only, this TU)
#include "../ui-core/NearbyModel.h"
#include "../ui-core/EtaTracker.h"
#include "../ui-core/SettingsSchema.h"
#include "../ui-core/GpsAverager.h"
#include "../ui-core/TrackBack.h"
#include "../ui-core/RadioControl.h"
#include "Theme.h"
#include "LvglPort.h"
#include "../ui-core/KeyboardData.h"
@@ -109,6 +114,7 @@ void UITask::begin(DisplayDriver* display_drv, SensorManager* sensors, NodePrefs
lv_display_set_theme(lv_display_get_default(), th);
buildStatusBar();
applyDisplayPrefs(); // a slider percentage overrides the level main.cpp set
showHome();
}
@@ -126,7 +132,8 @@ void UITask::loop() {
s_sim_btn_click = false;
#endif
if (btn_click) {
if (_asleep) wake();
if (_core->clock.isRinging()) dismissRing();
else if (_asleep) wake();
else if (_screen == SCR_HOME) sleep();
else back();
}
@@ -135,12 +142,17 @@ void UITask::loop() {
if (lvport::touched()) { lvport::swallowTouch(); wake(); }
} else {
uint32_t aoff = autoOffMillis();
if (aoff > 0 && lv_display_get_inactive_time(NULL) > aoff) sleep();
if (aoff > 0 && lv_display_get_inactive_time(NULL) > aoff && !_core->clock.isRinging()) sleep();
}
_core->loop();
drainCoreEvents();
checkLowBattery();
mapDownloadTick(); // a map download keeps running on any screen, and asleep
if ((int32_t)(millis() - _next_trackback_ms) >= 0) { // walking the trail back, on any screen
_next_trackback_ms = millis() + 1000;
navPollTrackBack();
}
if (!_asleep) {
if ((int32_t)(millis() - _next_status_ms) >= 0) {
@@ -148,6 +160,7 @@ void UITask::loop() {
refreshStatusBar();
if (_screen == SCR_HOME) refreshHome();
}
if (_screen == SCR_HOME) homeSwipePoll();
if ((_screen == SCR_NEARBY || _screen == SCR_NODE) && (int32_t)(millis() - _next_nearby_ms) >= 0) {
// Scan results trickle in over a few seconds: poll fast while scanning.
_next_nearby_ms = millis() + (_scanning ? 250 : 2000);
@@ -161,6 +174,10 @@ void UITask::loop() {
}
if (_screen == SCR_MAP) mapLoop();
if (_screen == SCR_WIFI) pollWifiScan();
if (_screen == SCR_CLOCK && (int32_t)(millis() - _next_clock_ms) >= 0) {
_next_clock_ms = millis() + 100; // stopwatch tenths
refreshClock();
}
if (_screen == SCR_THREAD && (int32_t)(millis() - _next_thread_check_ms) >= 0) {
_next_thread_check_ms = millis() + 500;
uint32_t sig = threadSignature();
@@ -188,6 +205,22 @@ void UITask::shutdown(bool restart) {
}
}
// Settings > Display & power > Battery shutdown (NodePrefs::low_batt_mv), as
// ui-new does it: a smoothed reading every 8 s, never while on USB power.
void UITask::checkLowBattery() {
if ((int32_t)(millis() - _next_batt_ms) < 0) return;
_next_batt_ms = millis() + 8000;
uint16_t raw = getBattMilliVolts();
if (raw > 0) _batt_mv = _batt_mv == 0 ? raw : (uint16_t)((_batt_mv * 4u + raw) / 5u); // EMA, alpha 0.2
uint16_t low = _prefs ? _prefs->low_batt_mv : 0;
if (low == 0 || _batt_mv == 0 || _batt_mv >= low || _board->isExternalPowered()) return;
wake();
showToast("Low battery - shutting down", 3000);
lv_timer_handler();
delay(2000);
shutdown();
}
uint32_t UITask::autoOffMillis() const {
if (!_prefs || _prefs->auto_off_secs == 0) return 0;
return (uint32_t)_prefs->auto_off_secs * 1000UL;
@@ -220,10 +253,10 @@ void UITask::drainCoreEvents() {
onMessageArrived(ev);
break;
case UiEventType::ClockAlert:
wake();
showToast(ev.text, ClockEngine::RING_MS);
showRing(ev.text);
break;
case UiEventType::ClockRingEnded:
hideRing();
break;
case UiEventType::LiveShareEnded:
showToast("Live share ended");
@@ -272,6 +305,7 @@ void UITask::buildStatusBar() {
lv_obj_align(_status_time, LV_ALIGN_LEFT_MID, 0, 0);
_status_icons = label(bar, "", THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_obj_align(_status_icons, LV_ALIGN_RIGHT_MID, 0, 0);
_status_gps = label(bar, LV_SYMBOL_GPS, THEME_FONT_SMALL, theme::TEXT_MUTED);
_toast = lv_obj_create(lv_layer_top());
lv_obj_remove_flag(_toast, LV_OBJ_FLAG_SCROLLABLE);
@@ -284,7 +318,10 @@ void UITask::buildStatusBar() {
lv_obj_set_style_pad_hor(_toast, 12, 0);
lv_obj_set_style_pad_ver(_toast, 6, 0);
lv_obj_align(_toast, LV_ALIGN_BOTTOM_MID, 0, -12);
label(_toast, "", THEME_FONT_BODY, theme::TEXT);
lv_obj_t* tl = label(_toast, "", THEME_FONT_BODY, theme::TEXT);
lv_label_set_long_mode(tl, LV_LABEL_LONG_WRAP); // long texts wrap instead of running off the screen
lv_obj_set_style_max_width(tl, lv_display_get_horizontal_resolution(NULL) - 40, 0);
lv_obj_set_style_text_align(tl, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_add_flag(_toast, LV_OBJ_FLAG_HIDDEN);
refreshStatusBar();
@@ -305,15 +342,33 @@ void UITask::refreshStatusBar() {
int pct = mv <= 3300 ? 0 : mv >= 4200 ? 100 : (int)(mv - 3300) * 100 / 900;
const char* batt = pct > 80 ? LV_SYMBOL_BATTERY_FULL : pct > 55 ? LV_SYMBOL_BATTERY_3
: pct > 30 ? LV_SYMBOL_BATTERY_2 : pct > 10 ? LV_SYMBOL_BATTERY_1 : LV_SYMBOL_BATTERY_EMPTY;
bool gps = false;
if (_sensors) {
LocationProvider* loc = _sensors->getLocationProvider();
gps = loc && loc->isValid();
lv_label_set_text_fmt(_status_icons, "%s%s %d%%", hasConnection() ? LV_SYMBOL_BLUETOOTH " " : "", batt, pct);
// GPS: green with a fix, muted while on and searching, absent when off.
int32_t lat, lon;
bool fix = _core->course.currentLocation(lat, lon);
if (_core->gpsEnabled() || fix) {
lv_obj_set_style_text_color(_status_gps, lv_color_hex(fix ? theme::OK : theme::TEXT_MUTED), 0);
lv_obj_remove_flag(_status_gps, LV_OBJ_FLAG_HIDDEN);
lv_obj_align_to(_status_gps, _status_icons, LV_ALIGN_OUT_LEFT_MID, -8, 0);
} else {
lv_obj_add_flag(_status_gps, LV_OBJ_FLAG_HIDDEN);
}
lv_label_set_text_fmt(_status_icons, "%s%s%s %d%%",
hasConnection() ? LV_SYMBOL_BLUETOOTH " " : "",
gps ? LV_SYMBOL_GPS " " : "",
batt, pct);
}
void UITask::setGps(bool on) {
if (!_core->setGpsEnabled(on)) { showToast("No GPS on this device"); return; }
showToast(on ? "GPS on, waiting for a fix" : "GPS off");
refreshStatusBar();
}
void UITask::botSetGPS(bool on) { if (_core->setGpsEnabled(on)) refreshStatusBar(); }
bool UITask::ensureGps() {
int32_t lat, lon;
if (_core->course.currentLocation(lat, lon)) return true;
if (_core->gpsAvailable() && !_core->gpsEnabled()) setGps(true);
else showToast("Waiting for a GPS fix");
return false;
}
// One persistent timer, paused between toasts: hides the toast when it fires.
@@ -347,7 +402,10 @@ lv_obj_t* UITask::newScreen(const char* title, bool with_back) {
_node_info = _node_ping = _node_delete_lbl = nullptr;
_scan_overlay = _scan_list = _scan_status = nullptr; // the popup went with the old screen
_map_area = _map_marks = _map_me = _map_zoom_lbl = _map_hint = _map_dl_pill = nullptr;
_dl_overlay = _dl_info = _dl_zoom_lbl = _dl_start_lbl = nullptr;
_dl_overlay = _dl_info = _dl_zoom_lbl = _dl_start_lbl = _dl_job_row = _dl_job_lbl = nullptr;
_nav_bar = _nav_title = _nav_info = _nav_clear = nullptr;
_nav_overlay = _nav_ta = _nav_kb = _nav_del_lbl = _nav_rec = _nav_avg_pill = nullptr;
_nav_trail_lbl = _nav_trail_btn = _nav_reset_lbl = _nav_share_lbl = _nav_share_btn = _nav_tb_btn = nullptr;
_wifi_ssid = _wifi_pass = _wifi_kb = _wifi_list = _wifi_status = nullptr;
_wifi_scanning = false;
for (lv_obj_t*& t : _map_tiles) t = nullptr;
@@ -398,19 +456,24 @@ void UITask::back() {
case SCR_THREAD: showChats(); break;
case SCR_CONTACTS: showChats(); break;
case SCR_SETTINGS: showHome(); break;
case SCR_SETTINGS_NAV: showSettings(); break;
case SCR_CLOCK: showHome(); break;
case SCR_RADIO: if (radioPopupOpen()) radioCloseFreq(); else showSettings(); break;
case SCR_CHATS: showHome(); break;
case SCR_NEARBY:
if (_scan_overlay) closeScanPopup();
else showHome();
break;
case SCR_NODE: // back to where the node was picked: map, the list, or the scan popup over it
if (_node_from_map) { showMap(); break; }
if (_node_from_map) { openMap(false); break; }
_screen = SCR_NEARBY;
buildNearby();
if (_node_from_scan) showScanPopup();
break;
case SCR_MAP:
if (_dl_overlay) mapDownloadClose();
if (_nav_overlay) navClosePopup();
else if (_dl_overlay) mapDownloadClose();
else if (!_map_nav) showNearby(); // the Nodes map opens from Nearby
else showHome();
break;
case SCR_WIFI: if (_wifi_from_map) showMap(); else showSettings(); break;
@@ -424,9 +487,40 @@ static void onOpenChats(lv_event_t* e) { (void)e; s_ui->showChats(); }
static void onOpenSettings(lv_event_t* e) { (void)e; s_ui->showSettings(); }
static void onOpenNearby(lv_event_t* e) { (void)e; s_ui->showNearby(); }
static void onOpenMap(lv_event_t* e) { (void)e; s_ui->showMap(); }
static void onOpenNodesMap(lv_event_t* e) { (void)e; s_ui->openMap(false); }
static void onOpenNav(lv_event_t* e) { (void)e; s_ui->openMap(true); }
static void onOpenClock(lv_event_t* e); // ClockScreen.h
// Home tile: icon over label, one of a row of three; optional amber value
// Home apps, in order; PER_PAGE to a page, further pages are swiped to (with
// dots underneath), so new apps don't squeeze the row.
namespace home {
struct App { const char* icon; const char* text; lv_event_cb_t cb; bool unread; };
static const App APPS[] = {
{ LV_SYMBOL_ENVELOPE, "Messages", onOpenChats, true },
{ UI_SYMBOL_USERS, "Nearby", onOpenNearby, false },
{ UI_SYMBOL_MAP, "Map", onOpenNav, false },
{ LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, false },
// Page 2: tools
{ UI_SYMBOL_CLOCK, "Clock", onOpenClock, false },
};
static const int COUNT = sizeof(APPS) / sizeof(APPS[0]);
static const int PER_PAGE = 4;
static const int PAGES = (COUNT + PER_PAGE - 1) / PER_PAGE;
static lv_obj_t* s_row = nullptr; // the current page's tiles
static lv_obj_t* s_dots = nullptr;
static int s_page = 0; // kept across rebuilds (the home screen is rebuilt on return)
// A horizontal swipe anywhere on Home turns the page (the tiles row alone is
// too small a target), tracked from the touch itself: LVGL's gesture detector
// drops a slow swipe (its sum resets on every read without movement). Tapping
// a dot also goes to that page.
static bool s_touching = false, s_swiped = false;
static lv_point_t s_start;
static const int SWIPE_PX = 40;
static void onDot(lv_event_t* e) { s_ui->setHomePage((int)(uintptr_t)lv_event_get_user_data(e)); }
} // namespace home
// Home tile: icon over label, one of a page's row; optional amber value
// (unread count) in its top-right corner.
static lv_obj_t* homeTile(lv_obj_t* parent, const char* icon, const char* text, lv_event_cb_t cb,
lv_obj_t** value_out) {
@@ -450,6 +544,7 @@ static lv_obj_t* homeTile(lv_obj_t* parent, const char* icon, const char* text,
void UITask::showHome() {
_screen = SCR_HOME;
_share_text[0] = '\0'; // a share not sent to anyone
buildHome();
refreshHome();
}
@@ -463,16 +558,72 @@ void UITask::buildHome() {
lv_obj_t* name = label(body, the_mesh.getNodeName(), THEME_FONT_BODY, theme::ACCENT);
lv_obj_set_style_pad_bottom(name, 4, 0);
lv_obj_t* tiles = lv_obj_create(body);
styleSurface(tiles, theme::BG);
lv_obj_remove_flag(tiles, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(tiles, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(tiles, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(tiles, theme::GAP, 0);
homeTile(tiles, LV_SYMBOL_ENVELOPE, "Messages", onOpenChats, &_home_unread);
homeTile(tiles, UI_SYMBOL_USERS, "Nearby", onOpenNearby, NULL);
homeTile(tiles, UI_SYMBOL_MAP, "Map", onOpenMap, NULL);
homeTile(tiles, LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, NULL);
lv_obj_remove_flag(body, LV_OBJ_FLAG_SCROLLABLE); // a drag is a page swipe, not a scroll
home::s_row = lv_obj_create(body);
styleSurface(home::s_row, theme::BG);
lv_obj_remove_flag(home::s_row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(home::s_row, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(home::s_row, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(home::s_row, theme::GAP, 0);
home::s_dots = nullptr;
if (home::PAGES > 1) {
home::s_dots = lv_obj_create(body);
lv_obj_remove_style_all(home::s_dots);
lv_obj_set_size(home::s_dots, LV_SIZE_CONTENT, 22);
lv_obj_set_flex_flow(home::s_dots, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(home::s_dots, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(home::s_dots, 12, 0);
for (int p = 0; p < home::PAGES; p++) {
lv_obj_t* d = lv_obj_create(home::s_dots);
lv_obj_remove_style_all(d);
lv_obj_set_size(d, 8, 8);
lv_obj_set_style_radius(d, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_opa(d, LV_OPA_COVER, 0);
lv_obj_set_ext_click_area(d, 8);
lv_obj_add_flag(d, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(d, home::onDot, LV_EVENT_CLICKED, (void*)(uintptr_t)p);
}
}
setHomePage(home::s_page);
}
// From loop() on Home: turn the page once a press has moved SWIPE_PX sideways.
void UITask::homeSwipePoll() {
lv_indev_t* in = lv_indev_get_next(NULL);
if (!in || home::PAGES < 2) return;
bool down = lv_indev_get_state(in) == LV_INDEV_STATE_PRESSED;
lv_point_t p;
lv_indev_get_point(in, &p);
if (!down) { home::s_touching = false; return; }
if (!home::s_touching) { home::s_touching = true; home::s_swiped = false; home::s_start = p; return; }
if (home::s_swiped) return;
int dx = p.x - home::s_start.x, dy = p.y - home::s_start.y;
if (abs(dx) < home::SWIPE_PX || abs(dx) < 2 * abs(dy)) return;
home::s_swiped = true;
lv_indev_wait_release(in); // the swipe isn't also a tap on the tile it started on
setHomePage(home::s_page + (dx < 0 ? 1 : -1));
}
void UITask::setHomePage(int page) {
if (_screen != SCR_HOME || !home::s_row) return; // s_row went with an older screen
if (page < 0 || page >= home::PAGES) return;
home::s_page = page;
lv_obj_clean(home::s_row);
_home_unread = nullptr;
for (int i = page * home::PER_PAGE; i < (page + 1) * home::PER_PAGE; i++) {
if (i < home::COUNT) {
const home::App& a = home::APPS[i];
homeTile(home::s_row, a.icon, a.text, a.cb, a.unread ? &_home_unread : NULL);
} else { // keep a short last page's tiles the same width
lv_obj_t* gap = lv_obj_create(home::s_row);
lv_obj_remove_style_all(gap);
lv_obj_set_height(gap, 1);
lv_obj_set_flex_grow(gap, 1);
}
}
for (int i = 0; home::s_dots && i < (int)lv_obj_get_child_count(home::s_dots); i++)
lv_obj_set_style_bg_color(lv_obj_get_child(home::s_dots, i), lv_color_hex(i == page ? theme::ACCENT : theme::SURFACE_2), 0);
refreshHome();
}
void UITask::refreshHome() {
@@ -491,6 +642,7 @@ void UITask::refreshHome() {
lv_label_set_text(_home_clock, "--:--");
lv_label_set_text(_home_date, "time not synced");
}
if (!_home_unread) return;
int unread = _core->dmUnreadTotal() + _core->history.getTotalChannelUnread() + _core->roomUnread();
if (unread > 0) lv_label_set_text_fmt(_home_unread, "%d", unread);
else lv_label_set_text(_home_unread, "");
@@ -532,7 +684,7 @@ static lv_obj_t* listRow(lv_obj_t* parent, const char* title, const char* sub,
if (sub) {
lv_obj_t* s = label(row, sub, THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_label_set_long_mode(s, LV_LABEL_LONG_DOT);
lv_obj_set_width(s, 230);
lv_obj_set_size(s, 230, 15); // fixed height: LONG_DOT cuts instead of wrapping over the title
lv_obj_align(s, LV_ALIGN_BOTTOM_LEFT, theme::PAD, -5);
}
return row;
@@ -631,7 +783,7 @@ void UITask::buildContacts() {
// model, since it is a different set: who is in range, not who is known.
static NearbyModel::Entry s_node; // the node open in SCR_NODE (a copy: the list re-sorts)
enum : uint8_t { NODE_MSG, NODE_PING, NODE_FAV, NODE_ADD, NODE_DELETE };
enum : uint8_t { NODE_MSG, NODE_PING, NODE_FAV, NODE_ADD, NODE_DELETE, NODE_NAV };
static void onNearbyChip(lv_event_t* e) { s_ui->setNearbyFilter((uint8_t)(uintptr_t)lv_event_get_user_data(e)); }
static void onNearbySort(lv_event_t* e) { (void)e; s_ui->toggleNearbySort(); }
@@ -668,7 +820,8 @@ void UITask::buildNearby() {
lv_obj_set_style_pad_row(body, 4, 0);
if (_header) {
headerButton(_header, LV_SYMBOL_REFRESH " Scan", onNearbyScan, 4, NULL);
headerButton(_header, "", onNearbySort, 84, &_nearby_sort_lbl);
lv_obj_set_width(headerButton(_header, "", onNearbySort, 84, &_nearby_sort_lbl), 62); // fits "Recent" / "Dist"
headerButton(_header, UI_SYMBOL_MAP, onOpenNodesMap, 152, NULL); // the Nodes map
}
// Type filter chips
@@ -939,6 +1092,7 @@ void UITask::buildNode() {
bool contact = e.contact_idx >= 0;
if (contact && e.type == ADV_TYPE_CHAT) actionButton(acts, LV_SYMBOL_ENVELOPE " Message", NODE_MSG, true);
if (e.has_key) actionButton(acts, LV_SYMBOL_LOOP " Ping", NODE_PING, false);
if (e.lat_e6 != 0 || e.lon_e6 != 0) actionButton(acts, UI_SYMBOL_COMPASS, NODE_NAV, false);
if (contact) actionButton(acts, e.fav ? UI_SYMBOL_STAR " Unfav" : UI_SYMBOL_STAR " Fav", NODE_FAV, false);
if (!contact && e.has_key && !e.is_known) actionButton(acts, LV_SYMBOL_PLUS " Add", NODE_ADD, true);
if (contact) _node_delete_lbl = actionButton(acts, LV_SYMBOL_TRASH, NODE_DELETE, false);
@@ -1046,6 +1200,17 @@ void UITask::nodeAction(uint8_t action) {
showToast("Contacts full");
}
break;
case NODE_NAV:
// Followed by key when there is one (the Locator resolves live share,
// then advert position); otherwise a fixed point where it was seen.
if (e.has_prefix) navToNode(e.pub_key, e.lat_e6, e.lon_e6, e.name[0] ? e.name : "Node");
else {
_core->locator.setTarget(0, nullptr, e.lat_e6, e.lon_e6, e.name[0] ? e.name : "Node");
the_mesh.savePrefs();
openMap(true);
navFrameTarget();
}
break;
case NODE_DELETE:
if (!_delete_armed_ms) { // destructive: second tap within 3 s confirms
_delete_armed_ms = millis();
@@ -1063,6 +1228,9 @@ void UITask::nodeAction(uint8_t action) {
}
#include "MapScreen.h"
#include "NavMap.h"
#include "ClockScreen.h"
#include "RadioScreen.h"
#include "WifiScreen.h"
// ── Conversation ──────────────────────────────────────────────────────────────
@@ -1194,6 +1362,12 @@ void UITask::buildThread() {
lv_obj_add_event_cb(_compose_ta, onComposeClicked, LV_EVENT_CLICKED, _keyboard);
}
refreshThread();
if (_compose_ta && _share_text[0]) { // shareToMessage(): the text waits in the field
lv_textarea_set_text(_compose_ta, _share_text);
_share_text[0] = '\0';
lv_obj_update_layout(lv_screen_active());
setKeyboardVisible(true);
}
}
// Changes whenever the open conversation's content or delivery markers change.
@@ -1218,9 +1392,34 @@ uint32_t UITask::threadSignature() const {
return sig;
}
// Positions found in the shown messages ([WAY] / [LOC] / plain "lat,lon"),
// for the Go / Save buttons under such a bubble.
struct MsgLoc { int32_t lat, lon; char label[WAYPOINT_LABEL_LEN * 2]; };
static const int THREAD_MAX_SHOWN = 30;
static MsgLoc s_msg_locs[THREAD_MAX_SHOWN];
static int s_msg_loc_n = 0;
static void onMsgLoc(lv_event_t* e) {
uintptr_t v = (uintptr_t)lv_event_get_user_data(e);
s_ui->messageLocationAction((int)(v >> 1), (v & 1) != 0);
}
static void msgLocButton(lv_obj_t* parent, const char* text, int idx, bool save, bool accent) {
lv_obj_t* b = lv_button_create(parent);
lv_obj_set_size(b, LV_SIZE_CONTENT, 28);
lv_obj_set_style_pad_hor(b, 10, 0);
lv_obj_set_style_pad_ver(b, 0, 0);
lv_obj_set_style_radius(b, 14, 0);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(accent ? theme::ACCENT : theme::SURFACE_2), 0);
lv_obj_add_event_cb(b, onMsgLoc, LV_EVENT_CLICKED, (void*)(uintptr_t)((idx << 1) | (save ? 1 : 0)));
lv_obj_center(label(b, text, THEME_FONT_SMALL, accent ? theme::BG : theme::TEXT));
}
// One message bubble. Own messages right-aligned in amber, others left.
// loc_idx >= 0: the text carries a position (s_msg_locs[loc_idx]).
static void bubble(lv_obj_t* list, const char* from, const char* text, bool own,
uint32_t ts, const char* status, uint32_t status_col) {
uint32_t ts, const char* status, uint32_t status_col, int loc_idx = -1) {
// Full-width row that pushes the bubble to its side.
lv_obj_t* row = lv_obj_create(list);
styleSurface(row, theme::BG);
@@ -1267,6 +1466,20 @@ static void bubble(lv_obj_t* list, const char* from, const char* text, bool own,
lv_obj_set_style_max_width(t, 238, 0);
lv_obj_set_width(t, LV_SIZE_CONTENT);
if (loc_idx >= 0) { // position: navigate there / keep it as a waypoint
lv_obj_t* acts = lv_obj_create(b);
styleSurface(acts, theme::BG);
lv_obj_set_style_bg_opa(acts, LV_OPA_TRANSP, 0);
lv_obj_remove_flag(acts, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_remove_flag(acts, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(acts, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(acts, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(acts, 6, 0);
lv_obj_set_style_pad_top(acts, 2, 0);
msgLocButton(acts, UI_SYMBOL_COMPASS " Go", loc_idx, false, true);
msgLocButton(acts, UI_SYMBOL_FLAG " Save", loc_idx, true, false);
}
if (!meta_in_header) {
lv_obj_t* m = label(b, meta, THEME_FONT_SMALL, theme::TEXT_MUTED);
if (status) {
@@ -1276,13 +1489,43 @@ static void bubble(lv_obj_t* list, const char* from, const char* text, bool own,
}
}
// Remember the position in `text` (if any) for its bubble's buttons; label
// from a [WAY] tag, else the sender. Returns the slot or -1.
static int noteMsgLocation(const char* text, const char* sender) {
if (s_msg_loc_n >= THREAD_MAX_SHOWN) return -1;
MsgLoc& m = s_msg_locs[s_msg_loc_n];
if (!geo::parseLatLon(text, m.lat, m.lon, m.label, sizeof(m.label))) return -1;
if (!m.label[0]) snprintf(m.label, sizeof(m.label), "%s", sender && sender[0] ? sender : "Msg loc");
return s_msg_loc_n++;
}
void UITask::messageLocationAction(int idx, bool save) {
if (idx < 0 || idx >= s_msg_loc_n) return;
const MsgLoc& m = s_msg_locs[idx];
if (save) {
if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; }
if (_core->waypoints.add(m.lat, m.lon, rtc_clock.getCurrentTime(), m.label)) {
char t[48];
snprintf(t, sizeof(t), "Saved %s", _core->waypoints.at(_core->waypoints.count() - 1).label);
showToast(t);
}
return;
}
// A place snapshotted from the text (kind 0): nothing to keep re-resolving.
navSetTarget(0, nullptr, m.lat, m.lon, m.label);
openMap(true);
navFrameTarget();
refreshNavBar();
}
void UITask::refreshThread() {
if (!_thread_list) return;
_thread_dirty = false;
_thread_sig = threadSignature();
lv_obj_clean(_thread_list);
const MessageHistory& h = _core->history;
const int MAX_SHOWN = 30;
const int MAX_SHOWN = THREAD_MAX_SHOWN;
s_msg_loc_n = 0;
if (_thread_is_channel) {
int n = h.histCountForChannel(_thread_channel);
@@ -1302,7 +1545,8 @@ void UITask::refreshThread() {
const char* st = NULL; uint32_t col = theme::TEXT_MUTED;
if (own && e.relay_status == ACK_OK) { st = LV_SYMBOL_OK " relayed"; col = theme::OK; }
else if (own && e.relay_status == ACK_PENDING) st = "sent";
bubble(_thread_list, own ? NULL : from, body, own, e.timestamp, st, col);
bubble(_thread_list, own ? NULL : from, body, own, e.timestamp, st, col,
own ? -1 : noteMsgLocation(body, from));
}
} else {
int n = h.dmHistCountForContact(_thread_key);
@@ -1318,7 +1562,10 @@ void UITask::refreshThread() {
default: st = "sent"; break;
}
}
bubble(_thread_list, NULL, e.text, e.outgoing, e.timestamp, st, col);
char who[33] = "";
if (!e.outgoing) contactName(_thread_key, who, sizeof(who));
bubble(_thread_list, NULL, e.text, e.outgoing, e.timestamp, st, col,
e.outgoing ? -1 : noteMsgLocation(e.text, who));
}
}
if (lv_obj_get_child_count(_thread_list) == 0)
@@ -1375,12 +1622,197 @@ static lv_obj_t* dropdownRow(lv_obj_t* parent, const char* text, const char* opt
return dd;
}
// A preference toggle (0/1 byte in NodePrefs), saved on change.
static void onPrefSwitch(lv_event_t* e) {
uint8_t* pref = (uint8_t*)lv_event_get_user_data(e);
*pref = lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED) ? 1 : 0;
the_mesh.savePrefs();
}
static void onGpsSwitch(lv_event_t* e) {
s_ui->setGps(lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED));
}
// pref == nullptr: the caller wires the switch itself.
static lv_obj_t* switchRow(lv_obj_t* parent, const char* text, const char* sub, uint8_t* pref) {
lv_obj_t* row = lv_obj_create(parent);
styleSurface(row, theme::SURFACE);
lv_obj_remove_flag(row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(row, LV_PCT(100), theme::ROW_H);
lv_obj_set_style_radius(row, theme::RADIUS, 0);
lv_obj_t* l = label(row, text, THEME_FONT_BODY, theme::TEXT);
lv_obj_align(l, LV_ALIGN_TOP_LEFT, theme::PAD, sub ? 5 : 13);
if (sub) {
lv_obj_t* h = label(row, sub, THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_label_set_long_mode(h, LV_LABEL_LONG_DOT);
lv_obj_set_size(h, 236, 15); // clear of the switch
lv_obj_align(h, LV_ALIGN_BOTTOM_LEFT, theme::PAD, -5);
}
lv_obj_t* sw = lv_switch_create(row);
lv_obj_set_size(sw, 46, 24);
lv_obj_align(sw, LV_ALIGN_RIGHT_MID, -theme::PAD, 0);
if (pref) {
if (*pref) lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_add_event_cb(sw, onPrefSwitch, LV_EVENT_VALUE_CHANGED, pref);
}
return sw;
}
// ── Schema-driven settings (ui-core/SettingsSchema.h) ─────────────────────────
static void onSchemaSwitch(lv_event_t* e) {
s_ui->setSchemaValue((int)(uintptr_t)lv_event_get_user_data(e),
lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED) ? 1 : 0);
}
static void onSchemaDropdown(lv_event_t* e) {
s_ui->setSchemaValue((int)(uintptr_t)lv_event_get_user_data(e),
(int)lv_dropdown_get_selected((lv_obj_t*)lv_event_get_target(e)));
}
static void onOpenSchemaPage(lv_event_t* e) { s_ui->showSchemaSettings((int)(uintptr_t)lv_event_get_user_data(e)); }
static void onPruneContacts(lv_event_t* e) { (void)e; s_ui->pruneContacts(); }
void UITask::showSchemaSettings(int page) {
_screen = SCR_SETTINGS_NAV;
_settings_page = (uint8_t)(page < settings::PG_COUNT ? page : 0);
buildSchemaSettings();
}
// Contacts > prune now: first tap shows how many, the second (within 3 s) removes them.
void UITask::pruneContacts() {
int n = the_mesh.countStaleContacts();
if (n == 0) {
showToast(_prefs && _prefs->contact_expiry_idx == 0 ? "Contact expiry is off" : "No inactive contacts");
return;
}
if (!_prune_armed_ms || millis() - _prune_armed_ms > 3000) {
_prune_armed_ms = millis() | 1;
if (_prune_lbl) lv_label_set_text_fmt(_prune_lbl, LV_SYMBOL_TRASH " Remove %d contact%s?", n, n == 1 ? "" : "s");
return;
}
_prune_armed_ms = 0;
int removed = the_mesh.pruneStaleContacts();
char t[40];
snprintf(t, sizeof(t), "Removed %d contact%s", removed, removed == 1 ? "" : "s");
showToast(t);
if (_prune_lbl) lv_label_set_text(_prune_lbl, LV_SYMBOL_TRASH " Remove inactive contacts now");
}
void UITask::applyDisplayPrefs() {
if (!_prefs) return;
if (_prefs->display_brightness_pct) lvport::setBacklightPct(_prefs->display_brightness_pct);
else if (_display) _display->setBrightness(_prefs->display_brightness);
}
// Brightness slider: live while dragging, saved on release.
static void onBrightnessSlider(lv_event_t* e) {
lv_obj_t* sl = (lv_obj_t*)lv_event_get_target(e);
s_ui->setBrightnessPct((uint8_t)lv_slider_get_value(sl), lv_event_get_code(e) == LV_EVENT_RELEASED);
}
void UITask::setBrightnessPct(uint8_t pct, bool save) {
if (!_prefs) return;
_prefs->display_brightness_pct = pct;
_prefs->display_brightness = (uint8_t)((pct + 12) / 25 > 4 ? 4 : (pct + 12) / 25); // nearest level, for anything reading it
applyDisplayPrefs();
if (save) the_mesh.savePrefs();
}
void UITask::buildSchemaSettings() {
lv_obj_t* body = newScreen(settings::pageTitle(_settings_page), true);
_prune_lbl = nullptr;
_prune_armed_ms = 0;
uint8_t sec = 0xFF;
for (int i = 0; i < settings::COUNT; i++) {
const settings::Setting& st = settings::ALL[i];
if (settings::sectionPage(st.section) != _settings_page) continue;
if (st.section != sec) { sec = st.section; sectionTitle(body, settings::sectionTitle(sec)); }
uint8_t v = settings::get(*_prefs, st);
if (st.offset == offsetof(NodePrefs, display_brightness)) { // a slider here instead of five steps
lv_obj_t* row = lv_obj_create(body);
styleSurface(row, theme::SURFACE);
lv_obj_remove_flag(row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(row, LV_PCT(100), theme::ROW_H);
lv_obj_set_style_radius(row, theme::RADIUS, 0);
lv_obj_align(label(row, st.label, THEME_FONT_BODY, theme::TEXT), LV_ALIGN_LEFT_MID, theme::PAD, 0);
lv_obj_t* sl = lv_slider_create(row);
lv_slider_set_range(sl, 5, 100);
uint8_t pct = _prefs->display_brightness_pct ? _prefs->display_brightness_pct
: (uint8_t)(_prefs->display_brightness * 25 > 5 ? _prefs->display_brightness * 25 : 5);
lv_slider_set_value(sl, pct, LV_ANIM_OFF);
lv_obj_set_size(sl, 170, 10);
lv_obj_align(sl, LV_ALIGN_RIGHT_MID, -18, 0);
lv_obj_set_ext_click_area(sl, 14);
lv_obj_add_event_cb(sl, onBrightnessSlider, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(sl, onBrightnessSlider, LV_EVENT_RELEASED, NULL);
continue;
}
if (!st.option) {
lv_obj_t* sw = switchRow(body, st.label, st.hint, nullptr);
if (v) lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_add_event_cb(sw, onSchemaSwitch, LV_EVENT_VALUE_CHANGED, (void*)(uintptr_t)i);
continue;
}
char opts[160];
int o = 0;
for (uint8_t k = 0; k < st.count && o < (int)sizeof(opts) - 16; k++) {
if (k) opts[o++] = '\n';
st.option(k, opts + o, sizeof(opts) - o, *_prefs);
o += strlen(opts + o);
}
opts[o] = '\0';
lv_obj_t* row = lv_obj_create(body);
styleSurface(row, theme::SURFACE);
lv_obj_remove_flag(row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(row, LV_PCT(100), theme::ROW_H);
lv_obj_set_style_radius(row, theme::RADIUS, 0);
lv_obj_t* l = label(row, st.label, THEME_FONT_BODY, theme::TEXT);
lv_obj_align(l, LV_ALIGN_TOP_LEFT, theme::PAD, st.hint ? 5 : 13);
if (st.hint) {
lv_obj_t* h = label(row, st.hint, THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_label_set_long_mode(h, LV_LABEL_LONG_DOT);
lv_obj_set_size(h, 176, 15); // fixed height: cut, don't wrap onto the label
lv_obj_align(h, LV_ALIGN_BOTTOM_LEFT, theme::PAD, -5);
}
lv_obj_t* dd = lv_dropdown_create(row);
lv_dropdown_set_options(dd, opts);
lv_dropdown_set_selected(dd, v);
lv_obj_set_width(dd, 112);
lv_obj_align(dd, LV_ALIGN_RIGHT_MID, -4, 0);
lv_obj_add_event_cb(dd, onSchemaDropdown, LV_EVENT_VALUE_CHANGED, (void*)(uintptr_t)i);
}
if (_settings_page == settings::PG_MESSAGES) { // the action that goes with "Contact expiry"
lv_obj_t* b = lv_button_create(body);
lv_obj_set_size(b, LV_PCT(100), 38);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::SURFACE), 0);
lv_obj_add_event_cb(b, onPruneContacts, LV_EVENT_CLICKED, NULL);
_prune_lbl = label(b, LV_SYMBOL_TRASH " Remove inactive contacts now", THEME_FONT_BODY, theme::TEXT);
lv_obj_center(_prune_lbl);
}
}
void UITask::setSchemaValue(int idx, int v) {
if (!_prefs || idx < 0 || idx >= settings::COUNT) return;
const settings::Setting& st = settings::ALL[idx];
settings::set(*_prefs, st, (uint8_t)v);
if (st.changed) st.changed(*_core);
the_mesh.savePrefs();
if (st.offset == offsetof(NodePrefs, units_imperial)) { // other labels depend on it
lv_obj_t* body = _body;
int32_t y = body ? lv_obj_get_scroll_y(body) : 0;
buildSchemaSettings();
if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); }
}
}
void UITask::showSettings() {
_screen = SCR_SETTINGS;
buildSettings();
}
static void onOpenWifi(lv_event_t* e) { (void)e; s_ui->showWifi(false); }
static void onOpenRadio(lv_event_t* e); // RadioScreen.h
void UITask::buildSettings() {
lv_obj_t* body = newScreen("Settings", true);
@@ -1388,6 +1820,29 @@ void UITask::buildSettings() {
char ssid[33], pass[65];
bool have = lvport::loadWifi(ssid, sizeof(ssid), pass, sizeof(pass));
listRow(body, LV_SYMBOL_WIFI " WiFi", have ? ssid : "Not set (for map downloads)", onOpenWifi, NULL);
if (_prefs) {
char sub[48];
int pi = radioctl::currentPreset(_prefs);
const char* pn = "Custom"; float f, b; uint8_t sf, cr;
if (pi >= 0) radioctl::presetAt(_prefs, pi, pn, f, b, sf, cr);
snprintf(sub, sizeof(sub), "%s - %.3f MHz, %d dBm", pn, _prefs->freq, _prefs->tx_power_dbm);
listRow(body, UI_SYMBOL_RADIO " Radio", sub, onOpenRadio, NULL);
}
if (_prefs) { // the same NodePrefs switches as ui-new's Home GPS toggle / Live share / Locator screens
sectionTitle(body, "NAVIGATION");
if (_core->gpsAvailable()) {
lv_obj_t* sw = switchRow(body, "GPS", "Position for maps, sharing and adverts", nullptr);
if (_core->gpsEnabled()) lv_obj_add_state(sw, LV_STATE_CHECKED);
lv_obj_add_event_cb(sw, onGpsSwitch, LV_EVENT_VALUE_CHANGED, NULL);
}
listRow(body, UI_SYMBOL_COMPASS " Trail, live share, alerts", "Point spacing, auto-pause, sharing, radius",
onOpenSchemaPage, (void*)(uintptr_t)settings::PG_NAV);
sectionTitle(body, "DEVICE");
listRow(body, LV_SYMBOL_EYE_OPEN " Display & power", "Brightness, screen off, battery, GPS, time zone",
onOpenSchemaPage, (void*)(uintptr_t)settings::PG_DEVICE);
listRow(body, LV_SYMBOL_ENVELOPE " Messages & contacts", "Resending, contact expiry, sorting",
onOpenSchemaPage, (void*)(uintptr_t)settings::PG_MESSAGES);
}
sectionTitle(body, "KEYBOARD");
uint8_t main_a = _prefs ? _prefs->keyboard_main_alphabet : 0;
uint8_t alt_a = _prefs ? _prefs->keyboard_alt_alphabet : 0;
@@ -1399,6 +1854,16 @@ void UITask::buildSettings() {
s_kb_alt_dd = dropdownRow(body, "Additional", "None\nLatin\nCyrillic\nGreek",
alt_a == main_a ? 0 : alt_a + 1);
label(body, "Hold a letter for accents and other variants.", THEME_FONT_SMALL, theme::TEXT_MUTED);
sectionTitle(body, "ABOUT");
char about[200], built[24] = "";
if (!strstr(FIRMWARE_VERSION, FIRMWARE_BUILD_DATE)) snprintf(built, sizeof(built), " (%s)", FIRMWARE_BUILD_DATE);
snprintf(about, sizeof(about), "%s\nFirmware %s%s\n\nMap data: %s", the_mesh.getNodeName(), FIRMWARE_VERSION, built,
(lvport::mountStorage() && mapview::s_provider->available()) ? mapview::s_provider->attribution()
: "\xC2\xA9 OpenStreetMap contributors");
lv_obj_t* a = label(body, about, THEME_FONT_SMALL, theme::TEXT);
lv_label_set_long_mode(a, LV_LABEL_LONG_WRAP);
lv_obj_set_width(a, LV_PCT(100));
}
void UITask::setKeyboardAlphabets(int main_idx, int alt_sel) {
+110 -2
View File
@@ -32,14 +32,23 @@ public:
bool isViewingChannel(uint8_t channel_idx) override;
bool isViewingDM(const uint8_t* pub_key) override;
void onViewedHistoryGrew(bool channel) override { (void)channel; _thread_dirty = true; }
void botSetGPS(bool on) override;
// ── Navigation (called from LVGL event callbacks) ────────────────────────
void showHome();
void setHomePage(int page);
void showChats();
void showContacts();
void showSettings();
void showSchemaSettings(int page);
void pruneContacts();
void setBrightnessPct(uint8_t pct, bool save);
void applyDisplayPrefs() override;
void setSchemaValue(int idx, int v);
void showNearby();
void showMap();
void showMap(); // in its current mode
void openMap(bool nav); // Nodes map (false) or Navigation map (true)
void mapLongPress(int x, int y);
void mapZoom(int delta);
void mapCenterOnMe();
void mapPan(int dx, int dy);
@@ -48,7 +57,27 @@ public:
void mapDownloadClose();
void mapDownloadStart();
void mapDownloadStop();
void mapDownloadResume();
void mapDownloadDiscard();
void mapDownloadZmax(int delta);
// Navigation map (NavMap.h)
void navTargetsPopup();
void navClosePopup();
void navPick(int code);
void navWaypointMenu(int idx);
void navWaypointAction(uint8_t act);
void navRenameDone(bool ok);
void navMarkHere();
void navAveragingCancel();
void navToNode(const uint8_t* key, int32_t lat, int32_t lon, const char* name);
void navToolsPopup();
void navToolAction(uint8_t act);
void navSetShareTarget(int sel);
void navWaypointsPopup();
void navCoordsPopup();
void navCoordsDone(bool ok);
void shareToMessage(const char* text);
void messageLocationAction(int idx, bool save);
void showWifi(bool from_map);
void wifiScan();
void wifiPick(int idx);
@@ -69,22 +98,37 @@ public:
void sendFromCompose();
void setKeyboardVisible(bool show);
void showToast(const char* text, uint32_t ms = 2500);
// Clock tools (ClockScreen.h)
void showClock();
void clockTab(int tab);
void clockAction(uint8_t act);
void setAlarm(int which, int v);
void dismissRing();
// Settings > Radio (RadioScreen.h)
void showRadio();
void radioSet(int which, int v);
void radioFreqPopup();
void radioFreqDone(bool ok);
void setGps(bool on);
bool ensureGps(); // true with a fix; else turns GPS on / says it's waiting
// Kept for main.cpp's sim hooks (ui-new API).
void openContactDM(const ContactInfo& ci) { openDM(ci.id.pub_key); }
void openAdminFor(const ContactInfo& ci, bool from_picker) { (void)ci; (void)from_picker; }
private:
enum Screen : uint8_t { SCR_HOME, SCR_CHATS, SCR_CONTACTS, SCR_THREAD, SCR_SETTINGS, SCR_NEARBY, SCR_NODE, SCR_MAP, SCR_WIFI };
enum Screen : uint8_t { SCR_HOME, SCR_CHATS, SCR_CONTACTS, SCR_THREAD, SCR_SETTINGS, SCR_NEARBY, SCR_NODE, SCR_MAP, SCR_WIFI, SCR_SETTINGS_NAV, SCR_CLOCK, SCR_RADIO };
void buildStatusBar();
void refreshStatusBar();
lv_obj_t* newScreen(const char* title, bool with_back);
void buildHome();
void refreshHome();
void homeSwipePoll();
void buildChats();
void buildContacts();
void buildSettings();
void buildSchemaSettings();
void buildNearby();
void refreshNearbyList();
uint32_t nearbySignature() const;
@@ -94,6 +138,27 @@ private:
void layoutMap();
void mapLoop();
void rebuildMapMarkers();
void rebuildNodeMarkers();
void addMapMark(uint8_t kind, int idx, int32_t lat_e6, int32_t lon_e6, uint32_t col, const char* text);
void mapPointToLatLon(int x, int y, int32_t& lat_e6, int32_t& lon_e6) const;
double mapCenterBias() const;
void buildNavLayers();
void buildNavControls(lv_obj_t* body);
void rebuildNavMarkers();
void layoutNav();
void refreshNavBar();
void navFrameTarget();
void navSetTarget(uint8_t kind, const uint8_t* key, int32_t lat, int32_t lon, const char* name);
lv_obj_t* navPopupPanel(const char* title, bool full);
void navRenamePopup(int idx);
void navAddWaypoint(int32_t lat, int32_t lon);
void navDropAt(int x, int y);
void navSpotPopup(int32_t lat, int32_t lon);
void refreshNavTools();
void navPollAveraging();
void navPollTrackBack();
bool navCurrentTarget(int32_t& lat, int32_t& lon, char* name, int n, bool& person, bool& set);
bool exportTrailGpx(char* name_out, size_t n);
void mapDownloadTick();
void refreshDownloadPopup();
void buildWifi();
@@ -103,11 +168,19 @@ private:
void buildThread();
void refreshThread();
uint32_t threadSignature() const;
void buildClock();
void buildRadio();
void radioCloseFreq();
bool radioPopupOpen() const;
void refreshClock();
void showRing(const char* text);
void hideRing();
void drainCoreEvents();
void onMessageArrived(const UiEvent& ev);
void wake();
void sleep();
uint32_t autoOffMillis() const;
void checkLowBattery();
DisplayDriver* _display = nullptr;
SensorManager* _sensors = nullptr;
@@ -118,6 +191,13 @@ private:
bool _asleep = false;
uint32_t _next_status_ms = 0;
uint32_t _next_thread_check_ms = 0;
uint32_t _next_trackback_ms = 0;
uint32_t _next_clock_ms = 0;
uint8_t _settings_page = 0; // settings::Page shown in SCR_SETTINGS_NAV
uint32_t _prune_armed_ms = 0;
uint16_t _batt_mv = 0; // smoothed, for the low-battery shutdown
uint32_t _next_batt_ms = 0;
lv_obj_t* _prune_lbl = nullptr;
// Open conversation (SCR_THREAD)
bool _thread_is_channel = false;
@@ -129,6 +209,7 @@ private:
// Widgets
lv_obj_t* _status_time = nullptr;
lv_obj_t* _status_icons = nullptr;
lv_obj_t* _status_gps = nullptr;
lv_obj_t* _toast = nullptr;
lv_timer_t* _toast_timer = nullptr;
lv_obj_t* _home_clock = nullptr;
@@ -181,9 +262,36 @@ private:
lv_obj_t* _dl_info = nullptr;
lv_obj_t* _dl_zoom_lbl = nullptr;
lv_obj_t* _dl_start_lbl = nullptr;
lv_obj_t* _dl_job_row = nullptr; // "Unfinished ... Resume" in the popup
lv_obj_t* _dl_job_lbl = nullptr;
int _dl_zmax = 0;
uint8_t _dl_last_state = 0;
// Navigation map (SCR_MAP with _map_nav)
bool _map_nav = false;
uint32_t _next_nav_bar_ms = 0;
lv_obj_t* _nav_bar = nullptr; // target bar along the bottom
lv_obj_t* _nav_title = nullptr;
lv_obj_t* _nav_info = nullptr;
lv_obj_t* _nav_clear = nullptr;
lv_obj_t* _nav_overlay = nullptr; // target list / waypoint menu / rename
lv_obj_t* _nav_ta = nullptr;
lv_obj_t* _nav_kb = nullptr;
lv_obj_t* _nav_del_lbl = nullptr;
uint32_t _nav_del_armed_ms = 0;
int _nav_wp = -1; // waypoint the menu / rename is about
lv_obj_t* _nav_rec = nullptr; // "REC 1.2 km LIVE 58m" pill
lv_obj_t* _nav_avg_pill = nullptr; // GPS averaging progress (tap: cancel)
lv_obj_t* _nav_trail_lbl = nullptr; // tools panel
lv_obj_t* _nav_trail_btn = nullptr;
lv_obj_t* _nav_reset_lbl = nullptr;
lv_obj_t* _nav_share_lbl = nullptr;
lv_obj_t* _nav_share_btn = nullptr;
lv_obj_t* _nav_tb_btn = nullptr;
uint32_t _nav_reset_armed_ms = 0;
int32_t _nav_spot_lat = 0, _nav_spot_lon = 0; // the spot a long-press picked
char _share_text[96] = ""; // waiting for a conversation to be picked (shareToMessage)
// WiFi settings (SCR_WIFI)
bool _wifi_from_map = false;
bool _wifi_scanning = false;
@@ -27,8 +27,10 @@ TEXT="0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0
TEXT="$TEXT,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122"
# LVGL built-in symbols (same list as lvgl/scripts/built_in_font/built_in_font_gen.py)
# plus 61612 = globe (keyboard script key), 61445 = star (favourite),
# 62073 = map, 61632 = users (home tiles)
SYMS="61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650"
# 62073 = map, 61632 = users (home tiles), 61774 = compass (Navigate),
# 61476 = flag (waypoint), 62405 = map marker (mark here), 61463 = clock,
# 62194 = stopwatch, 62745 = broadcast tower (radio)
SYMS="62745,61463,62194,61612,61445,62073,61632,61774,61476,62405,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650"
CONV="npx -y lv_font_conv@1.5.3"
gen() { # size weight ranges name
@@ -38,7 +40,7 @@ gen() { # size weight ranges name
--format lvgl --lv-include lvgl.h --lv-font-name "$4" -o "$HERE/$4.c"
}
for s in 12 14 16 20; do gen "$s" Medium "$TEXT" "ui_font_$s"; done
# Clock: digits, colon, dash, space only.
$CONV --bpp 4 --size 40 --no-compress --font "$CACHE/NotoSans-SemiBold.ttf" -r "0x20,0x2D,0x30-0x3A" \
# Clock: digits, colon, dash, dot, space only.
$CONV --bpp 4 --size 40 --no-compress --font "$CACHE/NotoSans-SemiBold.ttf" -r "0x20,0x2D-0x2E,0x30-0x3A" \
--format lvgl --lv-include lvgl.h --lv-font-name ui_font_40 -o "$HERE/ui_font_40.c"
echo "done: $(ls "$HERE"/ui_font_*.c | wc -l) fonts"
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 12 px
* Bpp: 4
* Opts: --bpp 4 --size 12 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_12 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_12.c
* Opts: --bpp 4 --size 12 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 62745,61463,62194,61612,61445,62073,61632,61774,61476,62405,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_12 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_12.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -6457,6 +6457,16 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x1, 0xcb, 0xfa, 0x1, 0xff, 0xca, 0x60, 0x1,
0x1, 0x80, 0x4e, 0x0,
/* U+F017 "" */
0x0, 0xc2, 0x68, 0x20, 0x1e, 0x2b, 0xec, 0xbe,
0xb2, 0x0, 0x93, 0x50, 0x3, 0x26, 0xa0, 0xd,
0x0, 0x5d, 0xc0, 0xa, 0x86, 0x44, 0x3, 0xf0,
0xca, 0x0, 0x7f, 0x90, 0x40, 0x38, 0x48, 0x2,
0x11, 0x0, 0x67, 0xd, 0x40, 0x0, 0xa0, 0x6,
0x9b, 0x26, 0x0, 0x24, 0x80, 0x72, 0x69, 0x0,
0x24, 0x6c, 0x3, 0xf5, 0x88, 0x26, 0x18, 0x6,
0x3c, 0x40, 0x8, 0xf3, 0x6e, 0xdb, 0x86, 0x0,
/* U+F019 "" */
0x0, 0xc4, 0xee, 0x20, 0xf, 0xcb, 0x10, 0x50,
0xf, 0xde, 0x0, 0xf0, 0xf, 0xfe, 0x96, 0xfb,
@@ -6486,6 +6496,14 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x9b, 0x79, 0x83, 0x78, 0x47, 0x76, 0x91, 0x0,
0x0,
/* U+F024 "" */
0x3, 0x0, 0xff, 0x9f, 0x34, 0x3, 0xff, 0x80,
0x55, 0x94, 0xc2, 0x4, 0xf6, 0xc, 0x0, 0x53,
0x59, 0xef, 0xd8, 0x42, 0x0, 0xff, 0xfb, 0x8b,
0x44, 0x10, 0x2, 0x18, 0x20, 0x4, 0x4b, 0xba,
0xff, 0x75, 0xce, 0x1, 0xfc, 0x44, 0x0, 0x85,
0x54, 0x1, 0xfe,
/* U+F026 "" */
0x0, 0xc5, 0x40, 0x11, 0x62, 0xbc, 0x43, 0x4,
0x21, 0xdc, 0x20, 0x1f, 0xfc, 0x22, 0x0, 0xed,
@@ -6793,6 +6811,17 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xe0, 0x30, 0x7, 0xff, 0x6, 0x40, 0x3f, 0xce,
0xc, 0x20, 0x1f, 0xe9, 0x98, 0x3, 0x80,
/* U+F14E "" */
0x0, 0xc8, 0xca, 0x40, 0x1e, 0x5e, 0xb9, 0xad,
0xb2, 0x0, 0x9e, 0x84, 0x3, 0x26, 0x10, 0x1c,
0x0, 0x71, 0x28, 0xf8, 0x48, 0x6, 0x3b, 0xdc,
0x2, 0x44, 0x0, 0x51, 0xea, 0x16, 0x0, 0xc1,
0x0, 0xb, 0xf3, 0x21, 0x0, 0x4, 0x40, 0x9,
0xd, 0x78, 0x0, 0xb9, 0xc0, 0xc, 0x2f, 0xc6,
0x1, 0x25, 0x80, 0x23, 0xa0, 0x40, 0x24, 0x32,
0xb0, 0x60, 0xe, 0x3a, 0x0, 0x26, 0xa0, 0x4,
0x2f, 0x82, 0x1, 0x15, 0xfe, 0xeb, 0xa0, 0x2,
/* U+F15B "" */
0x9b, 0xbc, 0x4e, 0x0, 0x64, 0x4c, 0xd1, 0x20,
0x1f, 0x9a, 0x40, 0x38, 0x6e, 0xda, 0x1, 0xd5,
@@ -6889,6 +6918,16 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x1, 0x38, 0x2a, 0x86, 0x42, 0x81, 0xc0, 0xe5,
0xdf, 0xd1, 0x20,
/* U+F2F2 "" */
0x0, 0x92, 0x20, 0xa0, 0x1f, 0x73, 0xbb, 0x80,
0x3e, 0xa3, 0x2b, 0x0, 0xf4, 0x79, 0x97, 0xc4,
0x1c, 0x1, 0x4e, 0x6, 0x80, 0xee, 0xe0, 0x45,
0x0, 0x44, 0x0, 0x2c, 0xe, 0x0, 0xfd, 0xc0,
0xe0, 0x1f, 0x94, 0x4, 0x2, 0x17, 0x0, 0x8c,
0x10, 0x2, 0xab, 0x0, 0x90, 0x24, 0x40, 0x3c,
0x30, 0x3, 0xa2, 0x1, 0x87, 0x44, 0x0, 0x7d,
0xa, 0xa8, 0xf3, 0x0, 0x0,
/* U+F304 "" */
0x0, 0xfe, 0x17, 0x10, 0xf, 0xe1, 0xf8, 0xc1,
0x0, 0xf8, 0x50, 0x40, 0xb4, 0x3, 0xc3, 0xfe,
@@ -6900,6 +6939,28 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x3, 0xc4, 0x0, 0x1c, 0x20, 0xf, 0xa6, 0x57,
0xe4, 0x1, 0xf0,
/* U+F3C5 "" */
0x0, 0xa, 0xba, 0x88, 0x4, 0xbd, 0x51, 0x5c,
0xa0, 0x74, 0x1, 0xd4, 0x70, 0x0, 0x8d, 0x80,
0x4, 0x20, 0x23, 0x93, 0xa0, 0x20, 0x7, 0xf2,
0x2, 0x39, 0x3a, 0x2, 0x40, 0x2, 0x36, 0x0,
0x10, 0x6c, 0x1, 0xcc, 0x61, 0x24, 0x1, 0x14,
0x80, 0x5c, 0x1, 0x70, 0x6, 0x37, 0x7, 0x30,
0xe, 0x8a, 0x80, 0x8,
/* U+F519 "" */
0x14, 0x0, 0xff, 0x90, 0x62, 0xc0, 0x3f, 0xeb,
0x84, 0x50, 0x97, 0x5, 0xb5, 0x7, 0x90, 0x54,
0x22, 0x3, 0x78, 0x5a, 0x58, 0x7b, 0x1, 0x10,
0x44, 0x2, 0x40, 0x60, 0x60, 0x42, 0x2, 0x25,
0x40, 0xcb, 0x22, 0x9, 0x12, 0xf0, 0x11, 0x7d,
0x80, 0x86, 0xc3, 0x2, 0xc4, 0x85, 0xbd, 0x2c,
0x2, 0x66, 0x13, 0x30, 0x40, 0x6d, 0x0, 0x33,
0x3, 0x7b, 0x3, 0x0, 0x7e, 0xb2, 0x77, 0x8a,
0x80, 0x3e, 0x42, 0x88, 0xe2, 0x40, 0xf, 0x70,
0xa8, 0x4, 0xa3, 0xe0, 0x1e, 0x9a, 0x0, 0xea,
0x80, 0x8,
/* U+F55A "" */
0x0, 0x97, 0xbf, 0xff, 0xa0, 0x0, 0xb4, 0x20,
0x1f, 0xce, 0xb, 0x40, 0x13, 0x71, 0x13, 0x98,
@@ -7958,60 +8019,66 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 31387, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31462, .adv_w = 216, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31522, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31576, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31624, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31697, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31720, .adv_w = 144, .box_w = 9, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31755, .adv_w = 216, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31829, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31871, .adv_w = 132, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31921, .adv_w = 168, .box_w = 8, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 31958, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32004, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32031, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32048, .adv_w = 168, .box_w = 8, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 32086, .adv_w = 168, .box_w = 12, .box_h = 11, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 32133, .adv_w = 120, .box_w = 7, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32169, .adv_w = 120, .box_w = 7, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32206, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32245, .adv_w = 168, .box_w = 11, .box_h = 3, .ofs_x = 0, .ofs_y = 3},
{.bitmap_index = 32254, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 32316, .adv_w = 240, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32407, .adv_w = 216, .box_w = 15, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32482, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32541, .adv_w = 168, .box_w = 11, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 32575, .adv_w = 168, .box_w = 11, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 32609, .adv_w = 240, .box_w = 16, .box_h = 10, .ofs_x = -1, .ofs_y = 0},
{.bitmap_index = 32669, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 32689, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32746, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32810, .adv_w = 186, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32880, .adv_w = 240, .box_w = 15, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32954, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33015, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33055, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33099, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33131, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33170, .adv_w = 120, .box_w = 9, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33216, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33261, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33314, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33370, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33433, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33456, .adv_w = 240, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33531, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33574, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33623, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33672, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33721, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33763, .adv_w = 216, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33800, .adv_w = 240, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33865, .adv_w = 168, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33929, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33972, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 34039, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 34094, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34123, .adv_w = 193, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0}
{.bitmap_index = 31586, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31640, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31688, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31761, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31804, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31827, .adv_w = 144, .box_w = 9, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31862, .adv_w = 216, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31936, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31978, .adv_w = 132, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32028, .adv_w = 168, .box_w = 8, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 32065, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32111, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32138, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32155, .adv_w = 168, .box_w = 8, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 32193, .adv_w = 168, .box_w = 12, .box_h = 11, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 32240, .adv_w = 120, .box_w = 7, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32276, .adv_w = 120, .box_w = 7, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32313, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32352, .adv_w = 168, .box_w = 11, .box_h = 3, .ofs_x = 0, .ofs_y = 3},
{.bitmap_index = 32361, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 32423, .adv_w = 240, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32514, .adv_w = 216, .box_w = 15, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32589, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32648, .adv_w = 168, .box_w = 11, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 32682, .adv_w = 168, .box_w = 11, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 32716, .adv_w = 240, .box_w = 16, .box_h = 10, .ofs_x = -1, .ofs_y = 0},
{.bitmap_index = 32776, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 32796, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32853, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32917, .adv_w = 186, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32987, .adv_w = 240, .box_w = 15, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33061, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33122, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33162, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33206, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33238, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33277, .adv_w = 120, .box_w = 9, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33323, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33368, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33421, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33477, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33540, .adv_w = 186, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33612, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33635, .adv_w = 240, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33710, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33753, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33802, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33851, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33900, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33942, .adv_w = 216, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33979, .adv_w = 240, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 34044, .adv_w = 168, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34108, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34151, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34212, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 34279, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34331, .adv_w = 240, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34413, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 34468, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34497, .adv_w = 193, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0}
};
/*---------------------
@@ -8036,14 +8103,14 @@ static const uint16_t unicode_list_10[] = {
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 0xceef, 0xcef2, 0xcef5, 0xcef6, 0xcef7,
0xcefb, 0xcefd, 0xceff, 0xcf03, 0xcf06, 0xcf0b, 0xcf10, 0xcf11,
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
0xcefb, 0xcefd, 0xceff, 0xcf01, 0xcf03, 0xcf06, 0xcf0b, 0xcf0e,
0xcf10, 0xcf11, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36,
0xcf37, 0xcf3b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58,
0xcf5a, 0xcf5b, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d,
0xcf7f, 0xcf96, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca,
0xcfd1, 0xcfd4, 0xcfdd, 0xd006, 0xd00e, 0xd038, 0xd045, 0xd0d5,
0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d,
0xd1d7, 0xd1dc, 0xd1ee, 0xd2af, 0xd403, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -8099,7 +8166,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
},
{
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 72, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -8246,7 +8313,8 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -8387,7 +8455,8 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Kern values between classes*/
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 14 px
* Bpp: 4
* Opts: --bpp 4 --size 14 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_14 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_14.c
* Opts: --bpp 4 --size 14 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 62745,61463,62194,61612,61445,62073,61632,61774,61476,62405,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_14 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_14.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -7348,6 +7348,18 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x63, 0x0, 0xfc, 0x8e, 0xe2, 0x0, 0xff, 0xe6,
0x10, 0x5, 0xc0, 0x6, 0x0, 0x9, 0x80, 0x0,
/* U+F017 "" */
0x0, 0xff, 0xe2, 0x8c, 0x6f, 0xfb, 0x60, 0x40,
0x39, 0x39, 0xc8, 0x0, 0x4f, 0xc8, 0x1, 0x25,
0x80, 0x4a, 0xa0, 0xa, 0xd0, 0x1, 0x40, 0x1a,
0xa8, 0x1, 0xa8, 0x18, 0x40, 0x3f, 0xc2, 0xd8,
0x1, 0xff, 0xc0, 0xc6, 0x0, 0xff, 0xe0, 0x33,
0x80, 0x70, 0x87, 0x18, 0x4, 0xe6, 0x1, 0xde,
0x63, 0x0, 0x11, 0xd8, 0x7, 0xe, 0x4d, 0x0,
0x56, 0x6a, 0x1, 0xe6, 0x10, 0x2, 0x98, 0x51,
0x80, 0x7e, 0x3a, 0x0, 0xb1, 0xc0, 0x3c, 0xf8,
0x1, 0xd1, 0xd5, 0x10, 0xae, 0x80, 0x8,
/* U+F019 "" */
0x0, 0xf1, 0x98, 0x3, 0xfe, 0xbc, 0xc5, 0x80,
0x7f, 0x9c, 0x0, 0xe0, 0x1f, 0xfe, 0x58, 0xff,
@@ -7383,6 +7395,16 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x5, 0xea, 0x2f, 0x76, 0x71, 0x3f, 0x10, 0x1,
0x8d, 0x6b, 0xa2, 0x1a, 0xf0, 0x40, 0x0,
/* U+F024 "" */
0x1, 0x0, 0xff, 0xe0, 0xb7, 0x70, 0x40, 0x3f,
0xec, 0x1, 0x75, 0x51, 0x0, 0x71, 0x98, 0x38,
0x1, 0x35, 0x4d, 0xe9, 0x79, 0xdc, 0xc0, 0x28,
0x7, 0x85, 0xa1, 0x88, 0x3, 0xff, 0xfe, 0x1,
0xff, 0xc4, 0x20, 0x8, 0xab, 0x7f, 0xa9, 0x48,
0x4e, 0x34, 0x2, 0x75, 0x20, 0x15, 0xad, 0xec,
0x70, 0xf, 0xfe, 0x28, 0x9e, 0x80, 0x7f, 0xf0,
0x0,
/* U+F026 "" */
0x0, 0xe2, 0x70, 0xc, 0x59, 0x0, 0x11, 0x60,
0x86, 0xff, 0xb4, 0x40, 0x4, 0x1, 0xff, 0xcf,
@@ -7750,6 +7772,20 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xa4, 0x3, 0xff, 0x80, 0x60, 0x4a, 0x1, 0xff,
0xc0, 0x94, 0xe0, 0xf, 0x0,
/* U+F14E "" */
0x0, 0xff, 0xe2, 0x95, 0x77, 0xfb, 0x1c, 0x3,
0xcf, 0xaa, 0x20, 0x3, 0x8d, 0x20, 0x9, 0xe0,
0x3, 0xe2, 0xc1, 0x2, 0x80, 0xf, 0x1d, 0x10,
0xd0, 0x58, 0x7, 0x26, 0x61, 0x4c, 0x10, 0x90,
0x3, 0x5d, 0x8c, 0x1c, 0x40, 0x8, 0x40, 0x12,
0x23, 0xa0, 0x24, 0x2, 0xe1, 0x0, 0xa0, 0x26,
0xd4, 0x40, 0x2f, 0x20, 0x1, 0x98, 0x1d, 0x6c,
0x3, 0x15, 0x0, 0x28, 0x5f, 0xa4, 0x40, 0x21,
0x56, 0x20, 0x8e, 0x81, 0x0, 0xe8, 0x0, 0x78,
0x88, 0x3, 0xe6, 0x60, 0x0, 0xbd, 0x40, 0x38,
0x66, 0x40, 0x18, 0x6b, 0x65, 0xe2, 0xf9, 0x80,
0x20,
/* U+F15B "" */
0x35, 0x5e, 0x2, 0x0, 0xb2, 0xab, 0x8b, 0x50,
0x3, 0xfd, 0x68, 0x1, 0xfe, 0xb3, 0x0, 0xf0,
@@ -7858,6 +7894,17 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x10, 0xc, 0x60, 0x18, 0xc0, 0x18, 0x9, 0x1,
0x24, 0x6, 0x3, 0x8, 0x67, 0xf3, 0x28, 0x0,
/* U+F2F2 "" */
0x0, 0xff, 0xe2, 0x7f, 0xe0, 0xf, 0xca, 0x0,
0x50, 0xf, 0xd2, 0x0, 0x90, 0xf, 0x1e, 0xe0,
0x3, 0x35, 0x75, 0x1, 0x30, 0x80, 0x88, 0x5,
0x44, 0x63, 0x40, 0x16, 0x60, 0x3, 0x3b, 0x8,
0x7, 0xf3, 0x68, 0x7, 0xfb, 0x4, 0x3, 0xfc,
0x24, 0x1, 0x99, 0x80, 0x18, 0xb4, 0x3, 0x44,
0x0, 0x36, 0xa2, 0x80, 0x7e, 0x54, 0xa, 0x40,
0xf, 0x25, 0x0, 0x57, 0x48, 0x22, 0x4a, 0xb0,
0x0,
/* U+F304 "" */
0x0, 0xff, 0x84, 0x80, 0x3f, 0xf8, 0x9, 0xda,
0xa0, 0x1f, 0xe2, 0xb0, 0x5, 0x28, 0x7, 0xe4,
@@ -7870,6 +7917,35 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x32, 0x58, 0x7, 0xe2, 0x0, 0x92, 0xc0, 0x3f,
0x99, 0x5e, 0x6c, 0x3, 0xf8,
/* U+F3C5 "" */
0x0, 0xc4, 0x40, 0xf, 0x9f, 0xb7, 0x5f, 0x22,
0x1, 0x4c, 0x8, 0x4, 0xd8, 0x20, 0xac, 0x1,
0x8, 0x0, 0xac, 0x24, 0x0, 0x9f, 0xd0, 0x0,
0x51, 0x30, 0x4, 0x80, 0x1c, 0x80, 0x4, 0x1,
0xfe, 0x73, 0x0, 0x48, 0x1, 0xc8, 0x0, 0xb2,
0x0, 0x4f, 0xe8, 0x0, 0x28, 0xaa, 0x0, 0x42,
0x0, 0x1b, 0x0, 0x48, 0x7, 0xa0, 0x40, 0x5,
0x20, 0x19, 0x18, 0x3, 0x32, 0x0, 0x6, 0x80,
0x3d, 0x42, 0x16, 0x20, 0x1e, 0x1a, 0x85, 0x0,
0xc0,
/* U+F519 "" */
0x2, 0x0, 0xff, 0xe0, 0x88, 0x81, 0xb5, 0x80,
0x3f, 0xf8, 0x19, 0xc1, 0xa0, 0xe2, 0xc2, 0x0,
0x42, 0x0, 0x2a, 0x2, 0x82, 0x29, 0xb, 0xcb,
0x85, 0xdb, 0x4c, 0x2e, 0x43, 0x0, 0xc4, 0x40,
0x20, 0x40, 0x80, 0x4, 0x2, 0x20, 0x38, 0x78,
0x90, 0x10, 0x10, 0x10, 0x0, 0x80, 0x88, 0xc,
0x1e, 0x82, 0x4b, 0xcc, 0x64, 0x40, 0xe0, 0xfd,
0xc, 0x4, 0x92, 0x70, 0x10, 0x90, 0x93, 0x42,
0x10, 0x1, 0x21, 0x9b, 0x54, 0x0, 0x2a, 0x69,
0x1, 0x60, 0x15, 0xd8, 0x3, 0xd2, 0x3, 0x7e,
0xc, 0x1, 0xff, 0x30, 0x12, 0x20, 0x0, 0xc0,
0x1f, 0xca, 0x2f, 0xff, 0x70, 0x58, 0x7, 0xf4,
0x84, 0x80, 0x42, 0xc4, 0x80, 0x1f, 0x11, 0xa8,
0x80, 0x6b, 0xe, 0x0, 0xf8, 0xa6, 0x0, 0x38,
0xa6, 0x0, 0x30,
/* U+F55A "" */
0x0, 0xc2, 0xf1, 0x1f, 0xce, 0x80, 0x18, 0xbe,
0x1d, 0xff, 0xa2, 0xd0, 0x0, 0x78, 0x20, 0x11,
@@ -8934,60 +9010,66 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 38484, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38571, .adv_w = 252, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38651, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38716, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38779, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38874, .adv_w = 112, .box_w = 7, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38902, .adv_w = 168, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38948, .adv_w = 252, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39038, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 39094, .adv_w = 154, .box_w = 10, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39154, .adv_w = 196, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 39201, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39259, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39296, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39321, .adv_w = 196, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 39370, .adv_w = 196, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 39429, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39477, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39526, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39573, .adv_w = 196, .box_w = 13, .box_h = 4, .ofs_x = 0, .ofs_y = 3},
{.bitmap_index = 39587, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 39670, .adv_w = 280, .box_w = 18, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39787, .adv_w = 252, .box_w = 17, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 39875, .adv_w = 224, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39957, .adv_w = 196, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 40000, .adv_w = 196, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 40045, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40116, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40145, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40217, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40296, .adv_w = 217, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40389, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40487, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40568, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40616, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40672, .adv_w = 196, .box_w = 13, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40712, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40765, .adv_w = 140, .box_w = 10, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40821, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40884, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40950, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41023, .adv_w = 224, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41100, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41132, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41229, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41281, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41336, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41391, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41446, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41495, .adv_w = 252, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41540, .adv_w = 280, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41620, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41704, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41760, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41837, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41915, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41951, .adv_w = 225, .box_w = 15, .box_h = 10, .ofs_x = 0, .ofs_y = 0}
{.bitmap_index = 38730, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38795, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38858, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38953, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39010, .adv_w = 112, .box_w = 7, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39038, .adv_w = 168, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39084, .adv_w = 252, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39174, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 39230, .adv_w = 154, .box_w = 10, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39290, .adv_w = 196, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 39337, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39395, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39432, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39457, .adv_w = 196, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 39506, .adv_w = 196, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 39565, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39613, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39662, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39709, .adv_w = 196, .box_w = 13, .box_h = 4, .ofs_x = 0, .ofs_y = 3},
{.bitmap_index = 39723, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 39806, .adv_w = 280, .box_w = 18, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39923, .adv_w = 252, .box_w = 17, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40011, .adv_w = 224, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40093, .adv_w = 196, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 40136, .adv_w = 196, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 40181, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40252, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40281, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40353, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40432, .adv_w = 217, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40525, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40623, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40704, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40752, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40808, .adv_w = 196, .box_w = 13, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40848, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40901, .adv_w = 140, .box_w = 10, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40957, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41020, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41086, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41159, .adv_w = 224, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41236, .adv_w = 217, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41325, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41357, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41454, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41506, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41561, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41616, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41671, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41720, .adv_w = 252, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41765, .adv_w = 280, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41845, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41929, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41985, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 42050, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 42127, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 42200, .adv_w = 280, .box_w = 18, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 42315, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 42393, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 42429, .adv_w = 225, .box_w = 15, .box_h = 10, .ofs_x = 0, .ofs_y = 0}
};
/*---------------------
@@ -9012,14 +9094,14 @@ static const uint16_t unicode_list_10[] = {
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 0xceef, 0xcef2, 0xcef5, 0xcef6, 0xcef7,
0xcefb, 0xcefd, 0xceff, 0xcf03, 0xcf06, 0xcf0b, 0xcf10, 0xcf11,
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
0xcefb, 0xcefd, 0xceff, 0xcf01, 0xcf03, 0xcf06, 0xcf0b, 0xcf0e,
0xcf10, 0xcf11, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36,
0xcf37, 0xcf3b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58,
0xcf5a, 0xcf5b, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d,
0xcf7f, 0xcf96, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca,
0xcfd1, 0xcfd4, 0xcfdd, 0xd006, 0xd00e, 0xd038, 0xd045, 0xd0d5,
0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d,
0xd1d7, 0xd1dc, 0xd1ee, 0xd2af, 0xd403, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -9075,7 +9157,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
},
{
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 72, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -9222,7 +9304,8 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -9363,7 +9446,8 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Kern values between classes*/
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 16 px
* Bpp: 4
* Opts: --bpp 4 --size 16 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_16 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_16.c
* Opts: --bpp 4 --size 16 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 62745,61463,62194,61612,61445,62073,61632,61774,61476,62405,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_16 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_16.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -8411,6 +8411,21 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x84, 0x3, 0xff, 0xa6, 0x20, 0x17, 0x0, 0x38,
0x2, 0x10, 0x8,
/* U+F017 "" */
0x0, 0xc3, 0x19, 0xdc, 0xc8, 0x10, 0xf, 0x9f,
0x9c, 0xc4, 0x46, 0xfc, 0xe0, 0x1d, 0x30, 0x1,
0xfa, 0x24, 0x2, 0x66, 0x0, 0x6f, 0xf0, 0x6,
0x66, 0x0, 0x20, 0x3, 0x8, 0x0, 0x40, 0x34,
0x2, 0x88, 0x7, 0xff, 0x0, 0x57, 0x0, 0x3f,
0xf8, 0x58, 0x60, 0x1f, 0x8c, 0x3, 0x8c, 0x3,
0xc2, 0x0, 0xc4, 0x0, 0xe2, 0x0, 0xf5, 0x88,
0xac, 0x40, 0x22, 0xb0, 0xf, 0x27, 0x9b, 0x8,
0x5, 0x66, 0x60, 0xf, 0xe, 0x48, 0x4, 0x66,
0xe, 0x0, 0xff, 0xe0, 0x70, 0x0, 0xb0, 0x40,
0x3f, 0xe, 0x10, 0x4, 0x78, 0x80, 0x1e, 0x4c,
0x30, 0xe, 0x2b, 0xe9, 0x77, 0x4f, 0x59, 0x0,
0x40,
/* U+F019 "" */
0x0, 0xf6, 0xff, 0xb4, 0x3, 0xff, 0x80, 0x40,
0x2, 0x0, 0xff, 0xfe, 0x27, 0xfc, 0x1, 0xbf,
@@ -8447,6 +8462,16 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x40, 0xa, 0xec, 0x40, 0x91, 0x2a, 0x3, 0x4e,
0x1, 0xc9, 0xb4, 0xa6, 0x49, 0x3c, 0xa0, 0x10,
/* U+F024 "" */
0x0, 0xff, 0xe2, 0x1f, 0x70, 0xc0, 0x3f, 0xf8,
0x10, 0x22, 0x80, 0xf, 0xfe, 0x1, 0x80, 0x17,
0x7f, 0xad, 0xc8, 0x0, 0x4f, 0xb9, 0x0, 0x1,
0x20, 0x14, 0x8d, 0xff, 0x6c, 0x11, 0x80, 0x7f,
0xff, 0xc0, 0x3f, 0xfb, 0xa4, 0x8c, 0x80, 0x1f,
0x48, 0x5, 0xba, 0xb9, 0xbf, 0xb7, 0x56, 0x9e,
0x60, 0x8, 0x80, 0x39, 0x22, 0xa5, 0x84, 0x3,
0xff, 0x8c, 0x44, 0x0, 0xff, 0xe0, 0x80,
/* U+F026 "" */
0x0, 0xff, 0xe1, 0x46, 0x80, 0x74, 0x39, 0x0,
0x68, 0x70, 0x6, 0xff, 0xce, 0x1, 0x10, 0x7,
@@ -8851,6 +8876,21 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x38, 0x39, 0x0, 0x7f, 0xf0, 0xa3, 0xe0, 0x3,
0xe0,
/* U+F14E "" */
0x0, 0xc2, 0xd5, 0x75, 0x6, 0x1, 0xf9, 0xfa,
0x55, 0x15, 0xf3, 0x4, 0x1, 0xd5, 0x0, 0x1f,
0x1e, 0x98, 0x5, 0xa, 0x1, 0xfe, 0xd2, 0x2,
0x70, 0xf, 0x93, 0x24, 0x6, 0x2, 0x80, 0x3c,
0xdb, 0x67, 0xe0, 0x5, 0x15, 0x0, 0xc3, 0xb2,
0x40, 0x2c, 0x1, 0x21, 0x0, 0x69, 0x3e, 0x80,
0x90, 0xc, 0x40, 0x1c, 0xc0, 0x19, 0x80, 0x38,
0x80, 0x27, 0x11, 0x74, 0x40, 0x40, 0x31, 0x28,
0x5, 0x20, 0x6, 0xd7, 0x0, 0xe4, 0xa0, 0x1,
0x3, 0xf4, 0x90, 0x7, 0x28, 0x93, 0x81, 0x74,
0x8, 0x7, 0x86, 0x0, 0x10, 0xa0, 0x1f, 0xed,
0x20, 0xa, 0xa0, 0x3, 0xe3, 0xd3, 0x0, 0xe7,
0xe9, 0x54, 0x57, 0xcc, 0x10, 0x4,
/* U+F15B "" */
0xdf, 0xff, 0x6, 0x88, 0x0, 0x80, 0x3e, 0x2c,
0x10, 0xf, 0xf1, 0x60, 0x80, 0x7f, 0x8b, 0x0,
@@ -8962,6 +9002,19 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x0, 0x88, 0x4, 0x40, 0x1e, 0x66, 0x3, 0x30,
0x19, 0x80, 0x19, 0xc0, 0x3f, 0xce, 0x0,
/* U+F2F2 "" */
0x0, 0xc7, 0xff, 0x8c, 0x3, 0xff, 0x94, 0x7e,
0x0, 0xf3, 0x0, 0xf8, 0x5f, 0x0, 0x18, 0xe0,
0x28, 0x1, 0x1f, 0xc1, 0x80, 0xe, 0x3b, 0x2d,
0x0, 0xb0, 0x40, 0xe, 0xe0, 0x0, 0x91, 0x14,
0x24, 0x3, 0x44, 0x0, 0x33, 0x9, 0x20, 0x7,
0xf9, 0x9, 0x0, 0x3f, 0xf8, 0x8, 0x20, 0x1f,
0xfc, 0x1, 0x10, 0x7, 0xff, 0x0, 0x50, 0x3,
0xbf, 0xc0, 0x1c, 0x84, 0x60, 0x1f, 0xe4, 0x20,
0xe1, 0x0, 0xfc, 0x32, 0x0, 0x2d, 0x10, 0xf,
0xe, 0x90, 0x4, 0x7d, 0x4, 0x0, 0x38, 0xe3,
0x0, 0x0,
/* U+F304 "" */
0x0, 0xff, 0xe0, 0x3f, 0xd0, 0x7, 0xff, 0x5,
0xe0, 0x16, 0xc0, 0x3f, 0xf8, 0x14, 0x1, 0x25,
@@ -8976,6 +9029,38 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x1a, 0x1c, 0x3, 0xfc, 0x62, 0x49, 0x2e, 0x1,
0xff, 0x6f, 0x6d, 0xb0, 0x7, 0xfc,
/* U+F3C5 "" */
0x0, 0x9f, 0x3f, 0xd8, 0xe0, 0x18, 0x76, 0xc,
0x0, 0x71, 0xa2, 0x0, 0xc2, 0x0, 0xf1, 0x60,
0x39, 0x0, 0x4c, 0xc0, 0x8, 0x9e, 0xc0, 0x2d,
0x99, 0x68, 0x5, 0x66, 0x0, 0x62, 0x0, 0x13,
0x0, 0xc, 0x3, 0xff, 0x80, 0x60, 0x6, 0x20,
0x1, 0x30, 0x0, 0xe8, 0x2, 0xd9, 0x96, 0x80,
0x54, 0xc4, 0x1, 0x33, 0x0, 0x22, 0x60, 0xe0,
0xf, 0xdc, 0x0, 0x37, 0x0, 0xf3, 0x98, 0x5,
0x6, 0x1, 0x8e, 0x0, 0x3b, 0x80, 0x37, 0x0,
0x78, 0xa0, 0x1, 0x4, 0x1, 0xf3, 0xa2, 0x1c,
0x3, 0x0,
/* U+F519 "" */
0x3f, 0xc0, 0xf, 0xfe, 0x16, 0x79, 0xd0, 0x58,
0x7, 0xff, 0x9, 0x2, 0x90, 0x14, 0x2f, 0xc8,
0x13, 0xb8, 0x80, 0x5f, 0x61, 0x40, 0x84, 0x4,
0xa, 0x6, 0x14, 0x22, 0xa0, 0x30, 0x50, 0x20,
0x20, 0xf, 0xfe, 0x28, 0x80, 0x8, 0xc, 0x14,
0xc, 0xc, 0x0, 0x60, 0x60, 0xa0, 0x40, 0x48,
0x8, 0x17, 0xe4, 0x64, 0x22, 0x23, 0x2f, 0xb0,
0xb0, 0x4a, 0xb, 0x0, 0xd2, 0xc, 0xc0, 0x90,
0xc, 0x81, 0x47, 0xf8, 0x1, 0xa, 0x85, 0x50,
0x14, 0x40, 0x2c, 0xf3, 0x0, 0xf3, 0x2, 0x11,
0x10, 0x18, 0x3, 0xff, 0x81, 0x40, 0x9f, 0xe4,
0xa, 0x0, 0xff, 0x90, 0x80, 0x3c, 0x48, 0x1,
0xfe, 0xf0, 0x8f, 0xfd, 0x1, 0xe0, 0x1f, 0xc4,
0x80, 0xc0, 0x19, 0x81, 0x8, 0x3, 0xf5, 0x3,
0x8, 0x6, 0x16, 0xa, 0x0, 0xfd, 0xc3, 0x40,
0x1e, 0xa1, 0xe0, 0xf, 0xcd, 0xc6, 0x1, 0xe3,
0xe6, 0x0, 0xc0,
/* U+F55A "" */
0x0, 0xc3, 0x7f, 0xff, 0xfc, 0xe, 0x40, 0x8,
0x71, 0x0, 0x3f, 0xf8, 0x3, 0x40, 0x1, 0xc2,
@@ -10043,60 +10128,66 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 46964, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47066, .adv_w = 288, .box_w = 18, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47157, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47225, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 47288, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47392, .adv_w = 128, .box_w = 8, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47423, .adv_w = 192, .box_w = 12, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47474, .adv_w = 288, .box_w = 18, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47581, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 47648, .adv_w = 176, .box_w = 11, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47715, .adv_w = 224, .box_w = 10, .box_h = 16, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 47767, .adv_w = 224, .box_w = 14, .box_h = 18, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 47839, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47868, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47888, .adv_w = 224, .box_w = 10, .box_h = 16, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 47940, .adv_w = 224, .box_w = 16, .box_h = 14, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 48011, .adv_w = 160, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48068, .adv_w = 160, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48125, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48170, .adv_w = 224, .box_w = 14, .box_h = 4, .ofs_x = 0, .ofs_y = 4},
{.bitmap_index = 48187, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 48286, .adv_w = 320, .box_w = 20, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48420, .adv_w = 288, .box_w = 20, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 48532, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48632, .adv_w = 224, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 48687, .adv_w = 224, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 48744, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48832, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 48861, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48929, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49023, .adv_w = 248, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49131, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49241, .adv_w = 224, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49332, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49379, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49431, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49486, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49545, .adv_w = 160, .box_w = 12, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49613, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49674, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49744, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49810, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 49899, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49932, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50045, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50090, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50134, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50178, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50224, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50264, .adv_w = 288, .box_w = 18, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50323, .adv_w = 320, .box_w = 21, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50424, .adv_w = 224, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 50520, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50575, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 50669, .adv_w = 320, .box_w = 20, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 50758, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50797, .adv_w = 258, .box_w = 17, .box_h = 11, .ofs_x = 0, .ofs_y = 1}
{.bitmap_index = 47254, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47322, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 47385, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47489, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47552, .adv_w = 128, .box_w = 8, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47583, .adv_w = 192, .box_w = 12, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47634, .adv_w = 288, .box_w = 18, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47741, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 47808, .adv_w = 176, .box_w = 11, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47875, .adv_w = 224, .box_w = 10, .box_h = 16, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 47927, .adv_w = 224, .box_w = 14, .box_h = 18, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 47999, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48028, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48048, .adv_w = 224, .box_w = 10, .box_h = 16, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 48100, .adv_w = 224, .box_w = 16, .box_h = 14, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 48171, .adv_w = 160, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48228, .adv_w = 160, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48285, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48330, .adv_w = 224, .box_w = 14, .box_h = 4, .ofs_x = 0, .ofs_y = 4},
{.bitmap_index = 48347, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 48446, .adv_w = 320, .box_w = 20, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48580, .adv_w = 288, .box_w = 20, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 48692, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48792, .adv_w = 224, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 48847, .adv_w = 224, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 48904, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48992, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49021, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49089, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49183, .adv_w = 248, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49291, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49401, .adv_w = 224, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49492, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49539, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49591, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49646, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49705, .adv_w = 160, .box_w = 12, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49773, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49834, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49904, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49970, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 50059, .adv_w = 248, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50161, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50194, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50307, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50352, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50396, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50440, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50486, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50526, .adv_w = 288, .box_w = 18, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50585, .adv_w = 320, .box_w = 21, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50686, .adv_w = 224, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 50782, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50837, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50919, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 51013, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 51095, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 51226, .adv_w = 320, .box_w = 20, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 51315, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 51354, .adv_w = 258, .box_w = 17, .box_h = 11, .ofs_x = 0, .ofs_y = 1}
};
/*---------------------
@@ -10121,14 +10212,14 @@ static const uint16_t unicode_list_10[] = {
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 0xceef, 0xcef2, 0xcef5, 0xcef6, 0xcef7,
0xcefb, 0xcefd, 0xceff, 0xcf03, 0xcf06, 0xcf0b, 0xcf10, 0xcf11,
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
0xcefb, 0xcefd, 0xceff, 0xcf01, 0xcf03, 0xcf06, 0xcf0b, 0xcf0e,
0xcf10, 0xcf11, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36,
0xcf37, 0xcf3b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58,
0xcf5a, 0xcf5b, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d,
0xcf7f, 0xcf96, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca,
0xcfd1, 0xcfd4, 0xcfdd, 0xd006, 0xd00e, 0xd038, 0xd045, 0xd0d5,
0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d,
0xd1d7, 0xd1dc, 0xd1ee, 0xd2af, 0xd403, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -10184,7 +10275,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
},
{
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 72, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -10331,7 +10422,8 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -10472,7 +10564,8 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Kern values between classes*/
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 20 px
* Bpp: 4
* Opts: --bpp 4 --size 20 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_20 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_20.c
* Opts: --bpp 4 --size 20 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 62745,61463,62194,61612,61445,62073,61632,61774,61476,62405,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_20 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_20.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -10547,6 +10547,25 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x1c, 0x40, 0x11, 0x0, 0x66, 0x0, 0xf8, 0xbf,
0xf9, 0x0, 0x2c, 0xff, 0xd4, 0x1, 0x80,
/* U+F017 "" */
0x0, 0xff, 0xe6, 0x8b, 0xe7, 0x7f, 0xb6, 0x50,
0x3, 0xfc, 0xfd, 0x6, 0x20, 0x2, 0x6b, 0xc2,
0x0, 0xfa, 0xe0, 0x3, 0xf8, 0xf1, 0x0, 0x3a,
0x90, 0x3, 0x12, 0x20, 0x3, 0xd, 0x90, 0x4,
0xca, 0x1, 0xd7, 0x70, 0x80, 0x77, 0x80, 0x50,
0x1, 0xff, 0xc3, 0x26, 0x6, 0x10, 0xf, 0xfe,
0x25, 0x86, 0x0, 0x7f, 0xf1, 0x48, 0x58, 0x3,
0xff, 0x8c, 0xa4, 0x1, 0xfe, 0x20, 0xf, 0x84,
0xc0, 0x3f, 0xd8, 0xc0, 0x1e, 0x16, 0x0, 0xfa,
0xcc, 0x1, 0x32, 0x0, 0xe6, 0xc0, 0xf, 0x8b,
0x18, 0x1, 0x80, 0x18, 0x49, 0x40, 0x3f, 0xa6,
0xa1, 0x40, 0x33, 0x81, 0x30, 0x7, 0xf2, 0xb8,
0x7, 0x40, 0x2, 0x8, 0x3, 0xff, 0x85, 0x42,
0x0, 0x1f, 0x10, 0xf, 0xfe, 0x3, 0xa8, 0x6,
0x2f, 0x30, 0xf, 0xf5, 0x40, 0x7, 0x87, 0x28,
0xc0, 0x38, 0x5b, 0x94, 0x3, 0xf9, 0x73, 0xb2,
0xed, 0xbd, 0x22, 0x1, 0xc0,
/* U+F019 "" */
0x0, 0xfc, 0xac, 0xc5, 0x0, 0xff, 0xe1, 0xbd,
0x4c, 0xa9, 0xc0, 0x3f, 0xf8, 0x5e, 0x1, 0xbc,
@@ -10599,6 +10618,19 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x70, 0xc, 0xa9, 0x80, 0x12, 0xdf, 0xf7, 0x3f,
0x20, 0x40, 0x38,
/* U+F024 "" */
0x1, 0x30, 0xf, 0xfe, 0x2a, 0x76, 0x68, 0x80,
0x7f, 0xf0, 0xe0, 0x0, 0x4c, 0x1, 0xff, 0xc3,
0x70, 0xb, 0x66, 0xed, 0x2a, 0x20, 0x1e, 0x6b,
0x49, 0x0, 0x95, 0x91, 0xd, 0x5d, 0x90, 0xf1,
0x7f, 0x29, 0x4, 0x20, 0x1f, 0xc6, 0xf0, 0xe8,
0x1, 0x8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe,
0x1, 0xff, 0xd1, 0x40, 0xc, 0x4f, 0x57, 0x92,
0x80, 0x1e, 0x18, 0x90, 0x9, 0xb6, 0x15, 0xd,
0xaf, 0xee, 0x1e, 0x2f, 0x9c, 0x3, 0xff, 0x82,
0x8f, 0xe, 0x80, 0x1f, 0xfe, 0x9, 0x4a, 0x0,
0xff, 0xe2, 0x0,
/* U+F026 "" */
0x0, 0xfe, 0x10, 0xf, 0x8b, 0xbc, 0x3, 0xc5,
0x82, 0x20, 0xe, 0x2c, 0x10, 0x2, 0x3b, 0xec,
@@ -11154,6 +11186,27 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x20, 0x8, 0x0, 0xff, 0xe3, 0x64, 0x62, 0x0,
0x7e,
/* U+F14E "" */
0x0, 0xff, 0xe6, 0x94, 0x6f, 0xfd, 0xd4, 0x80,
0x1f, 0xe9, 0xd7, 0x20, 0x8, 0x56, 0xf0, 0x80,
0x3c, 0x38, 0xc0, 0x1f, 0xc7, 0xa8, 0x1, 0xda,
0x60, 0x1f, 0xfc, 0xb, 0x30, 0xa, 0xc, 0x3,
0xfc, 0xa6, 0x0, 0xd0, 0x0, 0xb8, 0x7, 0xf3,
0xf5, 0x50, 0x0, 0x2e, 0xc, 0x1, 0xf0, 0xc7,
0xc0, 0x85, 0x0, 0x54, 0x16, 0x1, 0xe5, 0xe7,
0x0, 0x90, 0xc0, 0x22, 0x28, 0x3, 0xd6, 0x32,
0x80, 0xf, 0x0, 0xe6, 0x10, 0xe, 0x71, 0x96,
0xa0, 0x24, 0x0, 0xe1, 0x10, 0x7, 0x48, 0x51,
0xc8, 0x40, 0x7, 0x84, 0x80, 0x32, 0x88, 0x16,
0x3a, 0x28, 0x7, 0x99, 0x0, 0x34, 0x0, 0x49,
0x76, 0x0, 0xf0, 0x95, 0x80, 0x46, 0x40, 0xbb,
0x68, 0x1, 0xf3, 0x81, 0xa8, 0x0, 0xe7, 0xa8,
0x80, 0x3f, 0xa0, 0x1, 0x62, 0x0, 0x61, 0x0,
0xff, 0x50, 0x80, 0x7, 0x4, 0x3, 0xff, 0x81,
0xa, 0x1, 0x8b, 0xcc, 0x3, 0xf8, 0x69, 0xc0,
0x3c, 0x39, 0x46, 0x1, 0xc4, 0xfe, 0xa0, 0x1f,
0xcb, 0x9f, 0x97, 0x9d, 0xb0, 0x20, 0x1c,
/* U+F15B "" */
0x24, 0x4f, 0x8c, 0xc, 0x3, 0x6d, 0xdf, 0xde,
0x18, 0xc0, 0x1f, 0xfc, 0x29, 0x60, 0xf, 0xfe,
@@ -11305,6 +11358,25 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x0, 0x70, 0x6, 0x0, 0x7f, 0xf0, 0x44, 0xc0,
0xf, 0x50, 0xef, 0xfe, 0x8c, 0x0, 0x0,
/* U+F2F2 "" */
0x0, 0xf2, 0xc4, 0x65, 0x0, 0xff, 0xe0, 0x43,
0xbe, 0x90, 0xf, 0xfe, 0x0, 0x80, 0x67, 0x0,
0xff, 0xe0, 0x65, 0x80, 0x32, 0xc0, 0x3f, 0xf8,
0xb, 0xe0, 0x9, 0x50, 0xf, 0xf3, 0x75, 0x20,
0x1, 0x6b, 0x95, 0x5e, 0xa0, 0x1a, 0x64, 0x20,
0x1e, 0x1a, 0xa0, 0x58, 0x5, 0xc, 0x1, 0xc,
0xc8, 0x40, 0x3b, 0x40, 0x6, 0xe0, 0x19, 0x59,
0x84, 0x1, 0xc4, 0x0, 0x90, 0xf, 0xfe, 0x14,
0x80, 0x10, 0x3, 0xff, 0x84, 0xa0, 0x22, 0x0,
0xff, 0xe1, 0x10, 0x18, 0x7, 0xff, 0x14, 0x80,
0x3c, 0xe0, 0x1, 0x0, 0xfc, 0x40, 0x1c, 0x7f,
0xe2, 0x0, 0xe4, 0x0, 0x50, 0x7, 0xff, 0xb,
0x40, 0xe, 0x20, 0x1f, 0xfc, 0x2, 0x60, 0xb,
0x40, 0x3f, 0xe1, 0xf0, 0xc, 0x78, 0x20, 0x1f,
0x8b, 0x8, 0x3, 0x8f, 0xdc, 0x40, 0x30, 0xc6,
0x90, 0x7, 0xc3, 0x1d, 0xb7, 0x6d, 0xe7, 0x0,
0xe0,
/* U+F304 "" */
0x0, 0xff, 0xe1, 0x8b, 0x20, 0x7, 0xff, 0x14,
0x72, 0x6e, 0x0, 0x3f, 0xf8, 0x63, 0xe4, 0x0,
@@ -11325,6 +11397,51 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x38, 0x40, 0x1f, 0xfc, 0x1a, 0x8a, 0xce, 0xe1,
0x0, 0x7f, 0xf0, 0x40,
/* U+F3C5 "" */
0x0, 0xe3, 0x67, 0x63, 0x0, 0xfc, 0xbb, 0x93,
0x13, 0x9a, 0xa0, 0x1d, 0x34, 0x40, 0x1c, 0x55,
0x20, 0x13, 0xb0, 0x7, 0xf3, 0x38, 0x14, 0x0,
0x70, 0x80, 0x74, 0x15, 0x80, 0x65, 0xee, 0x94,
0x3, 0x5a, 0x0, 0x47, 0x42, 0x3, 0x46, 0x1,
0x21, 0x0, 0x56, 0x1, 0xd6, 0x1, 0x10, 0x7,
0xff, 0xc, 0x80, 0x2b, 0x0, 0xeb, 0x0, 0x89,
0x40, 0x23, 0xa1, 0x1, 0xa3, 0x0, 0x96, 0x40,
0x32, 0xf7, 0x4a, 0x1, 0xa4, 0x5c, 0x3, 0x84,
0x3, 0x9c, 0x42, 0x8, 0x3, 0xf8, 0xa0, 0x2,
0xe0, 0xf, 0xee, 0x0, 0xc7, 0x0, 0x1f, 0x41,
0x80, 0x73, 0x98, 0x7, 0x1b, 0x80, 0x7d, 0xc0,
0x1d, 0xc0, 0x1f, 0x8a, 0x40, 0x29, 0x20, 0xf,
0xe6, 0x40, 0x46, 0x0, 0xff, 0xaa, 0x28, 0x3,
0xc0,
/* U+F519 "" */
0x3, 0x41, 0x0, 0xff, 0xe2, 0x8a, 0x18, 0x16,
0x5c, 0x80, 0x7f, 0xf1, 0x66, 0xf0, 0xa8, 0x1,
0x40, 0x1f, 0xfc, 0x5a, 0x0, 0x52, 0x1, 0x90,
0x3e, 0x40, 0x5, 0x17, 0x0, 0x14, 0x63, 0x81,
0x18, 0x21, 0x2, 0x80, 0x24, 0xc8, 0x1, 0x4e,
0x8f, 0x40, 0x2, 0x3a, 0x0, 0x28, 0x10, 0x80,
0x80, 0x4, 0x8, 0x0, 0xa0, 0x12, 0x80, 0x8,
0x8, 0x0, 0x20, 0x20, 0x1, 0x0, 0x8, 0x8,
0x0, 0x80, 0x22, 0x0, 0x8, 0x8, 0x0, 0x40,
0x4, 0x4, 0x0, 0x50, 0x50, 0x3, 0x0, 0x42,
0x0, 0x50, 0x50, 0x1, 0x1, 0x20, 0x20, 0x2,
0xb3, 0x40, 0x90, 0x2, 0x32, 0xd, 0xca, 0x0,
0x20, 0x3e, 0x80, 0x18, 0x4, 0xc4, 0x2c, 0x1,
0x60, 0xb, 0x1, 0x31, 0x6, 0x0, 0x7a, 0x2a,
0x18, 0x7, 0x30, 0x22, 0x40, 0xc0, 0x1c, 0x68,
0xaa, 0xa, 0xb5, 0x0, 0xcc, 0x0, 0xf0, 0xf0,
0x3, 0x80, 0x65, 0xba, 0x0, 0xfe, 0xb0, 0x24,
0x4, 0x20, 0xa0, 0xf, 0xfe, 0x1a, 0x10, 0x17,
0xfc, 0x40, 0x48, 0x1, 0xff, 0xc2, 0xe0, 0xf,
0xef, 0x0, 0xff, 0xe0, 0x8a, 0x82, 0x3b, 0xf2,
0x2, 0x10, 0x7, 0xff, 0x2, 0x40, 0x13, 0x11,
0xd2, 0x0, 0xa0, 0xf, 0xfe, 0x3, 0x2, 0x10,
0x7, 0x12, 0x3, 0x0, 0x7f, 0xca, 0x21, 0xc0,
0x1f, 0x70, 0xa, 0x80, 0x7f, 0xb0, 0x5, 0x40,
0x3e, 0x51, 0xc, 0x0, 0xff, 0x4d, 0xe8, 0x7,
0xf6, 0xd4, 0x80, 0x70,
/* U+F55A "" */
0x0, 0xf3, 0x77, 0xff, 0xff, 0xe, 0xc8, 0x3,
0xa2, 0x44, 0x3, 0xff, 0x86, 0x92, 0x1, 0xa1,
@@ -12399,60 +12516,66 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 63746, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 63899, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 64042, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64146, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64243, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64398, .adv_w = 160, .box_w = 10, .box_h = 16, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64436, .adv_w = 240, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 64510, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 64664, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64759, .adv_w = 220, .box_w = 14, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64862, .adv_w = 280, .box_w = 13, .box_h = 19, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 64930, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 65032, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65089, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65130, .adv_w = 280, .box_w = 13, .box_h = 19, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 65204, .adv_w = 280, .box_w = 19, .box_h = 19, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 65303, .adv_w = 200, .box_w = 11, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 65388, .adv_w = 200, .box_w = 11, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 65469, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65541, .adv_w = 280, .box_w = 18, .box_h = 5, .ofs_x = 0, .ofs_y = 5},
{.bitmap_index = 65563, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 65713, .adv_w = 400, .box_w = 26, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 65916, .adv_w = 360, .box_w = 24, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66069, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 66211, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 2},
{.bitmap_index = 66287, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 2},
{.bitmap_index = 66365, .adv_w = 400, .box_w = 26, .box_h = 16, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 66483, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 66527, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66637, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66775, .adv_w = 310, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66934, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67103, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67251, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67324, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67417, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 67477, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67567, .adv_w = 200, .box_w = 14, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67660, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67748, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67859, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67985, .adv_w = 320, .box_w = 22, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 68106, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68154, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68318, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68385, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68453, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68520, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68587, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68647, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68734, .adv_w = 400, .box_w = 26, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 68868, .adv_w = 280, .box_w = 16, .box_h = 21, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 69015, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69102, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 69242, .adv_w = 400, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 69362, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69421, .adv_w = 322, .box_w = 21, .box_h = 13, .ofs_x = 0, .ofs_y = 1}
{.bitmap_index = 64175, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64279, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64376, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64531, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64614, .adv_w = 160, .box_w = 10, .box_h = 16, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64652, .adv_w = 240, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 64726, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 64880, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64975, .adv_w = 220, .box_w = 14, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 65078, .adv_w = 280, .box_w = 13, .box_h = 19, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 65146, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 65248, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65305, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65346, .adv_w = 280, .box_w = 13, .box_h = 19, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 65420, .adv_w = 280, .box_w = 19, .box_h = 19, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 65519, .adv_w = 200, .box_w = 11, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 65604, .adv_w = 200, .box_w = 11, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 65685, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65757, .adv_w = 280, .box_w = 18, .box_h = 5, .ofs_x = 0, .ofs_y = 5},
{.bitmap_index = 65779, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 65929, .adv_w = 400, .box_w = 26, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66132, .adv_w = 360, .box_w = 24, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66285, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 66427, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 2},
{.bitmap_index = 66503, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 2},
{.bitmap_index = 66581, .adv_w = 400, .box_w = 26, .box_h = 16, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 66699, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 66743, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66853, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66991, .adv_w = 310, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67150, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67319, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67467, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67540, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67633, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 67693, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67783, .adv_w = 200, .box_w = 14, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67876, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67964, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68075, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 68201, .adv_w = 320, .box_w = 22, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 68322, .adv_w = 310, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68473, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68521, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68685, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68752, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68820, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68887, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68954, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 69014, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 69101, .adv_w = 400, .box_w = 26, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 69235, .adv_w = 280, .box_w = 16, .box_h = 21, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 69382, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69469, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69598, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 69738, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69851, .adv_w = 400, .box_w = 25, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 70055, .adv_w = 400, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 70175, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 70234, .adv_w = 322, .box_w = 21, .box_h = 13, .ofs_x = 0, .ofs_y = 1}
};
/*---------------------
@@ -12477,14 +12600,14 @@ static const uint16_t unicode_list_10[] = {
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 0xceef, 0xcef2, 0xcef5, 0xcef6, 0xcef7,
0xcefb, 0xcefd, 0xceff, 0xcf03, 0xcf06, 0xcf0b, 0xcf10, 0xcf11,
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
0xcefb, 0xcefd, 0xceff, 0xcf01, 0xcf03, 0xcf06, 0xcf0b, 0xcf0e,
0xcf10, 0xcf11, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36,
0xcf37, 0xcf3b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58,
0xcf5a, 0xcf5b, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d,
0xcf7f, 0xcf96, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca,
0xcfd1, 0xcfd4, 0xcfdd, 0xd006, 0xd00e, 0xd038, 0xd045, 0xd0d5,
0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d,
0xd1d7, 0xd1dc, 0xd1ee, 0xd2af, 0xd403, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -12540,7 +12663,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
},
{
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 72, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -12687,7 +12810,8 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -12828,7 +12952,8 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0
};
/*Kern values between classes*/
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 40 px
* Bpp: 4
* Opts: --bpp 4 --size 40 --no-compress --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-SemiBold.ttf -r 0x20,0x2D,0x30-0x3A --format lvgl --lv-include lvgl.h --lv-font-name ui_font_40 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_40.c
* Opts: --bpp 4 --size 40 --no-compress --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-SemiBold.ttf -r 0x20,0x2D-0x2E,0x30-0x3A --format lvgl --lv-include lvgl.h --lv-font-name ui_font_40 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_40.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -30,6 +30,12 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x8b, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xbf, 0xff,
0xff, 0xff, 0xff, 0x80,
/* U+002E "." */
0x0, 0x2, 0x0, 0x0, 0x8f, 0xff, 0x80, 0x5f,
0xff, 0xff, 0x58, 0xff, 0xff, 0xf8, 0x7f, 0xff,
0xff, 0x83, 0xff, 0xff, 0xf3, 0x5, 0xdf, 0xd5,
0x0,
/* U+0030 "0" */
0x0, 0x0, 0x0, 0x4a, 0xdf, 0xfe, 0xc8, 0x20,
0x0, 0x0, 0x0, 0x0, 0x2, 0xdf, 0xff, 0xff,
@@ -431,17 +437,18 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
{.bitmap_index = 0, .adv_w = 166, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 0, .adv_w = 205, .box_w = 11, .box_h = 5, .ofs_x = 1, .ofs_y = 9},
{.bitmap_index = 28, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 322, .adv_w = 366, .box_w = 14, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 518, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 812, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1106, .adv_w = 366, .box_w = 23, .box_h = 28, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 1428, .adv_w = 366, .box_w = 19, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1694, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1988, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2282, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2576, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2870, .adv_w = 177, .box_w = 7, .box_h = 22, .ofs_x = 2, .ofs_y = 0}
{.bitmap_index = 28, .adv_w = 177, .box_w = 7, .box_h = 7, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 53, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 347, .adv_w = 366, .box_w = 14, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 543, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 837, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1131, .adv_w = 366, .box_w = 23, .box_h = 28, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 1453, .adv_w = 366, .box_w = 19, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1719, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2013, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2307, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2601, .adv_w = 366, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2895, .adv_w = 177, .box_w = 7, .box_h = 22, .ofs_x = 2, .ofs_y = 0}
};
/*---------------------
@@ -449,18 +456,18 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
*--------------------*/
static const uint16_t unicode_list_0[] = {
0x0, 0xd
0x0, 0xd, 0xe
};
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 32, .range_length = 14, .glyph_id_start = 1,
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 2, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.range_start = 32, .range_length = 15, .glyph_id_start = 1,
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 3, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
},
{
.range_start = 48, .range_length = 11, .glyph_id_start = 3,
.range_start = 48, .range_length = 11, .glyph_id_start = 4,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
@@ -1,7 +1,9 @@
#pragma once
// Decoded map tiles (RGB565, 128 KB each) kept for reuse while panning / zooming
// back, least-recently-used evicted. Buffers live in PSRAM on the ESP32 and are
// allocated on first use, then kept for the session.
// allocated on first use, then kept for the session. Tiles found missing are
// remembered separately (no buffer), so looking up coarser zoom levels for the
// overzoom fallback doesn't push real tiles out.
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp only.
@@ -15,7 +17,7 @@ namespace mapview {
class TileCache {
public:
static const int SLOTS = 12; // 6 on screen at 320x218 + a ring of neighbours
static const int SLOTS = 24; // 6 on screen at 320x218 + the 14-tile read-ahead ring + a few coarser (3 MB)
struct Slot {
int16_t z = -1;
@@ -26,10 +28,13 @@ public:
lv_image_dsc_t dsc;
};
// The slot holding z/x/y if it was loaded (present or known missing), else nullptr.
// The slot holding z/x/y if it was loaded (present, or known missing: a
// shared slot with present == false), else nullptr.
Slot* find(int z, int x, int y) {
for (Slot& s : _slots)
if (s.z == z && s.x == x && s.y == y) { s.used = ++_tick; return &s; }
for (const Missing& m : _missing)
if (m.z == z && m.x == x && m.y == y) return &_miss_slot;
return nullptr;
}
@@ -49,6 +54,12 @@ public:
s->z = z; s->x = x; s->y = y;
s->used = ++_tick;
s->present = src.renderTile(z, x, y, s->px);
if (!s->present) { // remember the miss without holding a buffer slot
Missing& m = _missing[_miss_next++ % MISSING];
m.z = (int16_t)z; m.x = x; m.y = y;
s->z = -1; s->used = 0;
return &_miss_slot;
}
memset(&s->dsc, 0, sizeof(s->dsc));
s->dsc.header.magic = LV_IMAGE_HEADER_MAGIC;
s->dsc.header.cf = LV_COLOR_FORMAT_RGB565;
@@ -61,13 +72,18 @@ public:
}
// Forget the "no tile here" answers (new tiles may have been written).
void forgetMissing() { for (Slot& s : _slots) if (s.z >= 0 && !s.present) { s.z = -1; s.used = 0; } }
void forgetMissing() { for (Missing& m : _missing) m.z = -1; }
// Forget everything (e.g. the card was swapped); keeps the buffers.
void invalidate() { for (Slot& s : _slots) { s.z = -1; s.used = 0; } }
void invalidate() { for (Slot& s : _slots) { s.z = -1; s.used = 0; } forgetMissing(); }
private:
struct Missing { int16_t z = -1; int32_t x = 0, y = 0; };
static const int MISSING = 48;
Slot _slots[SLOTS];
Missing _missing[MISSING];
int _miss_next = 0;
Slot _miss_slot; // present == false
uint32_t _tick = 0;
};
@@ -4,6 +4,10 @@
// alternative is tools/maps/fetch_tiles.py). Tiles already on the card are
// skipped, so a download can be repeated to fill gaps or extend an area.
//
// The job (area + zoom range) is kept in <root>/.job until it completes, so a
// download cut short by a power-off, a lost network or Stop can be resumed:
// resuming re-runs the same job, and the tiles already written are skipped.
//
// Driven by loop() from the UI loop: one small step per call, the HTTP GET
// itself runs asynchronously in the board's fetcher (lvport::fetch*), so the
// mesh keeps being serviced during a long download.
@@ -78,6 +82,20 @@ public:
const char* message() const { return _msg; }
const char* sourceHost() { loadSource(); return _host; }
// Unfinished job from <root>/.job (read once, then tracked in memory).
bool savedJob(TileArea& a) {
if (!_job_checked) { _job_checked = true; _has_job = readJob(_job); }
if (_has_job) a = _job;
return _has_job;
}
void discardJob() {
char path[64];
jobPath(path, sizeof(path));
remove(path);
_has_job = false;
_job_checked = true;
}
bool start(const TileArea& a, const char* ssid, const char* pass) {
if (active()) return false;
_area = a;
@@ -88,6 +106,7 @@ public:
if (_total == 0 || _total > MAX_TILES) { fail("Area too large: use the PC tool"); return false; }
loadSource();
writeAttribution();
writeJob(a);
_z = a.zmin;
tileRange(a, _z, _x0, _y0, _x1, _y1);
_x = _x0; _y = _y0;
@@ -174,6 +193,8 @@ private:
char _attr[96] = "";
char _host[48] = "";
char _msg[64] = "";
TileArea _job = {0, 0, 0, 0, 0, 0};
bool _has_job = false, _job_checked = false;
void fail(const char* m) { snprintf(_msg, sizeof(_msg), "%s", m); _state = FAILED; }
@@ -181,6 +202,35 @@ private:
snprintf(_msg, sizeof(_msg), "%s", m);
_state = s;
lvport::netEnd();
if (s == DONE) discardJob(); // anything else stays resumable
}
void jobPath(char* out, size_t n) const { snprintf(out, n, "%s/.job", _root); }
void writeJob(const TileArea& a) {
char path[64];
jobPath(path, sizeof(path));
makeParents(path);
if (FILE* f = fopen(path, "w")) {
fprintf(f, "%.6f %.6f %.6f %.6f %d %d\n", a.lon0, a.lat0, a.lon1, a.lat1, a.zmin, a.zmax);
fclose(f);
}
_job = a;
_has_job = true;
_job_checked = true;
}
bool readJob(TileArea& a) const {
char path[64];
jobPath(path, sizeof(path));
FILE* f = fopen(path, "r");
if (!f) return false;
TileArea t;
int n = fscanf(f, "%lf %lf %lf %lf %d %d", &t.lon0, &t.lat0, &t.lon1, &t.lat1, &t.zmin, &t.zmax);
fclose(f);
if (n != 6 || t.zmin < 0 || t.zmax > 20 || t.zmin > t.zmax) return false;
a = t;
return true;
}
void onFailure(const char* m) {
@@ -162,6 +162,12 @@ class WioTrackerL2Display : public LGFXDisplay {
public:
WioTrackerL2Display() : LGFXDisplay(320, 240, disp) {}
// NodePrefs::display_brightness level 0-4 -> backlight PWM
void setBrightness(uint8_t level) override {
static const uint8_t PWM[5] = { 20, 60, 120, 190, 255 };
display->setBrightness(PWM[level > 4 ? 4 : level]);
}
// direct access to the LGFX device (LVGL flush/touch glue)
lgfx::LGFX_Device* lgfxDevice() { return &disp; }