From c7c150da58d701c3244627ce5006692878251339 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Sat, 26 Sep 2026 22:54:13 +0200 Subject: [PATCH] fix(ui-lvgl): map download popup fits the screen A progress bar and one line of counts while downloading (the zoom row hides, it can't change then), failures on one line, the source as "opentopomap.org", only the WiFi network's name (set up in Settings > WiFi). The debug line (IP, RSSI, heap) is gone. Roadmap: mentions highlight added, four items ticked. Co-Authored-By: Claude Opus 5.5 --- docs/development/l2-roadmap.md | 16 ++-- examples/companion_radio/ui-lvgl/LvglPort.h | 12 --- examples/companion_radio/ui-lvgl/MapScreen.h | 86 ++++++++++++++------ examples/companion_radio/ui-lvgl/UITask.h | 2 + 4 files changed, 73 insertions(+), 43 deletions(-) diff --git a/docs/development/l2-roadmap.md b/docs/development/l2-roadmap.md index 4b24a359..0134e54a 100644 --- a/docs/development/l2-roadmap.md +++ b/docs/development/l2-roadmap.md @@ -235,12 +235,18 @@ Ideas to come back to (2026-09-26): User list, second batch (2026-09-26): -- [ ] Status bar icons look like different sizes? -- [ ] Under own messages: a small counter as in the original (L1), not a +- [x] Status bar icons look like different sizes? Own icon font + (fonts/status_icons.py refits FontAwesome to one size); scrollbars + everywhere thin and at the edge, clear of the content. +- [x] Under own messages: a small counter as in the original (L1), not a checkmark with "relayed". -- [ ] Polish the path popup of a sent / received message. -- [ ] Map download popup runs off the screen; take the WiFi settings out - of it (one place: Settings > WiFi). +- [x] Polish the path popup of a sent / received message (roles on the + right, our post's repeaters under us, long paths scroll). +- [x] Map download popup runs off the screen; take the WiFi settings out + of it (one place: Settings > WiFi). Compact: a progress bar, one + line of counts, errors on one line, only the network's name. +- [ ] Mentions: highlight @[nick] in message text (a coloured chip in + place of the raw "@[...]" markup), our own name stronger. - [ ] Splash: "MeshCore Solo" with the upstream version and ours, all in the MeshCore title font. - [ ] Emoji, ideally the full range (glyphs could live on the SD card). diff --git a/examples/companion_radio/ui-lvgl/LvglPort.h b/examples/companion_radio/ui-lvgl/LvglPort.h index e0ddceea..eead73c9 100644 --- a/examples/companion_radio/ui-lvgl/LvglPort.h +++ b/examples/companion_radio/ui-lvgl/LvglPort.h @@ -303,17 +303,6 @@ static void netEnd() { } // One line for the download popup: link details and the internal heap (TLS, // WiFi and BLE all compete for it). -static void netInfo(char* out, size_t n) { - size_t free_int = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); - size_t big_int = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); - if (WiFi.status() == WL_CONNECTED) - snprintf(out, n, "%s %d dBm heap %uk/%uk", WiFi.localIP().toString().c_str(), (int)WiFi.RSSI(), - (unsigned)(free_int / 1024), (unsigned)(big_int / 1024)); - else - snprintf(out, n, "WiFi status %d heap %uk/%uk", (int)WiFi.status(), - (unsigned)(free_int / 1024), (unsigned)(big_int / 1024)); -} - // Async scan: scanStart(), then scanResults() returns -1 while running, else // the count, filling `names` (strongest first, as the driver reports them). static void scanStart() { @@ -541,7 +530,6 @@ static void setLiveTiles(bool on) { s_live_tiles = on; } static void netBegin(const char*, const char*) {} static int netState() { return NET_UP; } static void netEnd() {} -static void netInfo(char* out, size_t n) { snprintf(out, n, "browser network"); } static uint32_t s_scan_at = 0; static void scanStart() { s_scan_at = millis(); } static int scanResults(char names[][33], int max) { // a pretend scan, for the UI diff --git a/examples/companion_radio/ui-lvgl/MapScreen.h b/examples/companion_radio/ui-lvgl/MapScreen.h index 5038678f..41cf2e17 100644 --- a/examples/companion_radio/ui-lvgl/MapScreen.h +++ b/examples/companion_radio/ui-lvgl/MapScreen.h @@ -600,14 +600,14 @@ void UITask::mapDownloadPopup() { lv_obj_set_style_border_width(panel, 1, 0); lv_obj_set_style_radius(panel, theme::RADIUS, 0); lv_obj_set_style_pad_all(panel, theme::PAD, 0); - lv_obj_set_style_pad_row(panel, 6, 0); + lv_obj_set_style_pad_row(panel, 4, 0); lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN); lv_obj_set_style_max_height(panel, lv_display_get_vertical_resolution(NULL) - theme::STATUS_H - 12, 0); // scrolls past that 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_set_size(hdr, LV_PCT(100), 26); lv_obj_align(label(hdr, "Download this area", THEME_FONT_TITLE, theme::TEXT), LV_ALIGN_LEFT_MID, 0, 0); headerButton(hdr, LV_SYMBOL_CLOSE, onDlClose, 0, NULL); @@ -640,6 +640,15 @@ void UITask::mapDownloadPopup() { lv_label_set_long_mode(_dl_info, LV_LABEL_LONG_WRAP); lv_obj_set_width(_dl_info, LV_PCT(100)); lv_obj_set_style_text_line_space(_dl_info, 2, 0); + _dl_bar = lv_bar_create(panel); + lv_obj_set_size(_dl_bar, LV_PCT(100), 6); + lv_obj_set_style_bg_color(_dl_bar, lv_color_hex(theme::SURFACE_2), 0); + lv_obj_set_style_bg_color(_dl_bar, lv_color_hex(theme::ACCENT), LV_PART_INDICATOR); + lv_obj_add_flag(_dl_bar, LV_OBJ_FLAG_HIDDEN); + _dl_err = label(panel, "", THEME_FONT_SMALL, theme::FAIL); + lv_label_set_long_mode(_dl_err, LV_LABEL_LONG_DOT); // one line, whatever the server said + lv_obj_set_width(_dl_err, LV_PCT(100)); + lv_obj_add_flag(_dl_err, LV_OBJ_FLAG_HIDDEN); lv_obj_t* acts = lv_obj_create(panel); styleSurface(acts, theme::BG); @@ -663,7 +672,7 @@ void UITask::mapDownloadPopup() { void UITask::mapDownloadClose() { if (_dl_overlay) lv_obj_delete_async(_dl_overlay); // may be closing from its own button - _dl_overlay = _dl_info = _dl_zoom_lbl = _dl_start_lbl = _dl_job_row = _dl_job_lbl = nullptr; + _dl_overlay = _dl_info = _dl_bar = _dl_err = _dl_zoom_lbl = _dl_start_lbl = _dl_job_row = _dl_job_lbl = nullptr; } void UITask::mapDownloadZmax(int delta) { @@ -674,27 +683,52 @@ void UITask::mapDownloadZmax(int delta) { refreshDownloadPopup(); } +// "a.tile.opentopomap.org" -> "opentopomap.org": the name people know. +static const char* shortHost(const char* h) { + const char* p = h; + for (;;) { + const char* dot = strchr(p, '.'); + if (!dot || !strchr(dot + 1, '.')) return p; // two labels left + p = dot + 1; + } +} + +static void dlSize(char* out, size_t n, uint64_t bytes) { + if (bytes < 1024 * 1024) snprintf(out, n, "%lu KB", (unsigned long)(bytes / 1024)); + else snprintf(out, n, "%lu MB", (unsigned long)((bytes + 512 * 1024) / (1024 * 1024))); +} + void UITask::refreshDownloadPopup() { if (!_dl_info) return; mapview::TileDownloader& dl = mapview::s_dl; lv_label_set_text_fmt(_dl_zoom_lbl, "z%d", _dl_zmax); - if (dl.active()) { - char net[64]; - lvport::netInfo(net, sizeof(net)); - if (dl.state() == mapview::TileDownloader::CONNECTING) - lv_label_set_text_fmt(_dl_info, "Connecting to WiFi...\nFrom %s\n%s", dl.sourceHost(), net); - else if (dl.failed()) - lv_label_set_text_fmt(_dl_info, "Downloading %lu / %lu tiles - %lu failed\nLast error: %s\n%s", - (unsigned long)dl.processed(), (unsigned long)dl.total(), - (unsigned long)dl.failed(), dl.message(), net); - else - lv_label_set_text_fmt(_dl_info, "Downloading %lu / %lu tiles (%lu new)\nFrom %s - you can close this\n%s", - (unsigned long)dl.processed(), (unsigned long)dl.total(), - (unsigned long)dl.downloaded(), dl.sourceHost(), net); - lv_label_set_text(_dl_start_lbl, LV_SYMBOL_STOP " Stop"); + const char* host = shortHost(dl.sourceHost()); + lv_obj_t* zoom_row = lv_obj_get_parent(_dl_zoom_lbl); + if (dl.active()) { // the range is fixed while it runs: room for the progress instead + lv_obj_add_flag(zoom_row, LV_OBJ_FLAG_HIDDEN); if (_dl_job_row) lv_obj_add_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(_dl_bar, LV_OBJ_FLAG_HIDDEN); + uint32_t total = dl.total(); + lv_bar_set_range(_dl_bar, 0, total > 0 ? (int32_t)total : 1); + lv_bar_set_value(_dl_bar, (int32_t)dl.processed(), LV_ANIM_OFF); + if (dl.state() == mapview::TileDownloader::CONNECTING) + lv_label_set_text_fmt(_dl_info, "Connecting to WiFi... - from %s", host); + else + lv_label_set_text_fmt(_dl_info, "%lu / %lu tiles, %lu new - from %s\nYou can close this, it carries on", + (unsigned long)dl.processed(), (unsigned long)total, + (unsigned long)dl.downloaded(), host); + if (dl.failed()) { + lv_label_set_text_fmt(_dl_err, "%lu failed: %s", (unsigned long)dl.failed(), dl.message()); + lv_obj_remove_flag(_dl_err, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(_dl_err, LV_OBJ_FLAG_HIDDEN); + } + lv_label_set_text(_dl_start_lbl, LV_SYMBOL_STOP " Stop"); return; } + lv_obj_remove_flag(zoom_row, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(_dl_bar, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(_dl_err, LV_OBJ_FLAG_HIDDEN); mapview::TileArea job; if (_dl_job_row && dl.savedJob(job)) { lv_label_set_text_fmt(_dl_job_lbl, "Unfinished: z%d-%d,\n%lu tiles", job.zmin, job.zmax, @@ -706,20 +740,20 @@ void UITask::refreshDownloadPopup() { int w = _map_area ? lv_obj_get_width(_map_area) : 320, h = _map_area ? lv_obj_get_height(_map_area) : 218; mapview::TileArea a = visibleArea(_map_cx, _map_cy, _map_z, w, h, dlZmin(_map_z), _dl_zmax); uint32_t n = mapview::countTiles(a); - char ssid[33], pass[65]; - bool have_wifi = lvport::loadWifi(ssid, sizeof(ssid), pass, sizeof(pass)); + char size[16]; + dlSize(size, sizeof(size), (uint64_t)n * mapview::AVG_TILE_BYTES); + // The network itself is set up in Settings > WiFi only; here just which one. + char ssid[33], pass[65], net[64]; + if (!lvport::wifiAllowed()) snprintf(net, sizeof(net), "WiFi is off - Settings > WiFi"); + else if (!lvport::loadWifi(ssid, sizeof(ssid), pass, sizeof(pass))) snprintf(net, sizeof(net), "No WiFi network - Settings > WiFi"); + else snprintf(net, sizeof(net), "Over WiFi: %s", ssid); char last[64] = ""; bool job_shown = _dl_job_row && !lv_obj_has_flag(_dl_job_row, LV_OBJ_FLAG_HIDDEN); // it says enough if (!job_shown && (dl.state() == mapview::TileDownloader::DONE || dl.state() == mapview::TileDownloader::FAILED || dl.state() == mapview::TileDownloader::CANCELLED)) snprintf(last, sizeof(last), "\nLast: %s (%lu new)", dl.message(), (unsigned long)dl.downloaded()); - char size[16]; - uint64_t bytes = (uint64_t)n * mapview::AVG_TILE_BYTES; - if (bytes < 1024 * 1024) snprintf(size, sizeof(size), "%lu KB", (unsigned long)(bytes / 1024)); - else snprintf(size, sizeof(size), "%lu MB", (unsigned long)((bytes + 512 * 1024) / (1024 * 1024))); - lv_label_set_text_fmt(_dl_info, "z%d-%d: %lu tiles, about %s - from %s\nWiFi: %s%s", - a.zmin, a.zmax, (unsigned long)n, size, - dl.sourceHost(), have_wifi ? ssid : "none - Settings > WiFi", last); + lv_label_set_text_fmt(_dl_info, "z%d-%d: %lu tiles, about %s - %s\n%s%s", + a.zmin, a.zmax, (unsigned long)n, size, host, net, last); lv_label_set_text(_dl_start_lbl, n > mapview::TileDownloader::MAX_TILES ? "Too large - zoom in" : LV_SYMBOL_DOWNLOAD " Download"); } diff --git a/examples/companion_radio/ui-lvgl/UITask.h b/examples/companion_radio/ui-lvgl/UITask.h index 8d71882b..e6ebb996 100644 --- a/examples/companion_radio/ui-lvgl/UITask.h +++ b/examples/companion_radio/ui-lvgl/UITask.h @@ -474,6 +474,8 @@ private: lv_obj_t* _map_dl_pill = nullptr; // download progress over the map lv_obj_t* _dl_overlay = nullptr; // download popup lv_obj_t* _dl_info = nullptr; + lv_obj_t* _dl_bar = nullptr; // progress while downloading + lv_obj_t* _dl_err = nullptr; // failures, one line lv_obj_t* _dl_zoom_lbl = nullptr; lv_obj_t* _dl_start_lbl = nullptr; lv_obj_t* _dl_job_row = nullptr; // "Unfinished ... Resume" in the popup