feat(ui-lvgl): map tools regrouped; a map area previews when picked

Map tools: TRAIL, LIVE SHARE and ARRIVAL ALERT each with their own options
page (that section of Map options; back returns to Map tools), the arrival
alert switch where its state is shown, OFFLINE MAPS (Map areas, Live
tiles; the separate Download an area is gone -- a running or unfinished
download shows at the top of Map areas) and LAYERS.

Map areas: picking one frames it on the map above a small sheet (no Show
button, the map above isn't dimmed); closing the sheet, however it's
closed, puts the map back where it was and drops the outline -- it used
to stay on the map. New popup kind POP_BOTTOM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-28 21:30:46 +02:00
co-authored by Claude Opus 5.5
parent a11df8431b
commit e47738ce01
4 changed files with 128 additions and 64 deletions
+64 -37
View File
@@ -1,7 +1,8 @@
#pragma once #pragma once
// Map areas: picking an area to download with a frame on the map, and the // Map areas: picking an area to download with a frame on the map, and the
// list of downloaded areas (map/AreaStore.h) -- show, rename, fill gaps, // list of downloaded areas (map/AreaStore.h). Picking one frames it on the
// refresh, trails on / off, delete. // map above a small sheet -- rename, fill gaps, refresh, trails on / off,
// delete; closing the sheet puts the map back where it was.
// //
// The frame: four corner handles on the map, dragged to size it; the map // The frame: four corner handles on the map, dragged to size it; the map
// pans and zooms under it as usual, so an area can be larger than the screen. // pans and zooms under it as usual, so an area can be larger than the screen.
@@ -14,7 +15,7 @@
namespace areas { namespace areas {
enum : uint8_t { A_SHOW, A_RENAME, A_FILL, A_REFRESH, A_TRAILS, A_DELETE }; enum : uint8_t { A_RENAME, A_FILL, A_REFRESH, A_TRAILS, A_DELETE };
static bool s_sel = false; // the frame is up static bool s_sel = false; // the frame is up
static double s_fx0, s_fy0, s_fx1, s_fy1; // the frame, world units (tiles at z 0) static double s_fx0, s_fy0, s_fx1, s_fy1; // the frame, world units (tiles at z 0)
@@ -26,7 +27,11 @@ static lv_obj_t* s_bar = nullptr;
static lv_obj_t* s_info = nullptr; static lv_obj_t* s_info = nullptr;
static lv_obj_t* s_zoom = nullptr; static lv_obj_t* s_zoom = nullptr;
static lv_obj_t* s_trails_btn = nullptr; static lv_obj_t* s_trails_btn = nullptr;
static int s_show = -1; // area outlined after Show static int s_show = -1; // area outlined while its sheet is open
static bool s_preview = false; // the map is framing it; the view before:
static double s_prev_cx, s_prev_cy;
static int s_prev_z;
static bool s_prev_follow;
static int s_idx = -1; // area in the open popup static int s_idx = -1; // area in the open popup
static lv_obj_t* s_del_lbl = nullptr; static lv_obj_t* s_del_lbl = nullptr;
static uint32_t s_del_armed_ms = 0; static uint32_t s_del_armed_ms = 0;
@@ -83,6 +88,7 @@ static void onZPlus(lv_event_t* e) { (void)e; s_ui->areaSelectZmax(+1); }
static void onTrailsChip(lv_event_t* e) { (void)e; s_trails = !s_trails; s_ui->areaLayout(); } static void onTrailsChip(lv_event_t* e) { (void)e; s_trails = !s_trails; s_ui->areaLayout(); }
static void onArea(lv_event_t* e) { s_ui->mapAreaPopup((int)(intptr_t)lv_event_get_user_data(e)); } static void onArea(lv_event_t* e) { s_ui->mapAreaPopup((int)(intptr_t)lv_event_get_user_data(e)); }
static void onNewArea(lv_event_t* e) { (void)e; s_ui->areaSelectBegin(); } static void onNewArea(lv_event_t* e) { (void)e; s_ui->areaSelectBegin(); }
static void onDlOpen(lv_event_t* e) { (void)e; s_ui->navClosePopup(); s_ui->mapDownloadPopup(); }
static void onAct(lv_event_t* e) { s_ui->mapAreaAction((uint8_t)(uintptr_t)lv_event_get_user_data(e)); } static void onAct(lv_event_t* e) { s_ui->mapAreaAction((uint8_t)(uintptr_t)lv_event_get_user_data(e)); }
static void onRenameKb(lv_event_t* e) { s_ui->mapAreaRenameDone(lv_event_get_code(e) == LV_EVENT_READY); } static void onRenameKb(lv_event_t* e) { s_ui->mapAreaRenameDone(lv_event_get_code(e) == LV_EVENT_READY); }
@@ -150,18 +156,21 @@ static lv_obj_t* barButton(lv_obj_t* row, const char* text, lv_event_cb_t cb, in
return b; return b;
} }
static lv_obj_t* actButton(lv_obj_t* row, const char* text, uint8_t act, bool accent) { // A sheet button: its icon over a short word.
static lv_obj_t* actButton(lv_obj_t* row, const char* icon, const char* text, uint8_t act) {
lv_obj_t* b = lv_button_create(row); lv_obj_t* b = lv_button_create(row);
lv_obj_set_height(b, 36); lv_obj_set_height(b, 46);
lv_obj_set_flex_grow(b, 1); lv_obj_set_flex_grow(b, 1);
lv_obj_set_style_pad_hor(b, 4, 0); lv_obj_set_width(b, 1);
lv_obj_set_style_pad_all(b, 2, 0);
lv_obj_set_style_radius(b, theme::RADIUS, 0); lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_shadow_width(b, 0, 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), 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::SURFACE_2), LV_STATE_PRESSED); lv_obj_set_style_bg_color(b, lv_color_hex(theme::SURFACE_2), LV_STATE_PRESSED);
lv_obj_add_event_cb(b, onAct, LV_EVENT_CLICKED, (void*)(uintptr_t)act); lv_obj_add_event_cb(b, onAct, LV_EVENT_CLICKED, (void*)(uintptr_t)act);
lv_obj_align(label(b, icon, THEME_FONT_BODY, theme::ACCENT), LV_ALIGN_TOP_MID, 0, 3);
lv_obj_t* l = label(b, text, THEME_FONT_SMALL, theme::TEXT); lv_obj_t* l = label(b, text, THEME_FONT_SMALL, theme::TEXT);
lv_obj_center(l); lv_obj_align(l, LV_ALIGN_BOTTOM_MID, 0, -2);
return l; return l;
} }
@@ -358,6 +367,14 @@ void UITask::mapAreasPopup() {
snprintf(t, sizeof(t), "Deleting... %lu files", (unsigned long)mapview::s_areas.deleted()); snprintf(t, sizeof(t), "Deleting... %lu files", (unsigned long)mapview::s_areas.deleted());
label(list, t, THEME_FONT_SMALL, theme::ACCENT); label(list, t, THEME_FONT_SMALL, theme::ACCENT);
} }
mapview::TileArea job; // a download running or left unfinished: its progress / Resume
if (mapview::s_dl.active() || mapview::s_dl.savedJob(job)) {
char sub[48];
if (mapview::s_dl.active())
snprintf(sub, sizeof(sub), "%lu of %lu tiles", (unsigned long)mapview::s_dl.processed(), (unsigned long)mapview::s_dl.total());
listRow(group(list, nullptr), mapview::s_dl.active() ? LV_SYMBOL_DOWNLOAD " Downloading" : LV_SYMBOL_PAUSE " Unfinished download",
mapview::s_dl.active() ? sub : "Resume or discard it", onDlOpen, nullptr);
}
if (n) { if (n) {
lv_obj_t* g = group(list, nullptr); lv_obj_t* g = group(list, nullptr);
for (int i = n - 1; i >= 0; i--) { // newest first for (int i = n - 1; i >= 0; i--) { // newest first
@@ -386,28 +403,52 @@ void UITask::mapAreasPopup() {
void UITask::mapAreaPopup(int idx) { void UITask::mapAreaPopup(int idx) {
using namespace areas; using namespace areas;
if (idx < 0 || idx >= mapview::s_areas.count()) return; if (idx < 0 || idx >= mapview::s_areas.count()) return;
s_idx = idx;
const mapview::MapArea& m = mapview::s_areas.at(idx); const mapview::MapArea& m = mapview::s_areas.at(idx);
lv_obj_t* panel = navPopupPanel(m.name, false); lv_obj_t* panel = navPopupPanel(m.name, false, true); // ends an earlier preview first
char info[128], date[16]; s_idx = idx;
char info[96], date[16];
fmtDate(date, sizeof(date), m.created, _prefs); fmtDate(date, sizeof(date), m.created, _prefs);
uint32_t n = mapview::countTiles(m.box); uint32_t n = mapview::countTiles(m.box);
snprintf(info, sizeof(info), "Zoom %d-%d, %lu tiles%s%s%s\n%s", m.box.zmin, m.box.zmax, (unsigned long)n, snprintf(info, sizeof(info), "Zoom %d-%d, %lu tiles%s%s%s%s", m.box.zmin, m.box.zmax, (unsigned long)n,
(m.flags & mapview::AreaStore::F_TRAILS) ? ", trails" : "", date[0] ? " - " : "", date, (m.flags & mapview::AreaStore::F_TRAILS) ? ", trails" : "", date[0] ? " - " : "", date,
(m.flags & mapview::AreaStore::F_COMPLETE) ? "Downloaded" : "Unfinished - Fill gaps completes it"); (m.flags & mapview::AreaStore::F_COMPLETE) ? "" : "\nUnfinished - Fill gaps completes it");
lv_obj_t* l = label(panel, info, THEME_FONT_SMALL, theme::TEXT); lv_obj_t* l = label(panel, info, THEME_FONT_SMALL, theme::TEXT_MUTED);
lv_label_set_long_mode(l, LV_LABEL_LONG_WRAP); lv_label_set_long_mode(l, LV_LABEL_LONG_WRAP);
lv_obj_set_width(l, LV_PCT(100)); lv_obj_set_width(l, LV_PCT(100));
lv_obj_t* r = toolRow(panel); lv_obj_t* r = toolRow(panel);
actButton(r, UI_SYMBOL_MAP " Show", A_SHOW, true); actButton(r, LV_SYMBOL_EDIT, "Rename", A_RENAME);
actButton(r, LV_SYMBOL_EDIT " Rename", A_RENAME, false); actButton(r, LV_SYMBOL_DOWNLOAD, "Fill", A_FILL);
r = toolRow(panel); actButton(r, LV_SYMBOL_REFRESH, "Refresh", A_REFRESH);
actButton(r, LV_SYMBOL_DOWNLOAD " Fill gaps", A_FILL, false); actButton(r, (m.flags & mapview::AreaStore::F_TRAILS) ? LV_SYMBOL_MINUS : LV_SYMBOL_PLUS, "Trails", A_TRAILS);
actButton(r, LV_SYMBOL_REFRESH " Refresh", A_REFRESH, false); s_del_lbl = actButton(r, LV_SYMBOL_TRASH, "Delete", A_DELETE);
r = toolRow(panel);
actButton(r, (m.flags & mapview::AreaStore::F_TRAILS) ? "Remove trails" : LV_SYMBOL_PLUS " Trails", A_TRAILS, false);
s_del_lbl = actButton(r, LV_SYMBOL_TRASH " Delete", A_DELETE, false);
s_del_armed_ms = 0; s_del_armed_ms = 0;
// Framed in the map above the sheet: the view is kept to go back to.
lv_obj_update_layout(panel);
int w = _map_area ? lv_obj_get_width(_map_area) : 320, h = _map_area ? lv_obj_get_height(_map_area) : 218;
int sheet = lv_obj_get_height(panel) + 8; // its inset
int above = h - sheet;
if (above < 60) above = 60;
s_prev_cx = _map_cx; s_prev_cy = _map_cy; s_prev_z = _map_z; s_prev_follow = _map_follow;
s_preview = true;
double x0, y0, x1, y1;
boxWorld(m.box, x0, y0, x1, y1);
_map_z = fitZoom(x1 - x0, y1 - y0, w - 24, above - 16);
double s = (double)(1 << _map_z);
_map_cx = (x0 + x1) / 2 * s;
_map_cy = (y0 + y1) / 2 * s + (double)(h - above) / 2 / mapview::TILE_PX; // centred in the part left showing
_map_follow = false;
s_show = idx;
layoutMap();
}
void UITask::areaPreviewEnd() {
using namespace areas;
if (!s_preview) return;
s_preview = false;
s_show = -1;
_map_cx = s_prev_cx; _map_cy = s_prev_cy; _map_z = s_prev_z; _map_follow = s_prev_follow;
if (_screen == SCR_MAP && _map_area) layoutMap();
} }
void UITask::mapAreaAction(uint8_t act) { void UITask::mapAreaAction(uint8_t act) {
@@ -416,20 +457,6 @@ void UITask::mapAreaAction(uint8_t act) {
mapview::MapArea m = mapview::s_areas.at(s_idx); mapview::MapArea m = mapview::s_areas.at(s_idx);
bool trails = m.flags & mapview::AreaStore::F_TRAILS; bool trails = m.flags & mapview::AreaStore::F_TRAILS;
switch (act) { switch (act) {
case A_SHOW: {
navClosePopup();
double x0, y0, x1, y1;
boxWorld(m.box, x0, y0, x1, y1);
int w = _map_area ? lv_obj_get_width(_map_area) : 320, h = _map_area ? lv_obj_get_height(_map_area) : 218;
_map_z = fitZoom(x1 - x0, y1 - y0, w - 40, h - 60);
double s = (double)(1 << _map_z);
_map_cx = (x0 + x1) / 2 * s;
_map_cy = (y0 + y1) / 2 * s;
_map_follow = false;
s_show = s_idx;
layoutMap();
return;
}
case A_RENAME: { case A_RENAME: {
lv_obj_t* panel = navPopupPanel("Area name", false); lv_obj_t* panel = navPopupPanel("Area name", false);
lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4); // above the keyboard lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4); // above the keyboard
+31 -21
View File
@@ -474,9 +474,9 @@ void UITask::navToNode(const uint8_t* key, int32_t lat, int32_t lon, const char*
// ── Popups: target list, waypoint menu, rename ──────────────────────────────── // ── Popups: target list, waypoint menu, rename ────────────────────────────────
lv_obj_t* UITask::navPopupPanel(const char* title, bool full) { lv_obj_t* UITask::navPopupPanel(const char* title, bool full, bool bottom) {
navClosePopup(); navClosePopup();
lv_obj_t* panel = popupOpen(screen(), full ? POP_FULL : POP_FIT, _nav_overlay); lv_obj_t* panel = popupOpen(screen(), bottom ? POP_BOTTOM : full ? POP_FULL : POP_FIT, _nav_overlay);
lv_obj_set_style_pad_row(panel, 6, 0); lv_obj_set_style_pad_row(panel, 6, 0);
lv_obj_t* hdr = lv_obj_create(panel); lv_obj_t* hdr = lv_obj_create(panel);
@@ -492,6 +492,7 @@ lv_obj_t* UITask::navPopupPanel(const char* title, bool full) {
} }
void UITask::navClosePopup() { void UITask::navClosePopup() {
areaPreviewEnd(); // a map area's popup: the map goes back where it was
if (_nav_overlay) lv_obj_delete_async(_nav_overlay); // may be closing from its own button if (_nav_overlay) lv_obj_delete_async(_nav_overlay); // may be closing from its own button
_nav_overlay = _nav_ta = _nav_kb = _nav_del_lbl = nullptr; _nav_overlay = _nav_ta = _nav_kb = _nav_del_lbl = nullptr;
_nav_trail_lbl = _nav_trail_btn = _nav_reset_lbl = _nav_share_lbl = _nav_share_btn = _nav_tb_btn = nullptr; _nav_trail_lbl = _nav_trail_btn = _nav_reset_lbl = _nav_share_lbl = _nav_share_btn = _nav_tb_btn = nullptr;
@@ -755,14 +756,16 @@ void UITask::shareToMessage(const char* text) {
showToast("Pick a conversation to share it in"); showToast("Pick a conversation to share it in");
} }
// ── Tools panel: trail recording, live share, arrival alert, map download, // ── Tools panel: trail recording, live share, arrival alert, each with its
// and their options (the schema's PG_NAV page) -- all of it lives with the map. // options (a section of the schema's PG_NAV page); offline maps; layers --
// all of it lives with the map.
namespace navmap { namespace navmap {
enum : uint8_t { TL_TRAIL_TOGGLE, TL_TRAIL_SAVE, TL_TRAIL_LOAD, TL_TRAIL_RESET, TL_TRAIL_GPX, TL_TRACKBACK, enum : uint8_t { TL_TRAIL_TOGGLE, TL_TRAIL_SAVE, TL_TRAIL_LOAD, TL_TRAIL_RESET, TL_TRAIL_GPX, TL_TRACKBACK,
TL_SHARE_TOGGLE, TL_SHARE_ONCE, TL_DOWNLOAD, TL_AREAS, TL_REGIONS, TL_OPTIONS, TL_SHARE_TOGGLE, TL_SHARE_ONCE, TL_DOWNLOAD, TL_AREAS, TL_REGIONS,
TL_WP_HERE, TL_WP_COORDS, TL_SPOT_ADD, TL_SPOT_GO, TL_WP_HERE, TL_WP_COORDS, TL_SPOT_ADD, TL_SPOT_GO,
TL_ST_LOAD, TL_ST_GPX, TL_ST_DELETE }; TL_ST_LOAD, TL_ST_GPX, TL_ST_DELETE,
TL_OPT_TRAIL, TL_OPT_SHARE, TL_OPT_ALERT };
// Live-share targets offered in the dropdown: channels, then favourite contacts. // Live-share targets offered in the dropdown: channels, then favourite contacts.
static const int SHARE_TARGETS = MAX_GROUP_CHANNELS + 16; static const int SHARE_TARGETS = MAX_GROUP_CHANNELS + 16;
@@ -907,6 +910,8 @@ void UITask::navToolsPopup() {
_nav_tb_btn = toolButton(r, "", navmap::TL_TRACKBACK, false); _nav_tb_btn = toolButton(r, "", navmap::TL_TRACKBACK, false);
toolButton(r, LV_SYMBOL_SD_CARD " GPX", navmap::TL_TRAIL_GPX, false); toolButton(r, LV_SYMBOL_SD_CARD " GPX", navmap::TL_TRAIL_GPX, false);
_nav_reset_lbl = toolButton(r, LV_SYMBOL_TRASH " Reset", navmap::TL_TRAIL_RESET, false); _nav_reset_lbl = toolButton(r, LV_SYMBOL_TRASH " Reset", navmap::TL_TRAIL_RESET, false);
listRow(group(list, nullptr), "Trail options", "Point spacing, auto-pause, low battery", onNavTool,
(void*)(uintptr_t)navmap::TL_OPT_TRAIL);
sectionTitle(list, "LIVE SHARE"); sectionTitle(list, "LIVE SHARE");
_nav_share_lbl = label(list, "", THEME_FONT_SMALL, theme::TEXT); _nav_share_lbl = label(list, "", THEME_FONT_SMALL, theme::TEXT);
@@ -942,34 +947,36 @@ void UITask::navToolsPopup() {
_nav_share_btn = toolButton(r, "", navmap::TL_SHARE_TOGGLE, true); _nav_share_btn = toolButton(r, "", navmap::TL_SHARE_TOGGLE, true);
toolButton(r, LV_SYMBOL_UPLOAD " Send once", navmap::TL_SHARE_ONCE, false); toolButton(r, LV_SYMBOL_UPLOAD " Send once", navmap::TL_SHARE_ONCE, false);
sectionTitle(list, "ARRIVAL ALERT"); listRow(group(list, nullptr), "Share options", "How often, for how long", onNavTool, (void*)(uintptr_t)navmap::TL_OPT_SHARE);
char al[64], rad[12];
lv_obj_t* g = group(list, "ARRIVAL ALERT");
schemaRow(g, SETTING(locator_enabled));
char al[48], rad[12];
uint16_t m = NodePrefs::locatorRadiusMeters(_prefs->locator_radius_idx); uint16_t m = NodePrefs::locatorRadiusMeters(_prefs->locator_radius_idx);
geo::fmtDist(rad, sizeof(rad), m / 1000.0f, _prefs->units_imperial); geo::fmtDist(rad, sizeof(rad), m / 1000.0f, _prefs->units_imperial);
if (_prefs->locator_enabled) snprintf(al, sizeof(al), "On - %s, %s radius", NodePrefs::locatorModeLabel(_prefs->locator_mode), rad); snprintf(al, sizeof(al), "%s radius, %s", rad, NodePrefs::locatorModeLabel(_prefs->locator_mode));
else snprintf(al, sizeof(al), "Off"); listRow(g, "Alert options", al, onNavTool, (void*)(uintptr_t)navmap::TL_OPT_ALERT);
label(list, al, THEME_FONT_SMALL, theme::TEXT);
lv_obj_t* g = group(list, "MAP"); g = group(list, "OFFLINE MAPS");
char ar[48];
int na = mapview::s_areas.count();
snprintf(ar, sizeof(ar), na ? "%d on the card - new, rename, refresh, delete" : "None yet - pick one with a frame", na);
listRow(g, "Map areas", ar, onNavTool, (void*)(uintptr_t)navmap::TL_AREAS);
lv_obj_t* lsw = switchRow(g, "Live tiles", "Load missing tiles over WiFi", nullptr); lv_obj_t* lsw = switchRow(g, "Live tiles", "Load missing tiles over WiFi", nullptr);
if (lvport::liveTiles()) lv_obj_add_state(lsw, LV_STATE_CHECKED); if (lvport::liveTiles()) lv_obj_add_state(lsw, LV_STATE_CHECKED);
lv_obj_add_event_cb(lsw, onLiveTiles, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_add_event_cb(lsw, onLiveTiles, LV_EVENT_VALUE_CHANGED, NULL);
g = group(list, "LAYERS");
lv_obj_t* tsw = switchRow(g, "Hiking trails", "Marked routes in their colours", nullptr); lv_obj_t* tsw = switchRow(g, "Hiking trails", "Marked routes in their colours", nullptr);
if (lvport::trailsOn()) lv_obj_add_state(tsw, LV_STATE_CHECKED); if (lvport::trailsOn()) lv_obj_add_state(tsw, LV_STATE_CHECKED);
lv_obj_add_event_cb(tsw, onTrails, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_add_event_cb(tsw, onTrails, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_t* vsw = switchRow(g, "Vector map", "Drawn on the device: trails, contours", nullptr); lv_obj_t* vsw = switchRow(g, "Vector map (test)", "Drawn on the device: trails, contours", nullptr);
if (lvport::vectorOn()) lv_obj_add_state(vsw, LV_STATE_CHECKED); if (lvport::vectorOn()) lv_obj_add_state(vsw, LV_STATE_CHECKED);
lv_obj_add_event_cb(vsw, onVectorMap, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_add_event_cb(vsw, onVectorMap, LV_EVENT_VALUE_CHANGED, NULL);
listRow(g, "Download an area", "Pick it with a frame, for use offline", onNavTool, (void*)(uintptr_t)navmap::TL_DOWNLOAD);
char ar[40];
int na = mapview::s_areas.count();
snprintf(ar, sizeof(ar), na ? "%d on the card - rename, refresh, delete" : "None yet", na);
listRow(g, "Map areas", ar, onNavTool, (void*)(uintptr_t)navmap::TL_AREAS);
char vr[48]; char vr[48];
int nv = mapview::s_vpacks.count(); int nv = mapview::s_vpacks.count();
snprintf(vr, sizeof(vr), nv ? "%d on the card" : "None - packs go in /vmap", nv); snprintf(vr, sizeof(vr), nv ? "%d on the card" : "None - packs go in /vmap", nv);
listRow(g, "Vector regions", vr, onNavTool, (void*)(uintptr_t)navmap::TL_REGIONS); listRow(g, "Vector regions", vr, onNavTool, (void*)(uintptr_t)navmap::TL_REGIONS);
listRow(g, "Map options", "Trail, live sharing, arrival alert", onNavTool, (void*)(uintptr_t)navmap::TL_OPTIONS);
_nav_reset_armed_ms = 0; _nav_reset_armed_ms = 0;
refreshNavTools(); refreshNavTools();
@@ -1266,8 +1273,11 @@ void UITask::navToolAction(uint8_t act) {
case navmap::TL_REGIONS: case navmap::TL_REGIONS:
mapRegionsPopup(); mapRegionsPopup();
return; return;
case navmap::TL_OPTIONS: // back returns to the map case navmap::TL_OPT_TRAIL: // one section of the Map options; back returns to the map
showSchemaSettings(settings::PG_NAV); case navmap::TL_OPT_SHARE:
case navmap::TL_OPT_ALERT:
showMapOptions(act == navmap::TL_OPT_TRAIL ? settings::SEC_TRAIL
: act == navmap::TL_OPT_SHARE ? settings::SEC_LIVE_SHARE : settings::SEC_LOCATOR);
return; return;
case navmap::TL_WP_HERE: case navmap::TL_WP_HERE:
navClosePopup(); navClosePopup();
+30 -5
View File
@@ -163,7 +163,9 @@ static lv_obj_t* dimOverlay(lv_obj_t* parent) {
// POP_FIT: as tall as its content, centred below the status bar, scrolling // POP_FIT: as tall as its content, centred below the status bar, scrolling
// past that height. POP_FULL: all of that height; its own list scrolls. // past that height. POP_FULL: all of that height; its own list scrolls.
// POP_TOP: as tall as its content, under the status bar, clear of a keyboard. // POP_TOP: as tall as its content, under the status bar, clear of a keyboard.
enum PopFit : uint8_t { POP_FIT, POP_FULL, POP_TOP }; // POP_BOTTOM: as tall as its content, at the bottom, nothing dimmed -- for
// what's shown on the screen above it (a map area's preview).
enum PopFit : uint8_t { POP_FIT, POP_FULL, POP_TOP, POP_BOTTOM };
static lv_obj_t* popupOpen(lv_obj_t* parent, PopFit fit, lv_obj_t*& overlay, int32_t inset = 8) { static lv_obj_t* popupOpen(lv_obj_t* parent, PopFit fit, lv_obj_t*& overlay, int32_t inset = 8) {
overlay = dimOverlay(parent); overlay = dimOverlay(parent);
lv_obj_t* panel = lv_obj_create(overlay); lv_obj_t* panel = lv_obj_create(overlay);
@@ -175,6 +177,9 @@ static lv_obj_t* popupOpen(lv_obj_t* parent, PopFit fit, lv_obj_t*& overlay, int
} else if (fit == POP_TOP) { } else if (fit == POP_TOP) {
lv_obj_set_height(panel, LV_SIZE_CONTENT); lv_obj_set_height(panel, LV_SIZE_CONTENT);
lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4); lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4);
} else if (fit == POP_BOTTOM) {
lv_obj_set_height(panel, LV_SIZE_CONTENT);
lv_obj_align(panel, LV_ALIGN_BOTTOM_MID, 0, -inset);
} else { } else {
lv_obj_set_height(panel, LV_SIZE_CONTENT); lv_obj_set_height(panel, LV_SIZE_CONTENT);
lv_obj_set_style_max_height(panel, h - theme::STATUS_H - 12, 0); lv_obj_set_style_max_height(panel, h - theme::STATUS_H - 12, 0);
@@ -189,7 +194,7 @@ static lv_obj_t* popupOpen(lv_obj_t* parent, PopFit fit, lv_obj_t*& overlay, int
lv_obj_set_style_pad_all(panel, theme::PAD, 0); lv_obj_set_style_pad_all(panel, theme::PAD, 0);
lv_obj_set_style_pad_row(panel, 4, 0); lv_obj_set_style_pad_row(panel, 4, 0);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN);
anim::popup(overlay); anim::popup(overlay, fit == POP_BOTTOM ? LV_OPA_TRANSP : LV_OPA_60);
return panel; return panel;
} }
@@ -598,6 +603,7 @@ static const char* settingText(const NodePrefs& p, int idx, char* buf, int n) {
} }
static lv_obj_t* s_sec_card[settings::SEC_COUNT]; // schemaRows()' groups, by section static lv_obj_t* s_sec_card[settings::SEC_COUNT]; // schemaRows()' groups, by section
static bool s_opts_from_map = false; // Map options opened from the map: back returns there static bool s_opts_from_map = false; // Map options opened from the map: back returns there
static int s_nav_section = -1; // Map options: one section of them (from the map), -1 all
static int32_t s_settings_y = 0; // Settings' scroll, kept while a page of it is open static int32_t s_settings_y = 0; // Settings' scroll, kept while a page of it is open
// A one-of-N choice: a row (or grid, with "\n" in the map) of checkable // A one-of-N choice: a row (or grid, with "\n" in the map) of checkable
@@ -1674,7 +1680,12 @@ void UITask::back() {
case SCR_ADMIN: if (_nav_overlay) navClosePopup(); else adminLeave(); break; case SCR_ADMIN: if (_nav_overlay) navClosePopup(); else adminLeave(); break;
case SCR_SETTINGS: if (_nav_overlay) navClosePopup(); else showHome(); break; case SCR_SETTINGS: if (_nav_overlay) navClosePopup(); else showHome(); break;
case SCR_SETTINGS_NAV: // the map's options go back to the map case SCR_SETTINGS_NAV: // the map's options go back to the map
if (_settings_page == settings::PG_NAV && s_opts_from_map) openMap(true); else showSettings(); if (_settings_page == settings::PG_NAV && s_opts_from_map) {
openMap(true);
if (s_nav_section >= 0) navToolsPopup(); // one section's options: back into Map tools
} else {
showSettings();
}
break; break;
case SCR_QUICK: // back to Messages & contacts' bottom, where the row is case SCR_QUICK: // back to Messages & contacts' bottom, where the row is
if (_nav_overlay) { navClosePopup(); break; } if (_nav_overlay) { navClosePopup(); break; }
@@ -3075,7 +3086,10 @@ static void onSchemaDropdown(lv_event_t* e) {
s_ui->setSchemaValue((int)(uintptr_t)lv_event_get_user_data(e), s_ui->setSchemaValue((int)(uintptr_t)lv_event_get_user_data(e),
choiceSelected((lv_obj_t*)lv_event_get_target(e))); choiceSelected((lv_obj_t*)lv_event_get_target(e)));
} }
static void onOpenSchemaPage(lv_event_t* e) { s_ui->showSchemaSettings((int)(uintptr_t)lv_event_get_user_data(e)); } static void onOpenSchemaPage(lv_event_t* e) {
s_nav_section = -1; // from Settings: all of a page
s_ui->showSchemaSettings((int)(uintptr_t)lv_event_get_user_data(e));
}
static void onHomeApps(lv_event_t* e) { (void)e; s_ui->homeEdit(true); } static void onHomeApps(lv_event_t* e) { (void)e; s_ui->homeEdit(true); }
static void onPruneContacts(lv_event_t* e) { (void)e; s_ui->pruneContacts(); } static void onPruneContacts(lv_event_t* e) { (void)e; s_ui->pruneContacts(); }
static void onOpenQuickMsgs(lv_event_t* e); static void onOpenQuickMsgs(lv_event_t* e);
@@ -3087,6 +3101,12 @@ static void onVolumeSlider(lv_event_t* e) {
} }
// Map tools > Trail / Share / Alert options: that section of the Map options.
void UITask::showMapOptions(uint8_t section) {
s_nav_section = section;
showSchemaSettings(settings::PG_NAV);
}
void UITask::showSchemaSettings(int page) { void UITask::showSchemaSettings(int page) {
if (page == settings::PG_NAV && !_nav_back) s_opts_from_map = _screen == SCR_MAP; if (page == settings::PG_NAV && !_nav_back) s_opts_from_map = _screen == SCR_MAP;
_screen = SCR_SETTINGS_NAV; _screen = SCR_SETTINGS_NAV;
@@ -3146,6 +3166,9 @@ void UITask::setTapWake(bool on) {
void UITask::buildSchemaSettings() { void UITask::buildSchemaSettings() {
static const char* const OWN[] = { "Keyboard", "About", "Display", "Power", "Time" }; static const char* const OWN[] = { "Keyboard", "About", "Display", "Power", "Time" };
const char* title = _settings_page >= PG_KEYBOARD ? OWN[_settings_page - PG_KEYBOARD] : settings::pageTitle(_settings_page); const char* title = _settings_page >= PG_KEYBOARD ? OWN[_settings_page - PG_KEYBOARD] : settings::pageTitle(_settings_page);
if (_settings_page == settings::PG_NAV && s_nav_section >= 0)
title = s_nav_section == settings::SEC_TRAIL ? "Trail options"
: s_nav_section == settings::SEC_LIVE_SHARE ? "Share options" : "Alert options";
lv_obj_t* body = newScreen(title, true); lv_obj_t* body = newScreen(title, true);
_prune_lbl = nullptr; _prune_lbl = nullptr;
_prune_armed_ms = 0; _prune_armed_ms = 0;
@@ -3229,9 +3252,11 @@ void UITask::schemaRows(lv_obj_t* body, uint8_t page) {
for (int i = 0; i < settings::COUNT; i++) { for (int i = 0; i < settings::COUNT; i++) {
const settings::Setting& st = settings::ALL[i]; const settings::Setting& st = settings::ALL[i];
if (settings::sectionPage(st.section) != page) continue; if (settings::sectionPage(st.section) != page) continue;
bool one = page == settings::PG_NAV && s_nav_section >= 0; // one section: its own page, no heading
if (one && st.section != s_nav_section) continue;
if (st.section != sec) { if (st.section != sec) {
sec = st.section; sec = st.section;
card = s_sec_card[sec] = group(body, settings::sectionTitle(sec)); card = s_sec_card[sec] = group(body, one ? nullptr : settings::sectionTitle(sec));
if (sec == settings::SEC_SOUND) buildSoundRows(card, true); // On / Off / Auto if (sec == settings::SEC_SOUND) buildSoundRows(card, true); // On / Off / Auto
} }
schemaRow(card, i); schemaRow(card, i);
+3 -1
View File
@@ -88,6 +88,7 @@ public:
void mapRegionPopup(int idx); void mapRegionPopup(int idx);
void mapRegionAction(uint8_t act); void mapRegionAction(uint8_t act);
void mapAreaRenameDone(bool ok); void mapAreaRenameDone(bool ok);
void areaPreviewEnd(); // back to the view from before a map area's preview
int mapZoomLevel() const; int mapZoomLevel() const;
void setTapWake(bool on); void setTapWake(bool on);
void mapDownloadClose(); void mapDownloadClose();
@@ -317,6 +318,7 @@ private:
void buildSettings(); void buildSettings();
void buildSchemaSettings(); void buildSchemaSettings();
void schemaRows(lv_obj_t* body, uint8_t page); void schemaRows(lv_obj_t* body, uint8_t page);
void showMapOptions(uint8_t section);
void schemaRow(lv_obj_t* card, int idx); void schemaRow(lv_obj_t* card, int idx);
void buildKeyboardPage(lv_obj_t* body); void buildKeyboardPage(lv_obj_t* body);
void buildAboutPage(lv_obj_t* body); void buildAboutPage(lv_obj_t* body);
@@ -347,7 +349,7 @@ private:
void refreshNavBar(); void refreshNavBar();
void navFrameTarget(); void navFrameTarget();
void navSetTarget(uint8_t kind, const uint8_t* key, int32_t lat, int32_t lon, const char* name); void navSetTarget(uint8_t kind, const uint8_t* key, int32_t lat, int32_t lon, const char* name);
lv_obj_t* navPopupPanel(const char* title, bool full); lv_obj_t* navPopupPanel(const char* title, bool full, bool bottom = false);
void navRenamePopup(int idx); void navRenamePopup(int idx);
void navAddWaypoint(int32_t lat, int32_t lon); void navAddWaypoint(int32_t lat, int32_t lon);
void navDropAt(int x, int y); void navDropAt(int x, int y);