mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 03:06:39 +00:00
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:
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user