diff --git a/examples/companion_radio/ui-lvgl/DeviceScreen.h b/examples/companion_radio/ui-lvgl/DeviceScreen.h index c67fa5be..72856462 100644 --- a/examples/companion_radio/ui-lvgl/DeviceScreen.h +++ b/examples/companion_radio/ui-lvgl/DeviceScreen.h @@ -477,7 +477,7 @@ void UITask::favGrid(lv_obj_t* grid) { if (used) { int unread = chan ? _core->history.chUnread(_prefs->favourite_contacts[s][0]) : (c.type == ADV_TYPE_CHAT ? _core->dmUnread(c.id.pub_key) : 0); - badge(b, unread, false); + tileBadge(b, unread); } } lv_obj_t* hint = label(grid, "Tap to open. Hold to change or remove.", THEME_FONT_SMALL, theme::TEXT_MUTED); diff --git a/examples/companion_radio/ui-lvgl/HomeScreen.h b/examples/companion_radio/ui-lvgl/HomeScreen.h index 1f12c62d..0070fa02 100644 --- a/examples/companion_radio/ui-lvgl/HomeScreen.h +++ b/examples/companion_radio/ui-lvgl/HomeScreen.h @@ -128,7 +128,7 @@ static lv_obj_t* homeTile(lv_obj_t* parent, const char* icon, const char* text, lv_obj_set_style_text_align(n, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_width(n, w - 10); lv_obj_align(n, LV_ALIGN_BOTTOM_MID, 0, -8); - badge(b, badge_n, false); + tileBadge(b, badge_n); return b; } @@ -412,7 +412,12 @@ void UITask::homeMapFit() { bool me = _core->course.currentLocation(lat, lon); int live = 0; bool target = false; + bool last = false; // no fix: the saved / app-set position, hollow if (me) s_pts[s_npts++] = { lat, lon, dot(14, theme::ACCENT, theme::BG) }; + else if (_sensors && (_sensors->node_lat != 0 || _sensors->node_lon != 0)) { + last = true; + s_pts[s_npts++] = { (int32_t)lround(_sensors->node_lat * 1e6), (int32_t)lround(_sensors->node_lon * 1e6), dot(14, theme::BG, theme::ACCENT) }; + } if (_core->locator.activeTargetPos(lat, lon)) { target = true; s_pts[s_npts++] = { lat, lon, dot(12, theme::BG, theme::ACCENT) }; @@ -429,15 +434,21 @@ void UITask::homeMapFit() { lv_obj_move_foreground(s_caption); char cap[48]; - int o = snprintf(cap, sizeof(cap), "%s", me ? "You" : "No position yet"); + int o = snprintf(cap, sizeof(cap), "%s", me ? "You" : last ? "Last position" : "No position yet"); if (live) o += snprintf(cap + o, sizeof(cap) - o, " " LV_SYMBOL_GPS " %d sharing", live); if (target) snprintf(cap + o, sizeof(cap) - o, " " UI_SYMBOL_FLAG " target"); lv_label_set_text(s_caption, cap); - if (s_npts == 0) { // nothing to show: the default view - s_z = mapview::DEFAULT_Z; - s_cx = mapview::lonToTileX(mapview::DEFAULT_LON, s_z); - s_cy = mapview::latToTileY(mapview::DEFAULT_LAT, s_z); + if (s_npts == 0) { // nothing to show: where the map was left, else the default view + if (_map_z) { + s_z = _map_z; + s_cx = _map_cx; + s_cy = _map_cy; + } else { + s_z = mapview::DEFAULT_Z; + s_cx = mapview::lonToTileX(mapview::DEFAULT_LON, s_z); + s_cy = mapview::latToTileY(mapview::DEFAULT_LAT, s_z); + } return; } // Bounds in world units (tiles at z 0), then the deepest zoom they fit at. @@ -465,6 +476,7 @@ void UITask::homeMapLayout() { int tx0 = (int)floor(left / mapview::TILE_PX), ty0 = (int)floor(top / mapview::TILE_PX); int n = 1 << s_z; s_pending = false; + int shown = 0; for (int j = 0; j < ROWS; j++) { for (int i = 0; i < COLS; i++) { lv_obj_t* cell = s_cells[j * COLS + i]; @@ -492,6 +504,7 @@ void UITask::homeMapLayout() { lv_obj_set_pos(img, -(wx & m) * mapview::TILE_PX, -(ty & m) * mapview::TILE_PX); lv_obj_set_pos(cell, px, py); lv_obj_remove_flag(cell, LV_OBJ_FLAG_HIDDEN); + shown++; } } for (int i = 0; i < s_npts; i++) { @@ -500,7 +513,8 @@ void UITask::homeMapLayout() { int d = lv_obj_get_width(s_pts[i].obj); lv_obj_set_pos(s_pts[i].obj, (int)lround(x) - d / 2, (int)lround(y) - d / 2); } - const char* hint = !mapview::s_available ? "No map on the SD card" : nullptr; + const char* hint = !mapview::s_available ? "No map on the SD card" + : !shown && !s_pending ? "No map tiles for this area" : nullptr; if (hint) { lv_label_set_text(s_hint, hint); lv_obj_remove_flag(s_hint, LV_OBJ_FLAG_HIDDEN); } else lv_obj_add_flag(s_hint, LV_OBJ_FLAG_HIDDEN); } diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index ebc7087d..00778ff7 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -655,6 +655,13 @@ static void badge(lv_obj_t* parent, int n, bool overflow) { lv_obj_center(l); } +// The same on a Home tile: in its top-right corner, clear of the icon. +static void tileBadge(lv_obj_t* tile, int n) { + if (n <= 0) return; + badge(tile, n, false); + lv_obj_align(lv_obj_get_child(tile, -1), LV_ALIGN_TOP_RIGHT, -2, 2); +} + // Local time (NodePrefs::tz_offset_hours); false before the clock is set. static bool localTime(const NodePrefs* p, struct tm& out) { uint32_t now = rtc_clock.getCurrentTime(); @@ -1095,6 +1102,8 @@ void UITask::buildStatusBar() { lv_obj_align(_status_time, LV_ALIGN_LEFT_MID, 0, 0); _status_batt = label(bar, "", THEME_FONT_ICONS, theme::TEXT_MUTED); lv_obj_align(_status_batt, LV_ALIGN_RIGHT_MID, 0, 0); + _status_chg = label(bar, LV_SYMBOL_CHARGE, THEME_FONT_ICONS, theme::OK); + lv_obj_add_flag(_status_chg, LV_OBJ_FLAG_HIDDEN); _status_icons = lv_obj_create(bar); lv_obj_remove_style_all(_status_icons); lv_obj_remove_flag(_status_icons, LV_OBJ_FLAG_CLICKABLE); @@ -1146,19 +1155,25 @@ void UITask::refreshStatusBar() { case battery::VOLTAGE: snprintf(level, sizeof(level), " %u.%02u V", mv / 1000, (mv % 1000) / 10); break; default: break; } - lv_label_set_text_fmt(_status_batt, "%s%s%s", _board->isExternalPowered() ? LV_SYMBOL_CHARGE : "", batt, level); + lv_label_set_text_fmt(_status_batt, "%s%s", batt, level); + lv_obj_t* left_of = _status_batt; // the icons pack up to this + if (_board->isExternalPowered()) { + lv_obj_remove_flag(_status_chg, LV_OBJ_FLAG_HIDDEN); + lv_obj_align_to(_status_chg, _status_batt, LV_ALIGN_OUT_LEFT_MID, -2, 0); + left_of = _status_chg; + } else lv_obj_add_flag(_status_chg, LV_OBJ_FLAG_HIDDEN); - // Status icons, as the original's status bar (ui-new): Bluetooth (bright - // when the app is connected), WiFi (when switched on), GPS (green with a fix), the alarm, mute, then + // Status icons, as the original's status bar (ui-new): Bluetooth (accent + // when the app is connected), WiFi (when switched on, accent connected), GPS (green with a fix), the alarm, mute, then // the modes that keep running in the background -- auto-advert, trail, live // share, repeater, arrival alert -- in the accent colour. Right to left in // that order, next to the battery. struct Icon { const char* sym; uint32_t col; }; Icon icons[11]; int n = 0; - if (isSerialEnabled()) icons[n++] = { LV_SYMBOL_BLUETOOTH, hasConnection() ? theme::TEXT : theme::TEXT_MUTED }; - if (lvport::wifiAllowed()) // WiFi switched on: bright while connected (map tiles, update) - icons[n++] = { LV_SYMBOL_WIFI, lvport::netRadio() == lvport::NET_UP ? theme::TEXT : theme::TEXT_MUTED }; + if (isSerialEnabled()) icons[n++] = { LV_SYMBOL_BLUETOOTH, hasConnection() ? theme::ACCENT : theme::TEXT_MUTED }; + if (lvport::wifiAllowed()) // WiFi switched on: accent while connected (map tiles, update) + icons[n++] = { LV_SYMBOL_WIFI, lvport::netRadio() == lvport::NET_UP ? theme::ACCENT : theme::TEXT_MUTED }; int32_t lat, lon; bool fix = _core->course.currentLocation(lat, lon); if (_core->gpsEnabled() || fix) icons[n++] = { LV_SYMBOL_GPS, fix ? theme::OK : theme::TEXT_MUTED }; @@ -1181,7 +1196,7 @@ void UITask::refreshStatusBar() { for (int i = n - 1; i >= 0; i--) // the icon font gives each the same size and cell label(_status_icons, icons[i].sym, THEME_FONT_ICONS, icons[i].col); } - lv_obj_align_to(_status_icons, _status_batt, LV_ALIGN_OUT_LEFT_MID, -5, 0); + lv_obj_align_to(_status_icons, left_of, LV_ALIGN_OUT_LEFT_MID, -5, 0); } void UITask::setGps(bool on) { @@ -1256,7 +1271,7 @@ lv_obj_t* UITask::newScreen(const char* title, bool with_back) { lv_obj_t* hdr = lv_obj_create(scr); styleSurface(hdr, theme::BG); lv_obj_remove_flag(hdr, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_size(hdr, LV_PCT(100), 32); + lv_obj_set_size(hdr, LV_PCT(100), 38); lv_obj_set_pos(hdr, 0, top); _header = hdr; if (with_back) { @@ -1272,7 +1287,7 @@ lv_obj_t* UITask::newScreen(const char* title, bool with_back) { lv_label_set_long_mode(t, LV_LABEL_LONG_DOT); lv_obj_set_width(t, 230); lv_obj_align(t, LV_ALIGN_LEFT_MID, with_back ? 52 : theme::PAD, 0); - top += 32; + top += 38; } lv_obj_t* body = lv_obj_create(scr); diff --git a/examples/companion_radio/ui-lvgl/UITask.h b/examples/companion_radio/ui-lvgl/UITask.h index 39b2c956..0d5c6ed5 100644 --- a/examples/companion_radio/ui-lvgl/UITask.h +++ b/examples/companion_radio/ui-lvgl/UITask.h @@ -442,6 +442,7 @@ private: lv_obj_t* _status_time = nullptr; lv_obj_t* _status_icons = nullptr; // a row of icon labels, rebuilt with the state lv_obj_t* _status_batt = nullptr; + lv_obj_t* _status_chg = nullptr; // the charging bolt, green, left of the battery char _status_sig[128] = ""; // what the icon row shows, to skip unchanged rebuilds lv_obj_t* _toast = nullptr; lv_timer_t* _toast_timer = nullptr;