feat(l2): tidier map overlays: activities top centre, credit + scale + zoom in one pill

Recording / live share and the download stack in a column at the top
centre, empty when nothing runs; the recording pill is plain text with
only its mark coloured (red stop, accent pause / live) -- all-red text
was hard to read over the map. The credit, scale bar and zoom share one
pill bottom left (a tap shows the credit). Every map pill is 22 px with
round ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-10-01 11:40:15 +02:00
co-authored by Claude Opus 5.5
parent 1125e9a13e
commit ca5e66a4a1
2 changed files with 43 additions and 31 deletions
+34 -25
View File
@@ -82,6 +82,7 @@ static void setGrid(lv_obj_t* area, Grid& g, double left, double top, double ste
} }
static lv_obj_t* s_scale_bar = nullptr; static lv_obj_t* s_scale_bar = nullptr;
static lv_obj_t* s_scale_lbl = nullptr; static lv_obj_t* s_scale_lbl = nullptr;
static lv_obj_t* s_going = nullptr; // what's going on (recording, download): a column, top centre
// The step for zoom `z` at latitude `lat`: at least `min_px` apart. Label out. // The step for zoom `z` at latitude `lat`: at least `min_px` apart. Label out.
static double gridStep(int z, double lat, bool imperial, int min_px, char* lbl, size_t n) { static double gridStep(int z, double lat, bool imperial, int min_px, char* lbl, size_t n) {
@@ -308,9 +309,9 @@ static lv_obj_t* mapPill(lv_obj_t* parent, const char* text) {
lv_obj_t* l = label(parent, text, THEME_FONT_SMALL, theme::TEXT); lv_obj_t* l = label(parent, text, THEME_FONT_SMALL, theme::TEXT);
lv_obj_set_style_bg_color(l, lv_color_hex(theme::BG), 0); lv_obj_set_style_bg_color(l, lv_color_hex(theme::BG), 0);
lv_obj_set_style_bg_opa(l, LV_OPA_COVER, 0); lv_obj_set_style_bg_opa(l, LV_OPA_COVER, 0);
lv_obj_set_style_pad_hor(l, 5, 0); lv_obj_set_style_pad_hor(l, 7, 0); // 22 px tall, round ends: every pill over the map alike
lv_obj_set_style_pad_ver(l, 1, 0); lv_obj_set_style_pad_ver(l, 3, 0);
lv_obj_set_style_radius(l, 4, 0); lv_obj_set_style_radius(l, 11, 0);
return l; return l;
} }
@@ -442,51 +443,57 @@ void UITask::buildMap() {
int bottom = _map_nav ? navmap::BAR_H : 0; // the nav bar takes the bottom edge int bottom = _map_nav ? navmap::BAR_H : 0; // the nav bar takes the bottom edge
_map_center_btn = mapButton(body, LV_SYMBOL_GPS, onMapCenter); _map_center_btn = mapButton(body, LV_SYMBOL_GPS, onMapCenter);
lv_obj_align(_map_center_btn, LV_ALIGN_BOTTOM_RIGHT, -6, -6 - bottom); lv_obj_align(_map_center_btn, LV_ALIGN_BOTTOM_RIGHT, -6, -6 - bottom);
_map_dl_pill = mapPill(body, ""); // tap: the download popup // Top centre: what's going on, one pill under another -- the trail /
// live share (NavMap.h), the download. Empty most of the time.
mapview::s_going = lv_obj_create(body);
lv_obj_remove_style_all(mapview::s_going);
lv_obj_remove_flag(mapview::s_going, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_flag(mapview::s_going, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(mapview::s_going, 212, LV_SIZE_CONTENT); // between the button columns
lv_obj_set_flex_flow(mapview::s_going, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(mapview::s_going, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_row(mapview::s_going, 4, 0);
lv_obj_align(mapview::s_going, LV_ALIGN_TOP_MID, 0, 15);
_map_dl_pill = mapPill(mapview::s_going, ""); // tap: the download popup
lv_obj_set_style_text_color(_map_dl_pill, lv_color_hex(theme::ACCENT), 0); lv_obj_set_style_text_color(_map_dl_pill, lv_color_hex(theme::ACCENT), 0);
lv_obj_set_style_pad_ver(_map_dl_pill, 3, 0);
lv_obj_align(_map_dl_pill, LV_ALIGN_TOP_RIGHT, -52, 14); // beside +, clear of the zoom pill on the left
lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_CLICKABLE); lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_ext_click_area(_map_dl_pill, 6); lv_obj_set_ext_click_area(_map_dl_pill, 6);
lv_obj_add_event_cb(_map_dl_pill, onMapDownload, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(_map_dl_pill, onMapDownload, LV_EVENT_CLICKED, NULL);
lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_map_dl_pill, LV_OBJ_FLAG_HIDDEN);
_map_zoom_lbl = mapPill(body, ""); // Bottom left, one pill about the map itself: the map data credit (the
lv_obj_align(_map_zoom_lbl, LV_ALIGN_TOP_LEFT, 52, 16); // licences require it on the map; a tap shows the full line, also in
// Map data credit (the licences require it on the map): a small "©" that // Settings > About), a bar one grid step long with its distance, the zoom.
// shows the full line when tapped; it is also in Settings > About. lv_obj_t* sc = lv_obj_create(body); // a pill (mapPill's look) holding a row
lv_obj_t* attr = mapPill(body, "\xC2\xA9");
lv_obj_set_style_pad_hor(attr, 7, 0);
lv_obj_set_style_pad_ver(attr, 3, 0);
lv_obj_add_flag(attr, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_ext_click_area(attr, 8);
lv_obj_add_event_cb(attr, onMapCredits, LV_EVENT_CLICKED, NULL);
lv_obj_align(attr, LV_ALIGN_BOTTOM_LEFT, 6, -6 - bottom);
// Scale: a bar one grid step long, the distance beside it.
lv_obj_t* sc = lv_obj_create(body);
lv_obj_remove_style_all(sc); lv_obj_remove_style_all(sc);
lv_obj_remove_flag(sc, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_flag(sc, LV_OBJ_FLAG_SCROLLABLE); lv_obj_remove_flag(sc, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(sc, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_bg_color(sc, lv_color_hex(theme::BG), 0); lv_obj_set_style_bg_color(sc, lv_color_hex(theme::BG), 0);
lv_obj_set_style_bg_opa(sc, LV_OPA_COVER, 0); lv_obj_set_style_bg_opa(sc, LV_OPA_COVER, 0);
lv_obj_set_style_radius(sc, 4, 0); lv_obj_set_style_pad_hor(sc, 8, 0);
lv_obj_set_style_pad_hor(sc, 5, 0);
lv_obj_set_style_pad_ver(sc, 3, 0); lv_obj_set_style_pad_ver(sc, 3, 0);
lv_obj_set_style_radius(sc, 11, 0);
lv_obj_set_size(sc, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(sc, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(sc, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(sc, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_flex_align(sc, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(sc, 5, 0); lv_obj_set_style_pad_column(sc, 6, 0);
lv_obj_add_flag(sc, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_ext_click_area(sc, 8);
lv_obj_add_event_cb(sc, onMapCredits, LV_EVENT_CLICKED, NULL);
label(sc, "\xC2\xA9", THEME_FONT_SMALL, theme::TEXT_MUTED);
mapview::s_scale_bar = lv_obj_create(sc); mapview::s_scale_bar = lv_obj_create(sc);
lv_obj_remove_style_all(mapview::s_scale_bar); lv_obj_remove_style_all(mapview::s_scale_bar);
lv_obj_remove_flag(mapview::s_scale_bar, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(mapview::s_scale_bar, 60, 6); lv_obj_set_size(mapview::s_scale_bar, 60, 6);
lv_obj_set_style_border_color(mapview::s_scale_bar, lv_color_hex(theme::TEXT), 0); lv_obj_set_style_border_color(mapview::s_scale_bar, lv_color_hex(theme::TEXT), 0);
lv_obj_set_style_border_width(mapview::s_scale_bar, 2, 0); lv_obj_set_style_border_width(mapview::s_scale_bar, 2, 0);
lv_obj_set_style_border_side(mapview::s_scale_bar, (lv_border_side_t)(LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_RIGHT), 0); lv_obj_set_style_border_side(mapview::s_scale_bar, (lv_border_side_t)(LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_RIGHT), 0);
mapview::s_scale_lbl = label(sc, "", THEME_FONT_SMALL, theme::TEXT); mapview::s_scale_lbl = label(sc, "", THEME_FONT_SMALL, theme::TEXT);
lv_obj_align(sc, LV_ALIGN_BOTTOM_LEFT, 36, -6 - bottom); _map_zoom_lbl = label(sc, "", THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_obj_align(sc, LV_ALIGN_BOTTOM_LEFT, 6, -6 - bottom);
_map_hint = mapPill(body, ""); _map_hint = mapPill(body, "");
lv_label_set_long_mode(_map_hint, LV_LABEL_LONG_WRAP); lv_label_set_long_mode(_map_hint, LV_LABEL_LONG_WRAP);
lv_obj_set_width(_map_hint, 200); lv_obj_set_width(_map_hint, 200);
lv_obj_set_style_text_align(_map_hint, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_align(_map_hint, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_radius(_map_hint, 8, 0); // may wrap
lv_obj_align(_map_hint, LV_ALIGN_TOP_MID, 0, 50); // clear of own position, mid-map lv_obj_align(_map_hint, LV_ALIGN_TOP_MID, 0, 50); // clear of own position, mid-map
lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN);
if (_map_nav) buildNavControls(body); if (_map_nav) buildNavControls(body);
@@ -739,6 +746,8 @@ void UITask::addMapMark(uint8_t kind, int idx, int32_t lat_e6, int32_t lon_e6, u
lv_obj_remove_flag(dot, LV_OBJ_FLAG_CLICKABLE); lv_obj_remove_flag(dot, LV_OBJ_FLAG_CLICKABLE);
lv_obj_t* name = mapPill(m, text); lv_obj_t* name = mapPill(m, text);
lv_obj_remove_flag(name, LV_OBJ_FLAG_CLICKABLE); lv_obj_remove_flag(name, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_pad_ver(name, 1, 0); // a marker's name stays small
lv_obj_set_style_radius(name, 4, 0);
mapview::Mark& mk = mapview::s_marks[mapview::s_mark_count++]; mapview::Mark& mk = mapview::s_marks[mapview::s_mark_count++];
mk.lat_e6 = lat_e6; mk.lon_e6 = lon_e6; mk.idx = idx; mk.kind = kind; mk.col = col; mk.text_hash = h; mk.obj = m; mk.dot = dot; mk.lat_e6 = lat_e6; mk.lon_e6 = lon_e6; mk.idx = idx; mk.kind = kind; mk.col = col; mk.text_hash = h; mk.obj = m; mk.dot = dot;
+9 -6
View File
@@ -198,9 +198,9 @@ void UITask::buildNavControls(lv_obj_t* body) {
lv_obj_add_flag(_nav_avg_pill, LV_OBJ_FLAG_CLICKABLE); lv_obj_add_flag(_nav_avg_pill, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(_nav_avg_pill, onNavAvgCancel, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(_nav_avg_pill, onNavAvgCancel, LV_EVENT_CLICKED, NULL);
lv_obj_add_flag(_nav_avg_pill, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_nav_avg_pill, LV_OBJ_FLAG_HIDDEN);
_nav_rec = mapPill(body, ""); // trail recording / live share running _nav_rec = mapPill(mapview::s_going, ""); // trail recording / live share running, over the download
lv_obj_set_style_text_color(_nav_rec, lv_color_hex(theme::FAIL), 0); lv_label_set_recolor(_nav_rec, true);
lv_obj_align(_nav_rec, LV_ALIGN_TOP_MID, 0, 40); lv_obj_move_to_index(_nav_rec, 0);
lv_obj_add_flag(_nav_rec, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_nav_rec, LV_OBJ_FLAG_HIDDEN);
_next_nav_bar_ms = 0; _next_nav_bar_ms = 0;
navmap::s_eta.reset(); navmap::s_eta.reset();
@@ -380,18 +380,21 @@ void UITask::refreshNavBar() {
if (!_nav_bar) return; if (!_nav_bar) return;
refreshNavTools(); refreshNavTools();
{ {
char t[48] = ""; // Plain text, the marks coloured (recolor): red for recording, accent
// for paused and live -- all-red text was hard to read over the map.
char t[96] = "";
int o = 0; int o = 0;
TrailStore& ts = _core->trail.store(); TrailStore& ts = _core->trail.store();
if (ts.isActive()) { if (ts.isActive()) {
char d[12]; char d[12];
geo::fmtDist(d, sizeof(d), ts.totalDistanceMeters() / 1000.0f, _prefs && _prefs->units_imperial); geo::fmtDist(d, sizeof(d), ts.totalDistanceMeters() / 1000.0f, _prefs && _prefs->units_imperial);
o += snprintf(t + o, sizeof(t) - o, "%s %s", ts.isPaused() ? "PAUSED" : "REC", d); o += snprintf(t + o, sizeof(t) - o, ts.isPaused() ? "#%06lX " LV_SYMBOL_PAUSE "# PAUSED %s" : "#%06lX " LV_SYMBOL_STOP "# REC %s",
(unsigned long)(ts.isPaused() ? theme::ACCENT : theme::FAIL), d);
} }
if (_prefs && _prefs->loc_share_enabled) { if (_prefs && _prefs->loc_share_enabled) {
char left[12]; char left[12];
navmap::fmtDuration(left, sizeof(left), _core->live_share.remainingSecs()); navmap::fmtDuration(left, sizeof(left), _core->live_share.remainingSecs());
o += snprintf(t + o, sizeof(t) - o, "%sLIVE %s", o ? " " : "", left); o += snprintf(t + o, sizeof(t) - o, "%s#%06lX " LV_SYMBOL_GPS "# LIVE %s", o ? " " : "", (unsigned long)theme::ACCENT, left);
} }
setText(_nav_rec, t); setText(_nav_rec, t);
if (o) lv_obj_remove_flag(_nav_rec, LV_OBJ_FLAG_HIDDEN); else lv_obj_add_flag(_nav_rec, LV_OBJ_FLAG_HIDDEN); if (o) lv_obj_remove_flag(_nav_rec, LV_OBJ_FLAG_HIDDEN); else lv_obj_add_flag(_nav_rec, LV_OBJ_FLAG_HIDDEN);