feat(ui-lvgl): Nearby screen, node detail, scan popup; NearbyModel in the Core

ui-core/NearbyModel.h builds the Nearby list (contacts + live [LOC] shares
+ heard adverts, or discover-scan results) with type filter and sort,
moved verbatim out of ui-new's NearbyScreen, which now inherits it.

ui-lvgl: Home is three tiles (Messages, Nearby, Settings). Nearby lists
known nodes with filter chips and a Dist/Recent toggle; tapping a row opens
node detail (distance/bearing, position, last heard, ID) with Message,
Ping, favourite, Add and two-tap Delete. Scan is a popup over the list
with its own model: nodes in range now, not the known set. Fonts gain the
FontAwesome star for favourites. The LVGL sim page adds an optional
repeater so Scan has something to find.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-25 00:53:23 +02:00
co-authored by Claude Opus 5.5
parent 87fad71af3
commit 3e5ccd28c7
14 changed files with 1252 additions and 610 deletions
+1 -1
View File
@@ -160,7 +160,7 @@ checked in the sim plus on L1 hardware before the next one.
3. **Flip the interface** ✅. `UiCore` is `MyMesh::Listener`; `ui-new`'s `UITask` derives `UITaskBase` + `UiCoreHost` and is fed by events, drained at the start (mesh-originated) and end (engines) of its `loop()`. Mesh callbacks no longer touch the display or buzzer directly.
4. **Settings schema.** Convert `SettingsScreen` section by section.
5. **`ui-lvgl` skeleton** ✅ on L2 hardware (env `Wio_Tracker_L2_companion_solo_lvgl`, LVGL 9.2.2): status bar, home, conversation list, contact picker, conversation with keyboard, toasts, display sleep/wake. The Core gained the first actions (`sendDirectText`, `sendChannelText`) and runs DM resends itself. Then: European fonts + phone-style keyboard ✅, 320×240 sim target ✅. Next: screens by priority.
6. Contacts/Nearby, Admin, Bot logic extraction as the LVGL screens for them are built.
6. Contacts/Nearby, Admin, Bot logic extraction as the LVGL screens for them are built. Nearby ✅: `ui-core/NearbyModel.h` builds the list (contacts + live [LOC] shares + heard adverts, or scan results), filter and sort; `ui-new`'s `NearbyScreen` inherits it (entry array stays with the screen), `ui-lvgl` owns one for its Nearby list + node detail (Message, Ping, favourite, Add, Delete). Home became three tiles: Messages, Nearby, Settings.
## Decisions
@@ -0,0 +1,321 @@
#pragma once
// Nearby-nodes list model, shared by every frontend's Nearby screen: builds one
// list of entries from two sources and applies the type filter and sort.
// SRC_STORED -- contacts, with live [LOC] shares overlaid (fresher position,
// plus senders who aren't contacts) and, under the All filter,
// passively heard adverts that aren't listed yet
// SRC_SCAN -- NODE_DISCOVER_REQ results (RSSI / SNR), strongest first
// Selection, scrolling and actions stay in the frontend. Each screen owns its
// model (ui-new inherits it, so the entry array lives with the screen as
// before) and binds it to the Core for own position and live shares.
//
// Header-only; include after UiCore.h (needs the_mesh, rtc_clock, geo::).
#include "../GeoUtils.h"
class NearbyModel {
public:
enum Filter : uint8_t { F_ALL, F_FAV, F_COMP, F_RPT, F_ROOM, F_SNSR, F_COUNT };
enum Sort : uint8_t { SORT_DIST, SORT_TIME };
enum Source : uint8_t { SRC_STORED, SRC_SCAN };
struct Entry {
char name[32];
uint8_t type;
uint8_t pub_key[PUB_KEY_SIZE];
// has_key: the full 32-byte pubkey is present -- what Ping and the base64
// key view need. has_prefix: at least the leading FAVOURITE_PREFIX_LEN
// bytes are, which is all an identity-keyed reference needs (a Locator
// person target). Every has_key row also has_prefix; the reverse doesn't
// hold -- a [LOC] share and a heard advert carry a prefix and nothing more.
bool has_key;
bool has_prefix;
// stored-source fields
int32_t lat_e6, lon_e6;
float dist_km; // -1 = unknown (no own fix or no node position)
uint32_t lastmod;
int contact_idx; // raw contact-table index, -1 = not a contact
// scan-source fields
int8_t rssi, snr_x4, remote_snr_x4;
bool is_known;
// live-track ([LOC] share) overlay: this row's position/age came from a
// shared-location message. live_verified: a DM (pubkey) share, not a
// channel (name, best-effort) one.
bool is_live;
bool live_verified;
// Favourite (ContactInfo::flags bit 0) -- only a contact carries it.
bool fav;
};
static const int MAX_NEARBY = 32;
static const char* const* filterLabels() {
static const char* const L[F_COUNT] = { "All", "Fav", "Comp", "Rpt", "Room", "Snsr" };
return L;
}
static const char* filterLabel(uint8_t f) { return f < F_COUNT ? filterLabels()[f] : ""; }
static const char* typeName(uint8_t t) {
switch (t) {
case ADV_TYPE_CHAT: return "Companion";
case ADV_TYPE_REPEATER: return "Repeater";
case ADV_TYPE_ROOM: return "Room";
case ADV_TYPE_SENSOR: return "Sensor";
default: return "Unknown";
}
}
static const char* typeShort(uint8_t t) {
switch (t) {
case ADV_TYPE_REPEATER: return "Rpt";
case ADV_TYPE_SENSOR: return "Snsr";
case ADV_TYPE_ROOM: return "Room";
case ADV_TYPE_CHAT: return "Comp";
default: return "?";
}
}
void bindModel(UiCore* core, NodePrefs* prefs) { _core = core; _prefs = prefs; }
int count() const { return _count; }
const Entry& at(int i) const { return _entries[i]; }
Source source() const { return _source; }
uint8_t filter() const { return _filter; }
uint8_t sortMode() const { return _sort; }
bool ownPosition(int32_t& lat, int32_t& lon) const { lat = _own_lat; lon = _own_lon; return _own_gps; }
void setSource(Source s) { _source = s; }
void setFilter(uint8_t f) { _filter = f < F_COUNT ? f : F_ALL; }
void setSortMode(uint8_t s) { _sort = s; }
bool typeMatchesFilter(uint8_t type, uint8_t flags, bool have_flags) const {
switch (_filter) {
case F_FAV: return have_flags && (flags & 0x01);
case F_COMP: return type == ADV_TYPE_CHAT;
case F_RPT: return type == ADV_TYPE_REPEATER;
case F_ROOM: return type == ADV_TYPE_ROOM;
case F_SNSR: return type == ADV_TYPE_SENSOR;
case F_ALL:
default: return true;
}
}
// ── data refresh ──────────────────────────────────────────────────────────
void refreshStored() {
_count = 0;
_own_lat = _own_lon = 0;
_own_gps = _core && _core->course.currentLocation(_own_lat, _own_lon);
int nc = the_mesh.getNumContacts();
for (int i = 0; i < nc && _count < MAX_NEARBY; i++) {
ContactInfo ci;
// getContactByIdx() indexes the RAW contact table, whose first
// MAX_ANON_CONTACTS slots are reserved for anon-request bookkeeping
// (see BaseChatMesh::resetContacts()/ContactsIterator) -- getNumContacts()
// already excludes them from the count, so real contact 0 lives at raw
// index MAX_ANON_CONTACTS, not 0. Reading from 0 pulled those reserved
// (blank, type=ADV_TYPE_NONE) slots into the list as bogus "Unknown"
// rows, and silently dropped the same number of real contacts off the end.
if (!the_mesh.getContactByIdx(i + MAX_ANON_CONTACTS, ci)) continue;
if (!typeMatchesFilter(ci.type, ci.flags, true)) continue;
Entry& e = _entries[_count++];
strncpy(e.name, ci.name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
memcpy(e.pub_key, ci.id.pub_key, PUB_KEY_SIZE);
e.has_key = true;
e.has_prefix = true;
e.lat_e6 = ci.gps_lat;
e.lon_e6 = ci.gps_lon;
bool remote_gps = (ci.gps_lat != 0 || ci.gps_lon != 0);
e.dist_km = (_own_gps && remote_gps)
? geo::haversineKm(_own_lat, _own_lon, ci.gps_lat, ci.gps_lon)
: -1.0f;
e.type = ci.type;
e.fav = (ci.flags & 0x01) != 0;
e.contact_idx = i + MAX_ANON_CONTACTS; // raw index -- other lookups re-key off this directly
e.lastmod = ci.lastmod;
e.is_known = true;
e.is_live = false;
e.live_verified = false;
}
mergeLiveTrack();
mergeRecentlyHeard();
sortStored();
}
void refreshScan() {
static DiscoverResult dr[DISCOVER_RESULTS_MAX]; // scratch -- refresh isn't reentrant
int n = the_mesh.getDiscoverResults(dr, DISCOVER_RESULTS_MAX);
_count = 0;
for (int i = 0; i < n && _count < MAX_NEARBY; i++) {
if (!typeMatchesFilter(dr[i].type, 0, false)) continue;
Entry& e = _entries[_count++];
strncpy(e.name, dr[i].name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
e.type = dr[i].type;
memcpy(e.pub_key, dr[i].pub_key, PUB_KEY_SIZE);
e.has_key = true;
e.has_prefix = true;
e.rssi = dr[i].rssi;
e.snr_x4 = dr[i].snr_x4;
e.remote_snr_x4 = dr[i].remote_snr_x4;
e.is_known = dr[i].is_known;
e.fav = false;
e.lat_e6 = e.lon_e6 = 0;
e.dist_km = -1.0f;
e.lastmod = 0;
e.contact_idx = -1;
e.is_live = false;
e.live_verified = false;
}
// strongest first
for (int i = 0; i < _count - 1; i++) {
int best = i;
for (int j = i + 1; j < _count; j++)
if (_entries[j].rssi > _entries[best].rssi) best = j;
if (best != i) { Entry tmp = _entries[i]; _entries[i] = _entries[best]; _entries[best] = tmp; }
}
}
void refreshModel() { if (_source == SRC_SCAN) refreshScan(); else refreshStored(); }
// Index of the row for raw contact index `contact_idx`, or of the non-contact
// live row named `name` -- how a screen keeps its selection across a rebuild.
int findContact(int contact_idx) const {
for (int i = 0; i < _count; i++) if (_entries[i].contact_idx == contact_idx) return i;
return -1;
}
int findLiveByName(const char* name) const {
for (int i = 0; i < _count; i++)
if (_entries[i].is_live && strncmp(_entries[i].name, name, sizeof(_entries[i].name) - 1) == 0) return i;
return -1;
}
protected:
UiCore* _core = nullptr;
NodePrefs* _prefs = nullptr;
Entry _entries[MAX_NEARBY];
int _count = 0;
int32_t _own_lat = 0, _own_lon = 0;
bool _own_gps = false;
Source _source = SRC_STORED;
uint8_t _filter = F_ALL;
uint8_t _sort = SORT_DIST;
// Overlay live [LOC] shares onto the stored list: refresh a matching contact
// with the fresher shared position, and append senders who aren't contacts so
// a group sharing on a channel still shows up. DM shares match by pubkey
// prefix (verified); channel shares match by name (best-effort).
void mergeLiveTrack() {
if (!_core) return;
LiveTrackStore& lt = _core->live_share.track();
uint32_t now = rtc_clock.getCurrentTime();
for (int i = 0; i < LiveTrackStore::CAPACITY; i++) {
if (!lt.isActive(i, now)) continue;
const LiveTrackStore::Entry& s = lt.slotAt(i);
int m = -1;
for (int j = 0; j < _count; j++) {
if (s.verified && _entries[j].has_key
&& memcmp(_entries[j].pub_key, s.key, LiveTrackStore::KEY_LEN) == 0) { m = j; break; }
if (!s.verified && strncmp(_entries[j].name, s.name, sizeof(_entries[j].name) - 1) == 0) { m = j; break; }
}
if (m >= 0) {
Entry& e = _entries[m];
// A [LOC] share is an explicit "here I am now", so it defines the pin and
// the distance (used for proximity sorting). Recency for the time sort is
// the most recent of the advert and the share.
e.lat_e6 = s.lat_1e6;
e.lon_e6 = s.lon_1e6;
e.dist_km = _own_gps ? geo::haversineKm(_own_lat, _own_lon, s.lat_1e6, s.lon_1e6) : -1.0f;
if (s.ts > e.lastmod) e.lastmod = s.ts;
e.is_live = true;
e.live_verified = s.verified;
} else if (_count < MAX_NEARBY && typeMatchesFilter(ADV_TYPE_CHAT, 0, false)) {
// A sender we don't have as a contact: treat it as a companion (the only
// node type that shares position), so the type filter still applies.
Entry& e = _entries[_count++];
memset(&e, 0, sizeof(e));
strncpy(e.name, s.name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
// Only a key *prefix* is kept for shares, so Ping and the key view stay
// unavailable for a non-contact live entry; Navigate / waypoint work off
// lat/lon. A DM share's prefix is real (a Locator person target); a
// channel share is matched by name and carries no identity at all.
e.has_key = false;
e.has_prefix = s.verified;
if (s.verified) memcpy(e.pub_key, s.key, LiveTrackStore::KEY_LEN);
e.type = ADV_TYPE_CHAT;
e.lat_e6 = s.lat_1e6;
e.lon_e6 = s.lon_1e6;
e.dist_km = _own_gps ? geo::haversineKm(_own_lat, _own_lon, s.lat_1e6, s.lon_1e6) : -1.0f;
e.lastmod = s.ts;
e.contact_idx = -1;
e.is_known = false;
e.is_live = true;
e.live_verified = s.verified;
}
}
}
// Fold passively-heard adverts that aren't already listed (contact or live) into
// the list as name+age rows -- no full key/GPS, so informational only. Gated to
// the All filter because AdvertPath carries no node type to filter or sort on.
void mergeRecentlyHeard() {
if (_filter != F_ALL) return;
static AdvertPath heard[8]; // scratch -- refreshStored isn't reentrant
int n = the_mesh.getRecentlyHeard(heard, 8);
for (int i = 0; i < n && _count < MAX_NEARBY; i++) {
AdvertPath& a = heard[i];
if (a.name[0] == 0) continue;
bool dup = false;
for (int j = 0; j < _count; j++) {
if (_entries[j].has_key &&
memcmp(_entries[j].pub_key, a.pubkey_prefix, sizeof(a.pubkey_prefix)) == 0) { dup = true; break; }
if (strncmp(_entries[j].name, a.name, sizeof(a.name) - 1) == 0) { dup = true; break; }
}
if (dup) continue;
Entry& e = _entries[_count++];
memset(&e, 0, sizeof(e));
strncpy(e.name, a.name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
e.type = ADV_TYPE_CHAT; // unknown from AdvertPath -- best-effort label
e.has_key = false;
e.has_prefix = true;
memcpy(e.pub_key, a.pubkey_prefix, sizeof(a.pubkey_prefix));
e.dist_km = -1.0f;
e.lastmod = a.recv_timestamp;
e.contact_idx = -1;
e.is_known = false;
e.is_live = false;
}
}
void sortStored() {
uint32_t now_ts = rtc_clock.getCurrentTime();
const bool fav_first = !(_prefs && _prefs->fav_sort_off);
for (int i = 0; i < _count - 1; i++) {
int best = i;
for (int j = i + 1; j < _count; j++) {
if (fav_first && _entries[j].fav != _entries[best].fav) {
if (_entries[j].fav) best = j; // favourites outrank the time/distance key
continue;
}
if (_sort == SORT_TIME) {
// lastmod=0 or lastmod>now (RTC not synced) → "unknown" → sort to bottom.
uint32_t tj = (_entries[j].lastmod > 0 && now_ts >= _entries[j].lastmod) ? _entries[j].lastmod : 0;
uint32_t tb = (_entries[best].lastmod > 0 && now_ts >= _entries[best].lastmod) ? _entries[best].lastmod : 0;
if (tj > 0 && (tb == 0 || tj > tb)) best = j; // descending — most recent first
} else {
float dj = _entries[j].dist_km, db = _entries[best].dist_km;
if (dj >= 0.0f && (db < 0.0f || dj < db)) best = j; // ascending — closest first
}
}
if (best != i) { Entry tmp = _entries[i]; _entries[i] = _entries[best]; _entries[best] = tmp; }
}
}
};
+3
View File
@@ -28,4 +28,7 @@ namespace theme {
#define THEME_FONT_TITLE (&ui_font_16)
#define THEME_FONT_LARGE (&ui_font_20)
#define THEME_FONT_CLOCK (&ui_font_40)
// Extra FontAwesome glyphs baked into ui_font_12..20 beyond LVGL's LV_SYMBOL_*.
#define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite
}
+489 -10
View File
@@ -7,6 +7,7 @@
#endif
#include "../ui-core/UiCore.h" // shared UI Core (header-only, this TU)
#include "../ui-core/NearbyModel.h"
#include "Theme.h"
#include "LvglPort.h"
#include "../ui-core/KeyboardData.h"
@@ -87,6 +88,11 @@ void UITask::begin(DisplayDriver* display_drv, SensorManager* sensors, NodePrefs
_core = new UiCore();
_core->begin(node_prefs, sensors, this);
_nearby = new NearbyModel();
_nearby->bindModel(_core, node_prefs);
_scan = new NearbyModel();
_scan->bindModel(_core, node_prefs);
_scan->setSource(NearbyModel::SRC_SCAN);
#ifdef PIN_USER_BTN
user_btn.begin();
@@ -141,6 +147,17 @@ void UITask::loop() {
refreshStatusBar();
if (_screen == SCR_HOME) refreshHome();
}
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);
if (_scanning && (int32_t)(millis() - _scan_until_ms) >= 0) _scanning = false;
if (_screen == SCR_NEARBY) {
refreshNearbyList();
if (_scan_overlay) refreshScanPopup();
} else {
refreshNode();
}
}
if (_screen == SCR_THREAD && (int32_t)(millis() - _next_thread_check_ms) >= 0) {
_next_thread_check_ms = millis() + 500;
uint32_t sig = threadSignature();
@@ -323,6 +340,9 @@ lv_obj_t* UITask::newScreen(const char* title, bool with_back) {
_home_clock = _home_date = _home_unread = nullptr;
_thread_list = _compose_ta = _keyboard = nullptr;
_header = _body = nullptr;
_nearby_list = _nearby_status = _nearby_sort_lbl = _nearby_chips = nullptr;
_node_info = _node_ping = _node_delete_lbl = nullptr;
_scan_overlay = _scan_list = _scan_status = nullptr; // the popup went with the old screen
lv_obj_t* prev = lv_screen_active();
lv_obj_t* scr = lv_obj_create(NULL);
styleSurface(scr, theme::BG);
@@ -371,6 +391,15 @@ void UITask::back() {
case SCR_CONTACTS: showChats(); break;
case SCR_SETTINGS: showHome(); 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: the list, or the scan popup over it
_screen = SCR_NEARBY;
buildNearby();
if (_node_from_scan) showScanPopup();
break;
default: break;
}
}
@@ -380,19 +409,26 @@ void UITask::back() {
static void onOpenChats(lv_event_t* e) { (void)e; s_ui->showChats(); }
static void onOpenSettings(lv_event_t* e) { (void)e; s_ui->showSettings(); }
// Wide home tile: icon + label left, optional value label right.
static lv_obj_t* homeTile(lv_obj_t* parent, const char* text, lv_event_cb_t cb, lv_obj_t** value_out) {
static void onOpenNearby(lv_event_t* e) { (void)e; s_ui->showNearby(); }
// Home tile: icon over label, one of a row of three; 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) {
lv_obj_t* tile = lv_button_create(parent);
lv_obj_set_size(tile, LV_PCT(100), 42);
lv_obj_set_height(tile, 64);
lv_obj_set_flex_grow(tile, 1);
lv_obj_set_style_bg_color(tile, lv_color_hex(theme::SURFACE), 0);
lv_obj_set_style_bg_color(tile, lv_color_hex(theme::SURFACE_2), LV_STATE_PRESSED);
lv_obj_set_style_radius(tile, theme::RADIUS, 0);
lv_obj_set_style_shadow_width(tile, 0, 0);
lv_obj_set_style_pad_all(tile, 4, 0);
lv_obj_add_event_cb(tile, cb, LV_EVENT_CLICKED, NULL);
lv_obj_t* tl = label(tile, text, THEME_FONT_TITLE, theme::TEXT);
lv_obj_align(tl, LV_ALIGN_LEFT_MID, theme::PAD, 0);
lv_obj_align(label(tile, icon, THEME_FONT_LARGE, theme::TEXT), LV_ALIGN_TOP_MID, 0, 6);
lv_obj_align(label(tile, text, THEME_FONT_SMALL, theme::TEXT_MUTED), LV_ALIGN_BOTTOM_MID, 0, -4);
if (value_out) {
*value_out = label(tile, "", THEME_FONT_TITLE, theme::ACCENT);
lv_obj_align(*value_out, LV_ALIGN_RIGHT_MID, -theme::PAD, 0);
*value_out = label(tile, "", THEME_FONT_SMALL, theme::ACCENT);
lv_obj_align(*value_out, LV_ALIGN_TOP_RIGHT, -2, 0);
}
return tile;
}
@@ -412,8 +448,15 @@ 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);
homeTile(body, LV_SYMBOL_ENVELOPE " Messages", onOpenChats, &_home_unread);
homeTile(body, LV_SYMBOL_SETTINGS " Settings", onOpenSettings, NULL);
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, LV_SYMBOL_GPS, "Nearby", onOpenNearby, NULL);
homeTile(tiles, LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, NULL);
}
void UITask::refreshHome() {
@@ -433,7 +476,7 @@ void UITask::refreshHome() {
lv_label_set_text(_home_date, "time not synced");
}
int unread = _core->dmUnreadTotal() + _core->history.getTotalChannelUnread() + _core->roomUnread();
if (unread > 0) lv_label_set_text_fmt(_home_unread, "%d new", unread);
if (unread > 0) lv_label_set_text_fmt(_home_unread, "%d", unread);
else lv_label_set_text(_home_unread, "");
}
@@ -565,6 +608,442 @@ void UITask::buildContacts() {
if (rows == 0) label(body, "No contacts yet", THEME_FONT_BODY, theme::TEXT_MUTED);
}
// ── Nearby ────────────────────────────────────────────────────────────────────
// Contacts / live shares / heard adverts from NearbyModel, filtered by type
// chips, sorted by distance or recency. Tap a row for detail. Scan (nodes that
// answer a discover request right now) is a popup over the list with its own
// 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 };
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(); }
static void onNearbyScan(lv_event_t* e) { (void)e; s_ui->startNearbyScan(); }
static void onNearbyRow(lv_event_t* e) { s_ui->openNode((int)(uintptr_t)lv_event_get_user_data(e)); }
static void onScanRow(lv_event_t* e) { s_ui->openScanNode((int)(uintptr_t)lv_event_get_user_data(e)); }
static void onScanClose(lv_event_t* e) { (void)e; s_ui->closeScanPopup(); }
static void onNodeAction(lv_event_t* e) { s_ui->nodeAction((uint8_t)(uintptr_t)lv_event_get_user_data(e)); }
static lv_obj_t* headerButton(lv_obj_t* hdr, const char* text, lv_event_cb_t cb, int right, lv_obj_t** label_out) {
lv_obj_t* b = lv_button_create(hdr);
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_bg_color(b, lv_color_hex(theme::SURFACE), 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::SURFACE_2), LV_STATE_PRESSED);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_align(b, LV_ALIGN_RIGHT_MID, -right, 0);
lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, NULL);
lv_obj_t* l = label(b, text, THEME_FONT_SMALL, theme::TEXT);
lv_obj_center(l);
if (label_out) *label_out = l;
return b;
}
void UITask::showNearby() {
_screen = SCR_NEARBY;
_scanning = false;
buildNearby(); // filter / sort persist
}
void UITask::buildNearby() {
lv_obj_t* body = newScreen("Nearby", true);
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);
}
// Type filter chips
_nearby_chips = lv_obj_create(body);
styleSurface(_nearby_chips, theme::BG);
lv_obj_remove_flag(_nearby_chips, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(_nearby_chips, LV_PCT(100), 26);
lv_obj_set_flex_flow(_nearby_chips, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(_nearby_chips, 4, 0);
for (uint8_t f = 0; f < NearbyModel::F_COUNT; f++) {
lv_obj_t* c = lv_button_create(_nearby_chips);
lv_obj_set_height(c, 26);
lv_obj_set_flex_grow(c, 1);
lv_obj_set_style_pad_all(c, 0, 0);
lv_obj_set_style_radius(c, 13, 0);
lv_obj_set_style_shadow_width(c, 0, 0);
bool on = f == _nearby->filter();
lv_obj_set_style_bg_color(c, lv_color_hex(on ? theme::ACCENT : theme::SURFACE), 0);
lv_obj_add_event_cb(c, onNearbyChip, LV_EVENT_CLICKED, (void*)(uintptr_t)f);
lv_obj_center(label(c, NearbyModel::filterLabel(f), THEME_FONT_SMALL, on ? theme::BG : theme::TEXT));
}
_nearby_status = label(body, "", THEME_FONT_SMALL, theme::TEXT_MUTED);
_nearby_list = lv_obj_create(body);
styleSurface(_nearby_list, theme::BG);
lv_obj_set_width(_nearby_list, LV_PCT(100));
lv_obj_set_flex_grow(_nearby_list, 1);
lv_obj_set_flex_flow(_nearby_list, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(_nearby_list, theme::GAP, 0);
lv_obj_set_scrollbar_mode(_nearby_list, LV_SCROLLBAR_MODE_ACTIVE);
_nearby_sig = 0;
refreshNearbyList();
}
void UITask::setNearbyFilter(uint8_t f) {
_nearby->setFilter(f);
buildNearby();
}
void UITask::toggleNearbySort() {
_nearby->setSortMode(_nearby->sortMode() == NearbyModel::SORT_DIST ? NearbyModel::SORT_TIME
: NearbyModel::SORT_DIST);
_nearby_sig = 0;
refreshNearbyList();
}
void UITask::startNearbyScan() {
_scanning = true;
_scan_until_ms = millis() + 8000;
_next_nearby_ms = millis() + 250;
the_mesh.sendNodeDiscoverReq();
if (!_scan_overlay) showScanPopup();
else { _scan_sig = 0; refreshScanPopup(); }
}
// Dimmed full-screen overlay (swallows taps) holding a panel with the results.
// A child of the current screen, so it goes away with it.
void UITask::showScanPopup() {
_scan_overlay = lv_obj_create(lv_screen_active());
lv_obj_remove_style_all(_scan_overlay);
lv_obj_set_size(_scan_overlay, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_color(_scan_overlay, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(_scan_overlay, LV_OPA_60, 0);
lv_obj_add_flag(_scan_overlay, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_flag(_scan_overlay, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t* panel = lv_obj_create(_scan_overlay);
lv_obj_set_size(panel, lv_display_get_horizontal_resolution(NULL) - 16,
lv_display_get_vertical_resolution(NULL) - theme::STATUS_H - 12);
lv_obj_set_pos(panel, 8, theme::STATUS_H + 6);
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, 4, 0);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN);
lv_obj_remove_flag(panel, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t* hdr = lv_obj_create(panel);
styleSurface(hdr, theme::BG);
lv_obj_remove_flag(hdr, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(hdr, LV_PCT(100), 28);
lv_obj_align(label(hdr, "In range now", THEME_FONT_TITLE, theme::TEXT), LV_ALIGN_LEFT_MID, 0, 0);
headerButton(hdr, LV_SYMBOL_CLOSE, onScanClose, 0, NULL);
headerButton(hdr, LV_SYMBOL_REFRESH " Again", onNearbyScan, 44, NULL);
_scan_status = label(panel, "", THEME_FONT_SMALL, theme::TEXT_MUTED);
_scan_list = lv_obj_create(panel);
styleSurface(_scan_list, theme::BG);
lv_obj_set_width(_scan_list, LV_PCT(100));
lv_obj_set_flex_grow(_scan_list, 1);
lv_obj_set_flex_flow(_scan_list, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(_scan_list, theme::GAP, 0);
lv_obj_set_scrollbar_mode(_scan_list, LV_SCROLLBAR_MODE_ACTIVE);
_scan_sig = 0;
refreshScanPopup();
}
void UITask::closeScanPopup() {
if (_scan_overlay) lv_obj_delete_async(_scan_overlay); // may be closing from its own button
_scan_overlay = _scan_list = _scan_status = nullptr;
_scanning = false;
}
void UITask::refreshScanPopup() {
if (!_scan_list) return;
_scan->refreshScan();
int n = _scan->count();
if (_scanning) lv_label_set_text_fmt(_scan_status, LV_SYMBOL_REFRESH " Listening for replies... %d", n);
else lv_label_set_text_fmt(_scan_status, "%d node%s answered the discover request", n, n == 1 ? "" : "s");
uint32_t sig = (uint32_t)n + (_scanning ? 0x10000u : 0);
for (int i = 0; i < n; i++) {
const NearbyModel::Entry& e = _scan->at(i);
sig = sig * 31 + e.rssi * 7 + e.snr_x4 + e.is_known;
for (int k = 0; k < 4; k++) sig = sig * 31 + e.pub_key[k];
}
if (sig == _scan_sig) return;
_scan_sig = sig;
lv_obj_clean(_scan_list);
for (int i = 0; i < n; i++) {
const NearbyModel::Entry& e = _scan->at(i);
char title[40], sub[48], right[12];
if (e.name[0]) snprintf(title, sizeof(title), "%s", e.name);
else snprintf(title, sizeof(title), "%s %02X%02X%02X%02X", NearbyModel::typeName(e.type),
e.pub_key[0], e.pub_key[1], e.pub_key[2], e.pub_key[3]);
snprintf(sub, sizeof(sub), "%s - SNR %.1f / %.1f%s", NearbyModel::typeName(e.type),
e.snr_x4 / 4.0f, e.remote_snr_x4 / 4.0f, e.is_known ? "" : " - new");
snprintf(right, sizeof(right), "%d dBm", e.rssi);
lv_obj_t* row = listRow(_scan_list, title, sub, onScanRow, (void*)(uintptr_t)i);
lv_obj_align(label(row, right, THEME_FONT_SMALL, theme::TEXT_MUTED), LV_ALIGN_RIGHT_MID, -theme::PAD, 0);
}
if (n == 0) {
lv_obj_t* l = label(_scan_list, _scanning ? "Repeaters and rooms in range will answer."
: "Nobody answered. Try again later or move.",
THEME_FONT_BODY, theme::TEXT_MUTED);
lv_label_set_long_mode(l, LV_LABEL_LONG_WRAP);
lv_obj_set_width(l, LV_PCT(100));
lv_obj_set_style_pad_top(l, 8, 0);
}
}
void UITask::openScanNode(int row) {
if (row < 0 || row >= _scan->count()) return;
s_node = _scan->at(row);
_node_from_scan = true;
_scanning = false;
_pinging = false;
_delete_armed_ms = 0;
_screen = SCR_NODE;
buildNode();
}
// Changes whenever a rebuild would show something different.
uint32_t UITask::nearbySignature() const {
uint32_t sig = (uint32_t)_nearby->count() * 2654435761u + _nearby->sortMode() + (_scanning ? 7 : 0);
for (int i = 0; i < _nearby->count(); i++) {
const NearbyModel::Entry& e = _nearby->at(i);
sig = sig * 31 + e.contact_idx + (uint32_t)e.lastmod + (uint32_t)(e.dist_km * 100) + e.rssi + e.fav;
for (const char* p = e.name; *p; p++) sig = sig * 31 + (uint8_t)*p;
}
// Ages are shown in minutes, so let the list re-render once a minute anyway.
return sig + rtc_clock.getCurrentTime() / 60;
}
void UITask::refreshNearbyList() {
if (!_nearby_list) return;
_nearby->refreshModel();
uint32_t sig = nearbySignature();
int n = _nearby->count();
if (_nearby_sort_lbl)
lv_label_set_text(_nearby_sort_lbl, _nearby->sortMode() == NearbyModel::SORT_TIME ? "Recent" : "Dist");
int32_t lat, lon;
bool gps = _nearby->ownPosition(lat, lon);
lv_label_set_text_fmt(_nearby_status, "%d node%s%s", n, n == 1 ? "" : "s",
gps ? "" : " - no GPS fix, distances unknown");
if (sig == _nearby_sig) return;
_nearby_sig = sig;
int32_t scroll = lv_obj_get_scroll_y(_nearby_list);
lv_obj_clean(_nearby_list);
uint32_t now = rtc_clock.getCurrentTime();
bool imperial = _prefs && _prefs->units_imperial;
for (int i = 0; i < n; i++) {
const NearbyModel::Entry& e = _nearby->at(i);
char title[48], sub[48], right[16] = "";
const char* name = e.name[0] ? e.name : "(unknown)";
snprintf(title, sizeof(title), "%s%s", e.fav ? UI_SYMBOL_STAR " " : "", name);
char age[8];
geo::fmtAgeShort(age, sizeof(age), now, e.lastmod);
if (e.dist_km >= 0.0f) geo::fmtDist(right, sizeof(right), e.dist_km, imperial);
else if (age[0]) snprintf(right, sizeof(right), "%s", age);
const char* kind = e.contact_idx >= 0 ? NearbyModel::typeName(e.type) : "not a contact";
snprintf(sub, sizeof(sub), "%s%s%s%s", kind, e.is_live ? " - live" : "",
(e.dist_km >= 0.0f && age[0]) ? " - " : "", (e.dist_km >= 0.0f && age[0]) ? age : "");
lv_obj_t* row = listRow(_nearby_list, title, sub, onNearbyRow, (void*)(uintptr_t)i);
if (e.fav) lv_obj_set_style_text_color(lv_obj_get_child(row, 0), lv_color_hex(theme::ACCENT), 0);
if (right[0]) {
lv_obj_t* r = label(row, right, THEME_FONT_SMALL, e.is_live ? theme::OK : theme::TEXT_MUTED);
lv_obj_align(r, LV_ALIGN_RIGHT_MID, -theme::PAD, 0);
}
}
if (n == 0) {
lv_obj_t* l = label(_nearby_list, "Nobody here yet. Tap Scan to look around.",
THEME_FONT_BODY, theme::TEXT_MUTED);
lv_obj_set_style_pad_top(l, 12, 0);
}
lv_obj_update_layout(_nearby_list);
lv_obj_scroll_to_y(_nearby_list, scroll, LV_ANIM_OFF);
}
// ── Node detail ───────────────────────────────────────────────────────────────
void UITask::openNode(int row) {
if (row < 0 || row >= _nearby->count()) return;
s_node = _nearby->at(row);
_node_from_scan = false;
_pinging = false;
_delete_armed_ms = 0;
_screen = SCR_NODE;
buildNode();
}
static lv_obj_t* actionButton(lv_obj_t* parent, const char* text, uint8_t action, bool accent) {
lv_obj_t* b = lv_button_create(parent);
lv_obj_set_height(b, 40);
lv_obj_set_flex_grow(b, 1);
lv_obj_set_style_pad_hor(b, 4, 0);
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(accent ? theme::ACCENT_DIM : theme::SURFACE), 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::SURFACE_2), LV_STATE_PRESSED);
lv_obj_add_event_cb(b, onNodeAction, LV_EVENT_CLICKED, (void*)(uintptr_t)action);
lv_obj_t* l = label(b, text, THEME_FONT_SMALL, theme::TEXT);
lv_obj_center(l);
return l;
}
void UITask::buildNode() {
const NearbyModel::Entry& e = s_node;
lv_obj_t* body = newScreen(e.name[0] ? e.name : "(unknown)", true);
_node_info = label(body, "", THEME_FONT_BODY, theme::TEXT);
lv_label_set_long_mode(_node_info, LV_LABEL_LONG_WRAP);
lv_obj_set_width(_node_info, LV_PCT(100));
lv_obj_set_style_text_line_space(_node_info, 3, 0);
_node_ping = label(body, "", THEME_FONT_BODY, theme::ACCENT);
lv_obj_t* spacer = lv_obj_create(body); // pushes the actions to the bottom
lv_obj_remove_style_all(spacer);
lv_obj_set_width(spacer, 1);
lv_obj_set_flex_grow(spacer, 1);
lv_obj_t* acts = lv_obj_create(body);
styleSurface(acts, theme::BG);
lv_obj_remove_flag(acts, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(acts, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(acts, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(acts, theme::GAP, 0);
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 (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);
refreshNode();
}
void UITask::refreshNode() {
if (!_node_info) return;
const NearbyModel::Entry& e = s_node;
// Refresh position / age / signal from its model while the node is still listed.
NearbyModel* model = _node_from_scan ? _scan : _nearby;
model->refreshModel();
for (int i = 0; i < model->count(); i++) {
const NearbyModel::Entry& m = model->at(i);
bool same = e.has_key ? (m.has_key && memcmp(m.pub_key, e.pub_key, PUB_KEY_SIZE) == 0)
: (e.contact_idx >= 0) ? m.contact_idx == e.contact_idx
: strncmp(m.name, e.name, sizeof(m.name)) == 0;
if (same) { s_node = m; break; }
}
char buf[320];
int o = 0;
// A scan row carries no contact index; is_known says whether it's in the contacts.
bool known = e.contact_idx >= 0 || (_node_from_scan && e.is_known);
o += snprintf(buf + o, sizeof(buf) - o, "%s%s%s%s", NearbyModel::typeName(e.type),
known ? "" : " - not a contact",
e.is_live ? (e.live_verified ? " - live position" : " - live position (channel)") : "",
e.fav ? " - favourite" : "");
int32_t lat, lon;
bool gps = _nearby->ownPosition(lat, lon);
if (e.lat_e6 != 0 || e.lon_e6 != 0) {
if (gps && e.dist_km >= 0.0f) {
char d[16];
geo::fmtDist(d, sizeof(d), e.dist_km, _prefs && _prefs->units_imperial);
int az = geo::bearingDeg(lat, lon, e.lat_e6, e.lon_e6);
o += snprintf(buf + o, sizeof(buf) - o, "\n%s %d\xC2\xB0 %s", d, az, geo::bearingCardinal(az));
}
o += snprintf(buf + o, sizeof(buf) - o, "\n%.5f, %.5f", e.lat_e6 / 1e6, e.lon_e6 / 1e6);
} else if (!_node_from_scan) {
o += snprintf(buf + o, sizeof(buf) - o, "\nNo position shared");
}
char age[8];
geo::fmtAgeShort(age, sizeof(age), rtc_clock.getCurrentTime(), e.lastmod);
if (age[0]) o += snprintf(buf + o, sizeof(buf) - o, "\nHeard %s ago", age);
if (_node_from_scan)
o += snprintf(buf + o, sizeof(buf) - o, "\nRSSI %d dBm - SNR %.1f / %.1f dB", e.rssi,
e.snr_x4 / 4.0f, e.remote_snr_x4 / 4.0f);
if (e.has_prefix)
o += snprintf(buf + o, sizeof(buf) - o, "\nID %02X%02X%02X%02X", e.pub_key[0], e.pub_key[1], e.pub_key[2], e.pub_key[3]);
lv_label_set_text(_node_info, buf);
// Ping result (PingEngine releases its slot on reply; the view owns the timeout)
if (_pinging) {
int16_t out = 0, back = 0; uint32_t rtt = 0;
_core->ping.getResult(out, back, rtt);
if (!_core->ping.isActive() && (out || back || rtt)) {
lv_label_set_text_fmt(_node_ping, "Ping %lu ms - SNR out %.1f back %.1f", (unsigned long)rtt,
out / 4.0f, back / 4.0f);
_pinging = false;
} else if (millis() - _ping_started_ms > 3000) {
_core->ping.clear();
lv_label_set_text(_node_ping, "Ping: no reply");
_pinging = false;
}
}
if (_node_delete_lbl && _delete_armed_ms && millis() - _delete_armed_ms > 3000) {
_delete_armed_ms = 0;
lv_label_set_text(_node_delete_lbl, LV_SYMBOL_TRASH);
}
}
void UITask::nodeAction(uint8_t action) {
NearbyModel::Entry& e = s_node;
switch (action) {
case NODE_MSG:
openDM(e.pub_key);
break;
case NODE_PING: {
if (_pinging) break;
PingEngine::StartResult r = _core->ping.start(e.pub_key);
if (r == PingEngine::STARTED) {
_pinging = true;
_ping_started_ms = millis();
_next_nearby_ms = millis() + 250;
lv_label_set_text(_node_ping, "Pinging...");
} else {
lv_label_set_text(_node_ping, r == PingEngine::UNSUPPORTED ? "Ping needs 1-2 byte path hashes"
: "Ping failed");
}
break;
}
case NODE_FAV:
if (the_mesh.setContactFavourite(e.pub_key, !e.fav)) {
e.fav = !e.fav;
buildNode();
}
break;
case NODE_ADD:
if (the_mesh.addDiscoveredContact(e.pub_key, e.name, e.type)) {
showToast("Contact added");
_screen = SCR_NEARBY;
buildNearby();
} else {
showToast("Contacts full");
}
break;
case NODE_DELETE:
if (!_delete_armed_ms) { // destructive: second tap within 3 s confirms
_delete_armed_ms = millis();
if (_delete_armed_ms == 0) _delete_armed_ms = 1;
lv_label_set_text(_node_delete_lbl, "Delete?");
break;
}
if (the_mesh.deleteContactByKey(e.pub_key)) {
showToast("Contact deleted");
_screen = SCR_NEARBY;
buildNearby();
}
break;
}
}
// ── Conversation ──────────────────────────────────────────────────────────────
static void onKeyboard(lv_event_t* e) {
+40 -1
View File
@@ -15,6 +15,7 @@
#include "../ui-core/UiCoreHost.h"
class UiCore;
class NearbyModel;
struct UiEvent;
class UITask : public UITaskBase, public UiCoreHost {
@@ -37,6 +38,14 @@ public:
void showChats();
void showContacts();
void showSettings();
void showNearby();
void setNearbyFilter(uint8_t f);
void toggleNearbySort();
void startNearbyScan();
void closeScanPopup();
void openScanNode(int row);
void openNode(int row);
void nodeAction(uint8_t action);
void setKeyboardAlphabets(int main_idx, int alt_sel);
void openChannel(uint8_t channel_idx);
void openDM(const uint8_t* pub_key);
@@ -50,7 +59,7 @@ public:
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 };
enum Screen : uint8_t { SCR_HOME, SCR_CHATS, SCR_CONTACTS, SCR_THREAD, SCR_SETTINGS, SCR_NEARBY, SCR_NODE };
void buildStatusBar();
void refreshStatusBar();
@@ -60,6 +69,13 @@ private:
void buildChats();
void buildContacts();
void buildSettings();
void buildNearby();
void refreshNearbyList();
uint32_t nearbySignature() const;
void showScanPopup();
void refreshScanPopup();
void buildNode();
void refreshNode();
void buildThread();
void refreshThread();
uint32_t threadSignature() const;
@@ -99,4 +115,27 @@ private:
lv_obj_t* _thread_list = nullptr;
lv_obj_t* _compose_ta = nullptr;
lv_obj_t* _keyboard = nullptr;
// Nearby (SCR_NEARBY) / node detail (SCR_NODE); list data in NearbyModel
NearbyModel* _nearby = nullptr;
NearbyModel* _scan = nullptr; // NODE_DISCOVER results: nodes in range now (popup over the list)
bool _scanning = false;
bool _node_from_scan = false;
uint32_t _scan_sig = 0;
lv_obj_t* _scan_overlay = nullptr;
lv_obj_t* _scan_list = nullptr;
lv_obj_t* _scan_status = nullptr;
uint32_t _scan_until_ms = 0;
uint32_t _nearby_sig = 0;
uint32_t _next_nearby_ms = 0;
bool _pinging = false;
uint32_t _ping_started_ms = 0;
uint32_t _delete_armed_ms = 0;
lv_obj_t* _nearby_list = nullptr;
lv_obj_t* _nearby_status = nullptr;
lv_obj_t* _nearby_sort_lbl = nullptr;
lv_obj_t* _nearby_chips = nullptr;
lv_obj_t* _node_info = nullptr;
lv_obj_t* _node_ping = nullptr;
lv_obj_t* _node_delete_lbl = nullptr;
};
@@ -26,8 +26,8 @@ FA_URL=https://raw.githubusercontent.com/lvgl/lvgl/v9.2.2/scripts/built_in_font/
TEXT="0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3"
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)
SYMS="61612,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"
# plus 61612 = globe (keyboard script key), 61445 = star (favourite)
SYMS="61612,61445,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
@@ -1,7 +1,7 @@
/*******************************************************************************
* Size: 12 px
* Bpp: 4
* Opts: --bpp 4 --size 12 --force-fast-kern-format --font /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/fonts/fa.woff -r 61612,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 61612,61445,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
@@ -6379,6 +6379,17 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x70, 0x3, 0x20, 0x80, 0x19, 0x0, 0x2e, 0x0,
0x17, 0x6f, 0x4e, 0x32, 0xc3, 0x0, 0x62, 0x10,
/* U+F005 "" */
0x0, 0xf2, 0x90, 0x7, 0xfc, 0x75, 0x20, 0x1f,
0xf4, 0x2, 0x18, 0x7, 0xf1, 0xa0, 0x2, 0x0,
0x38, 0xa2, 0xb7, 0x0, 0x24, 0xec, 0x95, 0x4,
0x75, 0x20, 0xe, 0x13, 0x6b, 0x6, 0x60, 0x7,
0xf0, 0xe0, 0x2, 0x5c, 0x3, 0xe1, 0xc2, 0x0,
0xa0, 0x40, 0x3d, 0x4, 0x1, 0xc2, 0x1, 0xee,
0x0, 0xe3, 0x0, 0x84, 0x2, 0x30, 0xe, 0x60,
0x18, 0xec, 0x40, 0x60, 0xe, 0x49, 0xe7, 0x3,
0xbc, 0x80, 0x8,
/* U+F008 "" */
0xb4, 0xdf, 0xff, 0x6a, 0x5a, 0xe1, 0xc6, 0xee,
0x83, 0xc5, 0x28, 0x22, 0xf0, 0x94, 0x11, 0xe1,
@@ -7919,66 +7930,67 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 30941, .adv_w = 196, .box_w = 11, .box_h = 9, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 30986, .adv_w = 148, .box_w = 9, .box_h = 5, .ofs_x = 0, .ofs_y = 4},
{.bitmap_index = 31007, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31063, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31109, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31159, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31205, .adv_w = 132, .box_w = 9, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31247, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31320, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31395, .adv_w = 216, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31455, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31509, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31557, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31630, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31653, .adv_w = 144, .box_w = 9, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31688, .adv_w = 216, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31762, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31804, .adv_w = 132, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31854, .adv_w = 168, .box_w = 8, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 31891, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31937, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31964, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31981, .adv_w = 168, .box_w = 8, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 32019, .adv_w = 168, .box_w = 12, .box_h = 11, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 32066, .adv_w = 120, .box_w = 7, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32102, .adv_w = 120, .box_w = 7, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32139, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32178, .adv_w = 168, .box_w = 11, .box_h = 3, .ofs_x = 0, .ofs_y = 3},
{.bitmap_index = 32187, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 32249, .adv_w = 240, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32340, .adv_w = 216, .box_w = 15, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32415, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32474, .adv_w = 168, .box_w = 11, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 32508, .adv_w = 168, .box_w = 11, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 32542, .adv_w = 240, .box_w = 16, .box_h = 10, .ofs_x = -1, .ofs_y = 0},
{.bitmap_index = 32602, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 32622, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32679, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32743, .adv_w = 186, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32813, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32874, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32914, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32958, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32990, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33029, .adv_w = 120, .box_w = 9, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33075, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33120, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33173, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33229, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33292, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33315, .adv_w = 240, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33390, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33433, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33482, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33531, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33580, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33622, .adv_w = 240, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33687, .adv_w = 168, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33751, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33794, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33861, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33916, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33945, .adv_w = 193, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0}
{.bitmap_index = 31063, .adv_w = 216, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 31130, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31176, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 31226, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31272, .adv_w = 132, .box_w = 9, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 31314, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.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 = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32941, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32981, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33025, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33057, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33096, .adv_w = 120, .box_w = 9, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33142, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33187, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33240, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33296, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33359, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33382, .adv_w = 240, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33457, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33500, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33549, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33598, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33647, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33689, .adv_w = 240, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33754, .adv_w = 168, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33818, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33861, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33928, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33983, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34012, .adv_w = 193, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0}
};
/*---------------------
@@ -8002,14 +8014,14 @@ static const uint16_t unicode_list_10[] = {
};
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 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, 0xcfae,
0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xd006,
0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e,
0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
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,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -8065,7 +8077,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 = 63, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -8211,7 +8223,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, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -8351,7 +8364,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, 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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/fonts/fa.woff -r 61612,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 61612,61445,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
@@ -7257,6 +7257,19 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xbc, 0x44, 0x1, 0x8, 0x80, 0x2a, 0xfe, 0xd3,
0x1c, 0x42, 0x5d, 0x0, 0xf0, 0x80, 0x0,
/* U+F005 "" */
0x0, 0xf8, 0x84, 0x3, 0xff, 0x82, 0x3b, 0xa0,
0xf, 0xfe, 0xc, 0x81, 0xa0, 0x7, 0xfc, 0x2e,
0x0, 0x80, 0xf, 0xfa, 0x40, 0x23, 0x50, 0xe,
0x19, 0xbe, 0xf7, 0x0, 0xd5, 0xfb, 0x6e, 0x8,
0xc8, 0x20, 0x1f, 0x89, 0x20, 0x55, 0x80, 0x3f,
0xf8, 0x14, 0x21, 0x2e, 0x1, 0xfe, 0xa5, 0x0,
0xa1, 0x80, 0x3f, 0x52, 0x80, 0x77, 0x80, 0x7e,
0x70, 0xf, 0x78, 0x7, 0xe7, 0x0, 0xf2, 0x80,
0x42, 0x1, 0xb4, 0x3, 0xc4, 0x0, 0x8e, 0xd5,
0x0, 0x10, 0x7, 0x99, 0xf9, 0xc0, 0xab, 0x5b,
0xc0, 0x20,
/* U+F008 "" */
0x50, 0x18, 0x8f, 0xc8, 0xb, 0x55, 0x3a, 0xaf,
0x9e, 0x6a, 0x83, 0xd8, 0x19, 0xbb, 0xc6, 0xfc,
@@ -8889,66 +8902,67 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 37928, .adv_w = 229, .box_w = 13, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 37984, .adv_w = 173, .box_w = 10, .box_h = 5, .ofs_x = 0, .ofs_y = 5},
{.bitmap_index = 38007, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 38078, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38139, .adv_w = 224, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38199, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38260, .adv_w = 154, .box_w = 10, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38312, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38402, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38489, .adv_w = 252, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38569, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38634, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38697, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38792, .adv_w = 112, .box_w = 7, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38820, .adv_w = 168, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38866, .adv_w = 252, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38956, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 39012, .adv_w = 154, .box_w = 10, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39072, .adv_w = 196, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 39119, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39177, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39214, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39239, .adv_w = 196, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 39288, .adv_w = 196, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 39347, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39395, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39444, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39491, .adv_w = 196, .box_w = 13, .box_h = 4, .ofs_x = 0, .ofs_y = 3},
{.bitmap_index = 39505, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 39588, .adv_w = 280, .box_w = 18, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 39705, .adv_w = 252, .box_w = 17, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 39793, .adv_w = 224, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 39875, .adv_w = 196, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 39918, .adv_w = 196, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 39963, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40034, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40063, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40135, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40214, .adv_w = 217, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40307, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40388, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40436, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40492, .adv_w = 196, .box_w = 13, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40532, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40585, .adv_w = 140, .box_w = 10, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40641, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40704, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40770, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40843, .adv_w = 224, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40920, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40952, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41049, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41101, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41156, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41211, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41266, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41315, .adv_w = 280, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41395, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41479, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41535, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41612, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41690, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41726, .adv_w = 225, .box_w = 15, .box_h = 10, .ofs_x = 0, .ofs_y = 0}
{.bitmap_index = 38078, .adv_w = 252, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 38160, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38221, .adv_w = 224, .box_w = 14, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 38281, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38342, .adv_w = 154, .box_w = 10, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 38394, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.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 = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40470, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40518, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40574, .adv_w = 196, .box_w = 13, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40614, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40667, .adv_w = 140, .box_w = 10, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40723, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40786, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40852, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40925, .adv_w = 224, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41002, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41034, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41131, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41183, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41238, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41293, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41348, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41397, .adv_w = 280, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41477, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41561, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41617, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41694, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41772, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41808, .adv_w = 225, .box_w = 15, .box_h = 10, .ofs_x = 0, .ofs_y = 0}
};
/*---------------------
@@ -8972,14 +8986,14 @@ static const uint16_t unicode_list_10[] = {
};
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 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, 0xcfae,
0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xd006,
0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e,
0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
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,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -9035,7 +9049,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 = 63, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -9181,7 +9195,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, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -9321,7 +9336,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, 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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/fonts/fa.woff -r 61612,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 61612,61445,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
@@ -8308,6 +8308,21 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x0, 0x62, 0xbf, 0xf5, 0x97, 0xa0, 0x1, 0x3c,
0x3, 0xfc,
/* U+F005 "" */
0x0, 0xfd, 0x98, 0x0, 0xff, 0xe1, 0xa9, 0x99,
0x40, 0x3f, 0xf8, 0x50, 0x0, 0x80, 0xf, 0xfe,
0xa, 0x90, 0x0, 0x94, 0x3, 0xfc, 0x27, 0x0,
0x1a, 0xc, 0x40, 0x30, 0xce, 0x77, 0x30, 0x80,
0x31, 0x67, 0x73, 0x24, 0x55, 0x8c, 0x40, 0x3f,
0xc2, 0x6c, 0xa8, 0xc0, 0x1f, 0xfc, 0x26, 0x40,
0x96, 0x0, 0xff, 0xe0, 0x34, 0x80, 0x52, 0xe0,
0x1f, 0xe7, 0x90, 0xe, 0x82, 0x0, 0xfc, 0x50,
0x1, 0xf1, 0x0, 0x7e, 0x20, 0xf, 0x88, 0x3,
0xfc, 0x40, 0x1e, 0x60, 0xf, 0xf3, 0x0, 0x78,
0xc0, 0x27, 0xee, 0x38, 0x4, 0x60, 0x1e, 0x21,
0x6e, 0x81, 0x14, 0x73, 0x9, 0x0, 0x79, 0x7a,
0x44, 0x3, 0xc, 0xf2, 0x80, 0x40,
/* U+F008 "" */
0xd0, 0x7, 0xff, 0xf8, 0x1, 0xa5, 0xfe, 0x3,
0x77, 0xf1, 0x87, 0xf8, 0x83, 0xfc, 0x9, 0x11,
@@ -9993,66 +10008,67 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 46302, .adv_w = 262, .box_w = 15, .box_h = 12, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 46374, .adv_w = 198, .box_w = 12, .box_h = 7, .ofs_x = 0, .ofs_y = 5},
{.bitmap_index = 46410, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 46484, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 46548, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 46616, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 46689, .adv_w = 176, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 46753, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 46862, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 46964, .adv_w = 288, .box_w = 18, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47055, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47123, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 47186, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47290, .adv_w = 128, .box_w = 8, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47321, .adv_w = 192, .box_w = 12, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47372, .adv_w = 288, .box_w = 18, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47479, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 47546, .adv_w = 176, .box_w = 11, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 47613, .adv_w = 224, .box_w = 10, .box_h = 16, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 47665, .adv_w = 224, .box_w = 14, .box_h = 18, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 47737, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47766, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47786, .adv_w = 224, .box_w = 10, .box_h = 16, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 47838, .adv_w = 224, .box_w = 16, .box_h = 14, .ofs_x = -1, .ofs_y = -1},
{.bitmap_index = 47909, .adv_w = 160, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 47966, .adv_w = 160, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48023, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48068, .adv_w = 224, .box_w = 14, .box_h = 4, .ofs_x = 0, .ofs_y = 4},
{.bitmap_index = 48085, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 48184, .adv_w = 320, .box_w = 20, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48318, .adv_w = 288, .box_w = 20, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 48430, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48530, .adv_w = 224, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 48585, .adv_w = 224, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 48642, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 48730, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 48759, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48827, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 48921, .adv_w = 248, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49029, .adv_w = 224, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49120, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49167, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49219, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49274, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49333, .adv_w = 160, .box_w = 12, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49401, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49462, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49532, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49598, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 49687, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49720, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49833, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 49878, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 49922, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 49966, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50012, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50052, .adv_w = 320, .box_w = 21, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50153, .adv_w = 224, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 50249, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50304, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 50398, .adv_w = 320, .box_w = 20, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 50487, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50526, .adv_w = 258, .box_w = 17, .box_h = 11, .ofs_x = 0, .ofs_y = 1}
{.bitmap_index = 46484, .adv_w = 288, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 46586, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 46650, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 46718, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 46791, .adv_w = 176, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 46855, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.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 = 224, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49222, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49269, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49321, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49376, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49435, .adv_w = 160, .box_w = 12, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49503, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49564, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49634, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49700, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 49789, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49822, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49935, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 49980, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50024, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50068, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50114, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50154, .adv_w = 320, .box_w = 21, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50255, .adv_w = 224, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 50351, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50406, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 50500, .adv_w = 320, .box_w = 20, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 50589, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50628, .adv_w = 258, .box_w = 17, .box_h = 11, .ofs_x = 0, .ofs_y = 1}
};
/*---------------------
@@ -10076,14 +10092,14 @@ static const uint16_t unicode_list_10[] = {
};
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 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, 0xcfae,
0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xd006,
0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e,
0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
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,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -10139,7 +10155,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 = 63, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -10285,7 +10301,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, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -10425,7 +10442,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, 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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/fonts/fa.woff -r 61612,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 61612,61445,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
@@ -10406,6 +10406,25 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x1, 0xc4, 0xc0, 0x1f, 0x11, 0x0, 0x27, 0xd7,
0x55, 0x4e, 0x88, 0x7, 0xff, 0x0,
/* U+F005 "" */
0x0, 0xff, 0x1b, 0x0, 0x7f, 0xf1, 0xc7, 0x26,
0x0, 0x3f, 0xf8, 0xd2, 0x0, 0x71, 0x0, 0xff,
0xe2, 0xb, 0x80, 0x52, 0x1, 0xff, 0xc4, 0x90,
0xc, 0xe2, 0x1, 0xff, 0xc3, 0x70, 0xe, 0x60,
0xf, 0xf8, 0x95, 0xec, 0x3, 0xd1, 0x6, 0x41,
0x0, 0xd3, 0xbf, 0xb5, 0x8, 0x1, 0xf3, 0xcd,
0xf7, 0xe1, 0x93, 0x10, 0x7, 0xff, 0x14, 0xe4,
0x94, 0x3, 0xff, 0x8c, 0x32, 0x14, 0xc0, 0x1f,
0xfc, 0x41, 0xd3, 0x0, 0x4b, 0x0, 0x7f, 0xf0,
0x87, 0xc, 0x3, 0x4b, 0x80, 0x7f, 0xf0, 0x7,
0x8, 0x3, 0xd0, 0x60, 0x1f, 0xf5, 0x10, 0x7,
0xe2, 0x0, 0xff, 0x88, 0x3, 0xf8, 0x40, 0x3f,
0xee, 0x0, 0xfc, 0x60, 0x1f, 0xfc, 0x3, 0x0,
0xfc, 0xa0, 0x1e, 0x20, 0xe, 0x50, 0xf, 0xde,
0x1, 0x9b, 0xb6, 0x88, 0x2, 0x11, 0x0, 0x7f,
0x97, 0x64, 0x41, 0x76, 0x88, 0x3, 0xfd, 0x10,
0xda, 0x20, 0xe, 0x5d, 0xab, 0x10, 0x8,
/* U+F008 "" */
0xc4, 0x3, 0xff, 0xff, 0xe0, 0x20, 0x26, 0x1c,
0xe7, 0x0, 0x4, 0x7f, 0x0, 0x27, 0x24, 0xc1,
@@ -12334,66 +12353,67 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 62808, .adv_w = 327, .box_w = 19, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 62909, .adv_w = 247, .box_w = 14, .box_h = 8, .ofs_x = 0, .ofs_y = 7},
{.bitmap_index = 62952, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 63062, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 63174, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 63261, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 63361, .adv_w = 220, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 63452, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 63611, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 63764, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 63907, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64011, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64108, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64263, .adv_w = 160, .box_w = 10, .box_h = 16, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64301, .adv_w = 240, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 64375, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 64529, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 64624, .adv_w = 220, .box_w = 14, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64727, .adv_w = 280, .box_w = 13, .box_h = 19, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 64795, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 64897, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 64954, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 64995, .adv_w = 280, .box_w = 13, .box_h = 19, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 65069, .adv_w = 280, .box_w = 19, .box_h = 19, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 65168, .adv_w = 200, .box_w = 11, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 65253, .adv_w = 200, .box_w = 11, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 65334, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 65406, .adv_w = 280, .box_w = 18, .box_h = 5, .ofs_x = 0, .ofs_y = 5},
{.bitmap_index = 65428, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 65578, .adv_w = 400, .box_w = 26, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 65781, .adv_w = 360, .box_w = 24, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 65934, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 66076, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 2},
{.bitmap_index = 66152, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 2},
{.bitmap_index = 66230, .adv_w = 400, .box_w = 26, .box_h = 16, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 66348, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 66392, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66502, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66640, .adv_w = 310, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66799, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 66947, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67020, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67113, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 67173, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67263, .adv_w = 200, .box_w = 14, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67356, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67444, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67555, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67681, .adv_w = 320, .box_w = 22, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67802, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67850, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68014, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68081, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 63062, .adv_w = 360, .box_w = 22, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 63197, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 63309, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 63396, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 63496, .adv_w = 220, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 63587, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.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 = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67082, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67155, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67248, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 67308, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67398, .adv_w = 200, .box_w = 14, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67491, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67579, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67690, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67816, .adv_w = 320, .box_w = 22, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67937, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67985, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68149, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68216, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68283, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68343, .adv_w = 400, .box_w = 26, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 68477, .adv_w = 280, .box_w = 16, .box_h = 21, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 68624, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68711, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 68851, .adv_w = 400, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 68971, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69030, .adv_w = 322, .box_w = 21, .box_h = 13, .ofs_x = 0, .ofs_y = 1}
{.bitmap_index = 68284, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68351, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68418, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68478, .adv_w = 400, .box_w = 26, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 68612, .adv_w = 280, .box_w = 16, .box_h = 21, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 68759, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68846, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 68986, .adv_w = 400, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 69106, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69165, .adv_w = 322, .box_w = 21, .box_h = 13, .ofs_x = 0, .ofs_y = 1}
};
/*---------------------
@@ -12417,14 +12437,14 @@ static const uint16_t unicode_list_10[] = {
};
static const uint16_t unicode_list_12[] = {
0x0, 0xc, 0xceeb, 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, 0xcfae,
0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xd006,
0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xd12e,
0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
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,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -12480,7 +12500,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 = 63, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
@@ -12626,7 +12646,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, 0, 0, 0, 0,
0
};
/*Map glyph_ids to kern right classes*/
@@ -12766,7 +12787,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, 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 /private/tmp/claude-501/-Users-jakub-Documents-MeshCore-MeshCore/3d84fa48-e031-42a4-abec-01b33a2b06ef/scratchpad/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,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
+15 -300
View File
@@ -10,18 +10,12 @@
// Filter (type) and sort are independent axes and combine freely. The action
// menu (Hold Enter) is identical everywhere; only the per-row column and the
// detail fields differ between sources.
class NearbyScreen : public UIScreen {
class NearbyScreen : public UIScreen, protected NearbyModel {
UITask* _task;
// ── filter (type axis) ──────────────────────────────────────────────────────
enum Filter : uint8_t { F_ALL, F_FAV, F_COMP, F_RPT, F_ROOM, F_SNSR, F_COUNT };
static const char* FILTER_LABELS[F_COUNT];
// ── sort axis ───────────────────────────────────────────────────────────────
enum Sort : uint8_t { SORT_DIST, SORT_TIME };
// ── source ──────────────────────────────────────────────────────────────────
enum Source : uint8_t { SRC_STORED, SRC_SCAN };
// List data, filter / sort / source and the refresh logic live in
// ui-core/NearbyModel.h (shared with ui-lvgl); this class adds selection,
// detail / navigate views, scan timing and the action menu.
// ── action-menu actions (matched by id, not by row index) ────────────────────
enum Action : uint8_t { ACT_NAV, ACT_PING, ACT_WAYPOINT, ACT_LOCATOR,
@@ -36,52 +30,11 @@ class NearbyScreen : public UIScreen {
// startPickBotChannel()/startPickBotRoom() pick-mode idiom.
bool _pick_admin_target = false;
// ── unified list entry ───────────────────────────────────────────────────────
struct Entry {
char name[32];
uint8_t type;
uint8_t pub_key[PUB_KEY_SIZE];
// has_key: the full 32-byte pubkey is present -- what Ping and the base64
// key view need. has_prefix: at least the leading FAVOURITE_PREFIX_LEN
// bytes are, which is all an identity-keyed reference needs (a Locator
// person target, resolved via UITask::resolvePersonPos). Every has_key row
// also has_prefix; the reverse doesn't hold -- a [LOC] share and a heard
// advert carry a prefix and nothing more.
bool has_key;
bool has_prefix;
// stored-source fields
int32_t lat_e6, lon_e6;
float dist_km;
uint32_t lastmod;
int contact_idx;
// scan-source fields
int8_t rssi, snr_x4, remote_snr_x4;
bool is_known;
// live-track ([LOC] share) overlay: this row's position/age came from a
// shared-location message. live_verified == true for a DM (pubkey) share,
// false for a channel (name, best-effort) share.
bool is_live;
bool live_verified;
// Favourite (ContactInfo::flags bit 0) -- always false for a scan/live row
// that isn't a contact, since only a contact can carry the flag.
bool fav;
};
static const int MAX_NEARBY = 32;
Entry _entries[MAX_NEARBY];
int _count;
int _sel;
int _scroll;
bool _detail;
bool _nav = false; // full-screen navigate-to-node view (over detail)
navview::EtaTracker _nav_eta; // closing-speed/ETA for the navigate view
int32_t _own_lat, _own_lon;
bool _own_gps;
Source _source;
uint8_t _filter;
uint8_t _sort;
unsigned long _detail_refresh_ms;
unsigned long _list_refresh_ms = 0;
static const unsigned long DETAIL_REFRESH_MS = 10000UL;
@@ -137,90 +90,15 @@ class NearbyScreen : public UIScreen {
snprintf(buf, n, "%s ago", s);
}
static const char* typeName(uint8_t t) {
switch (t) {
case ADV_TYPE_CHAT: return "Companion";
case ADV_TYPE_REPEATER: return "Repeater";
case ADV_TYPE_ROOM: return "Room";
case ADV_TYPE_SENSOR: return "Sensor";
default: return "Unknown";
}
}
static const char* typeShort(uint8_t t) {
switch (t) {
case ADV_TYPE_REPEATER: return "Rpt";
case ADV_TYPE_SENSOR: return "Snsr";
case ADV_TYPE_ROOM: return "Room";
case ADV_TYPE_CHAT: return "Comp";
default: return "?";
}
}
// The selected entry, or nullptr when the list is empty.
const Entry* selected() const {
return (_count > 0 && _sel < _count) ? &_entries[_sel] : nullptr;
}
bool typeMatchesFilter(uint8_t type, uint8_t flags, bool have_flags) const {
switch (_filter) {
case F_FAV: return have_flags && (flags & 0x01);
case F_COMP: return type == ADV_TYPE_CHAT;
case F_RPT: return type == ADV_TYPE_REPEATER;
case F_ROOM: return type == ADV_TYPE_ROOM;
case F_SNSR: return type == ADV_TYPE_SENSOR;
case F_ALL:
default: return true;
}
}
// ── data refresh ──────────────────────────────────────────────────────────────
void refreshStored() {
_count = 0;
_own_lat = _own_lon = 0;
_own_gps = _task->currentLocation(_own_lat, _own_lon);
int nc = the_mesh.getNumContacts();
for (int i = 0; i < nc && _count < MAX_NEARBY; i++) {
ContactInfo ci;
// getContactByIdx() indexes the RAW contact table, whose first
// MAX_ANON_CONTACTS slots are reserved for anon-request bookkeeping
// (see BaseChatMesh::resetContacts()/ContactsIterator) -- getNumContacts()
// already excludes them from the count, so real contact 0 lives at raw
// index MAX_ANON_CONTACTS, not 0. Reading from 0 pulled those reserved
// (blank, type=ADV_TYPE_NONE) slots into the list as bogus "Unknown"
// rows, and silently dropped the same number of real contacts off the
// end -- while never touching the anon slots themselves, so it always
// reproduced the same way regardless of the auto-add overwrite setting.
if (!the_mesh.getContactByIdx(i + MAX_ANON_CONTACTS, ci)) continue;
if (!typeMatchesFilter(ci.type, ci.flags, true)) continue;
Entry& e = _entries[_count++];
strncpy(e.name, ci.name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
memcpy(e.pub_key, ci.id.pub_key, PUB_KEY_SIZE);
e.has_key = true;
e.has_prefix = true;
e.lat_e6 = ci.gps_lat;
e.lon_e6 = ci.gps_lon;
bool remote_gps = (ci.gps_lat != 0 || ci.gps_lon != 0);
e.dist_km = (_own_gps && remote_gps)
? geo::haversineKm(_own_lat, _own_lon, ci.gps_lat, ci.gps_lon)
: -1.0f;
e.type = ci.type;
e.fav = (ci.flags & 0x01) != 0;
e.contact_idx = i + MAX_ANON_CONTACTS; // raw index -- other lookups re-key off this directly
e.lastmod = ci.lastmod;
e.is_known = true;
e.is_live = false;
e.live_verified = false;
}
mergeLiveTrack();
mergeRecentlyHeard();
sortStored();
clampSelection();
}
// ── data refresh (NearbyModel) + selection clamping ─────────────────────────
void refreshStored() { NearbyModel::refreshStored(); clampSelection(); }
void refreshScan() { NearbyModel::refreshScan(); clampSelection(); }
void refresh() { refreshModel(); clampSelection(); }
// Rebuild the stored list (re-merge live shares + re-sort) while keeping the
// currently highlighted node selected across the rebuild — by contact index
@@ -236,173 +114,12 @@ class NearbyScreen : public UIScreen {
saved_name[sizeof(saved_name) - 1] = '\0';
}
refreshStored();
if (saved_idx >= 0) {
for (int i = 0; i < _count; i++)
if (_entries[i].contact_idx == saved_idx) { _sel = i; return true; }
} else if (saved_live && saved_name[0]) {
for (int i = 0; i < _count; i++)
if (_entries[i].is_live && strncmp(_entries[i].name, saved_name, sizeof(saved_name) - 1) == 0)
{ _sel = i; return true; }
}
int i = saved_idx >= 0 ? findContact(saved_idx)
: (saved_live && saved_name[0]) ? findLiveByName(saved_name) : -1;
if (i >= 0) { _sel = i; return true; }
return false;
}
// Overlay live [LOC] shares onto the stored list: refresh a matching contact
// with the fresher shared position, and append senders who aren't contacts so
// a group sharing on a channel still shows up. DM shares match by pubkey
// prefix (verified); channel shares match by name (best-effort).
void mergeLiveTrack() {
if (!_task) return;
LiveTrackStore& lt = _task->liveTrack();
uint32_t now = rtc_clock.getCurrentTime();
for (int i = 0; i < LiveTrackStore::CAPACITY; i++) {
if (!lt.isActive(i, now)) continue;
const LiveTrackStore::Entry& s = lt.slotAt(i);
int m = -1;
for (int j = 0; j < _count; j++) {
if (s.verified && _entries[j].has_key
&& memcmp(_entries[j].pub_key, s.key, LiveTrackStore::KEY_LEN) == 0) { m = j; break; }
if (!s.verified && strncmp(_entries[j].name, s.name, sizeof(_entries[j].name) - 1) == 0) { m = j; break; }
}
if (m >= 0) {
Entry& e = _entries[m];
// A [LOC] share is an explicit "here I am now", so it defines the pin and
// the distance (used for proximity sorting). Recency for the time sort is
// the most recent of the advert and the share.
e.lat_e6 = s.lat_1e6;
e.lon_e6 = s.lon_1e6;
e.dist_km = _own_gps ? geo::haversineKm(_own_lat, _own_lon, s.lat_1e6, s.lon_1e6) : -1.0f;
if (s.ts > e.lastmod) e.lastmod = s.ts;
e.is_live = true;
e.live_verified = s.verified;
} else if (_count < MAX_NEARBY && typeMatchesFilter(ADV_TYPE_CHAT, 0, false)) {
// A sender we don't have as a contact: treat it as a companion (the only
// node type that shares position), so the type filter still applies —
// e.g. it must not appear under the Repeater/Room/Sensor filters.
Entry& e = _entries[_count++];
memset(&e, 0, sizeof(e));
strncpy(e.name, s.name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
// We only keep a key *prefix* for shares, not the full pubkey, so Ping
// and the base64 key view (which need 32 bytes) stay unavailable for a
// non-contact live entry. Navigate / Save-waypoint work off lat/lon.
// A DM share is pubkey-keyed, so the prefix is real and good enough to
// keep re-resolving them as a Locator person target; a channel share is
// matched by name and carries no identity at all.
e.has_key = false;
e.has_prefix = s.verified;
if (s.verified) memcpy(e.pub_key, s.key, LiveTrackStore::KEY_LEN);
e.type = ADV_TYPE_CHAT;
e.lat_e6 = s.lat_1e6;
e.lon_e6 = s.lon_1e6;
e.dist_km = _own_gps ? geo::haversineKm(_own_lat, _own_lon, s.lat_1e6, s.lon_1e6) : -1.0f;
e.lastmod = s.ts;
e.contact_idx = -1;
e.is_known = false;
e.is_live = true;
e.live_verified = s.verified;
}
}
}
// Fold passively-heard adverts that aren't already listed (contact or live) into
// the list as name+age rows — no full key/GPS, so informational only. This is
// what absorbs the old standalone "Recent adverts" home page. Gated to the All
// filter because AdvertPath carries no node type to filter or sort-by-dist on.
void mergeRecentlyHeard() {
if (_filter != F_ALL || !_task) return;
static AdvertPath heard[8]; // scratch — refreshStored isn't reentrant
int n = the_mesh.getRecentlyHeard(heard, 8);
for (int i = 0; i < n && _count < MAX_NEARBY; i++) {
AdvertPath& a = heard[i];
if (a.name[0] == 0) continue;
bool dup = false;
for (int j = 0; j < _count; j++) {
if (_entries[j].has_key &&
memcmp(_entries[j].pub_key, a.pubkey_prefix, sizeof(a.pubkey_prefix)) == 0) { dup = true; break; }
if (strncmp(_entries[j].name, a.name, sizeof(a.name) - 1) == 0) { dup = true; break; }
}
if (dup) continue;
Entry& e = _entries[_count++];
memset(&e, 0, sizeof(e));
strncpy(e.name, a.name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
e.type = ADV_TYPE_CHAT; // unknown from AdvertPath — best-effort label
e.has_key = false;
e.has_prefix = true;
memcpy(e.pub_key, a.pubkey_prefix, sizeof(a.pubkey_prefix));
e.dist_km = -1.0f;
e.lastmod = a.recv_timestamp;
e.contact_idx = -1;
e.is_known = false;
e.is_live = false;
}
}
void sortStored() {
uint32_t now_ts = rtc_clock.getCurrentTime();
NodePrefs* p = _task->getNodePrefs();
const bool fav_first = !(p && p->fav_sort_off);
for (int i = 0; i < _count - 1; i++) {
int best = i;
for (int j = i + 1; j < _count; j++) {
if (fav_first && _entries[j].fav != _entries[best].fav) {
if (_entries[j].fav) best = j; // favourites outrank the time/distance key
continue;
}
if (_sort == SORT_TIME) {
// lastmod=0 or lastmod>now (RTC not synced) → "unknown" → sort to bottom.
uint32_t tj = (_entries[j].lastmod > 0 && now_ts >= _entries[j].lastmod) ? _entries[j].lastmod : 0;
uint32_t tb = (_entries[best].lastmod > 0 && now_ts >= _entries[best].lastmod) ? _entries[best].lastmod : 0;
if (tj > 0 && (tb == 0 || tj > tb)) best = j; // descending — most recent first
} else {
float dj = _entries[j].dist_km, db = _entries[best].dist_km;
if (dj >= 0.0f && (db < 0.0f || dj < db)) best = j; // ascending — closest first
}
}
if (best != i) { Entry tmp = _entries[i]; _entries[i] = _entries[best]; _entries[best] = tmp; }
}
}
void refreshScan() {
static DiscoverResult dr[DISCOVER_RESULTS_MAX]; // scratch — refresh isn't reentrant
int n = the_mesh.getDiscoverResults(dr, DISCOVER_RESULTS_MAX);
_count = 0;
for (int i = 0; i < n && _count < MAX_NEARBY; i++) {
if (!typeMatchesFilter(dr[i].type, 0, false)) continue;
Entry& e = _entries[_count++];
strncpy(e.name, dr[i].name, sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = '\0';
e.type = dr[i].type;
memcpy(e.pub_key, dr[i].pub_key, PUB_KEY_SIZE);
e.has_key = true;
e.has_prefix = true;
e.rssi = dr[i].rssi;
e.snr_x4 = dr[i].snr_x4;
e.remote_snr_x4 = dr[i].remote_snr_x4;
e.is_known = dr[i].is_known;
e.fav = false;
e.lat_e6 = e.lon_e6 = 0;
e.dist_km = -1.0f;
e.lastmod = 0;
e.contact_idx = -1;
e.is_live = false;
e.live_verified = false;
}
// strongest first
for (int i = 0; i < _count - 1; i++) {
int best = i;
for (int j = i + 1; j < _count; j++)
if (_entries[j].rssi > _entries[best].rssi) best = j;
if (best != i) { Entry tmp = _entries[i]; _entries[i] = _entries[best]; _entries[best] = tmp; }
}
clampSelection();
}
void refresh() { if (_source == SRC_SCAN) refreshScan(); else refreshStored(); }
void clampSelection() {
if (_count == 0) { _sel = _scroll = 0; }
else if (_sel >= _count) { _sel = _count - 1; if (_scroll > _sel) _scroll = _sel; }
@@ -804,17 +521,16 @@ class NearbyScreen : public UIScreen {
// side and wrap around (the tab before "All" is "Snsr", and vice-versa),
// matching the wrap-around LEFT/RIGHT cycle.
void drawFilterTabs(DisplayDriver& display) {
tabbar::draw(display, FILTER_LABELS, F_COUNT, _filter, display.menuHintWidth());
tabbar::draw(display, filterLabels(), F_COUNT, _filter, display.menuHintWidth());
display.drawContextMenuHint(DisplayDriver::LIGHT, ctxMenuOpen()); // Nodes list has a Hold-Enter menu
}
public:
NearbyScreen(UITask* task)
: _task(task), _count(0), _sel(0), _scroll(0), _detail(false),
_own_lat(0), _own_lon(0), _own_gps(false),
_source(SRC_STORED), _filter(F_ALL), _sort(SORT_DIST),
: _task(task), _sel(0), _scroll(0), _detail(false),
_detail_refresh_ms(0), _scanning(false), _scan_started_ms(0),
_menu_action_count(0), _pinging(false), _ping_started_ms(0) {
bindModel(&task->core(), task->getNodePrefs());
resetPingLines();
_sort_label[0] = '\0';
}
@@ -895,7 +611,7 @@ public:
int dist_col = display.width() - display.getCharWidth() * 7;
display.setColor(DisplayDriver::LIGHT);
const char* flt = (_filter != F_ALL) ? FILTER_LABELS[_filter] : nullptr;
const char* flt = (_filter != F_ALL) ? filterLabel(_filter) : nullptr;
if (_source == SRC_SCAN) {
char title[28];
const char* base = _scanning ? "SCANNING" : "SCAN";
@@ -1056,4 +772,3 @@ public:
}
};
const char* NearbyScreen::FILTER_LABELS[F_COUNT] = { "All", "Fav", "Comp", "Rpt", "Room", "Snsr" };
@@ -167,6 +167,7 @@ static const int QUICK_MSGS_MAX = 10;
#include "RingtoneEditorScreen.h"
#include "BotScreen.h"
#include "AdminScreen.h"
#include "../ui-core/NearbyModel.h"
#include "NearbyScreen.h"
#include "DashboardConfigScreen.h"
#include "AutoAdvertScreen.h"
+23 -9
View File
@@ -1,11 +1,13 @@
<!DOCTYPE html>
<!--
ui-lvgl simulator: the Wio Tracker L2 frontend (320x240 touch LCD) running
the real companion firmware in the browser, plus one ui-new peer device on a
direct simulated link so there is someone to talk to.
the real companion firmware in the browser, plus one ui-new peer device and
one repeater on a shared simulated link, so there is someone to talk to and
something for Nearby > Scan to find.
Build both, then serve this directory (http://, not file://):
variants/sim/build_wasm.sh # peer -> build/meshcore_sim.js
variants/sim/build_wasm_repeater.sh # repeater (optional; page runs without it)
SIM_UI=lvgl variants/sim/build_wasm.sh # L2 -> build/meshcore_sim_lvgl.js
cd variants/sim/web && python3 -m http.server 8080
and open http://localhost:8080/lvgl.html .
@@ -74,7 +76,7 @@
<div class="row">
<button id="peer-dm">Peer → DM to L2</button>
<button id="peer-ch">Peer → Public</button>
<button id="advert">Advert both</button>
<button id="advert">Advert all</button>
</div>
<div id="log"></div>
</div>
@@ -82,6 +84,7 @@
<script src="build/meshcore_sim.js"></script>
<script src="build/meshcore_sim_lvgl.js"></script>
<script src="build/repeater/meshcore_sim_repeater.js" onerror="window.MeshCoreSimRepeater = null"></script>
<script>
const logEl = document.getElementById('log');
function log(msg) {
@@ -116,13 +119,14 @@
};
}
let L = null, P = null;
let L = null, P = null, R = null;
// Direct link L <-> P, 50 ms air time.
// Everyone hears everyone, 50 ms air time.
setInterval(() => {
if (!L || !P) return;
for (const [from, to] of [[L, P], [P, L]]) {
for (const pkt of from.pollTx()) setTimeout(() => to.injectRx(pkt), 50);
const nodes = [L, P, R].filter(Boolean);
for (const from of nodes) {
for (const pkt of from.pollTx())
for (const to of nodes) if (to !== from) setTimeout(() => to.injectRx(pkt), 50);
}
}, 40);
@@ -158,10 +162,19 @@
return ok;
}
async function bootR() {
if (!window.MeshCoreSimRepeater) { log('repeater build not found (optional)'); return false; }
R = wrap(await MeshCoreSimRepeater({ simInstanceTag: 'R' }), 'R');
const ok = await waitReady(R);
log(ok ? 'repeater up' : 'repeater boot timed out');
return ok;
}
function advertBoth() {
L.call('sim_test_advert_flood', 'number');
setTimeout(() => P.call('sim_test_advert_flood', 'number'), 300);
log('advert: both');
if (R) setTimeout(() => R.call('sim_test_advert_flood', 'number'), 600);
log('advert: all');
}
// ── Touch: mouse / pen / finger on the LCD canvas ─────────────────────
@@ -202,6 +215,7 @@
});
const ready = (async () => {
await bootR();
const [okP, okL] = [await bootP(), await bootL()];
if (okP && okL) { advertBoth(); log('both devices up'); }
return okP && okL;