mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 11:16:39 +00:00
fix(ui-lvgl): tile badges in the corner, minimap without a fix, taller header, accent status icons
- Unread badge on Home tiles sits in the top-right corner, clear of the icon - Minimap with no GPS fix: the saved position (hollow dot), else where the map was left, else the default view; a hint when no tiles cover the area - Screen header 38 px, so the back and header buttons get a margin - Bluetooth / WiFi connected shown in the accent colour; charging bolt green Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user