diff --git a/examples/companion_radio/ui-lvgl/MapAreas.h b/examples/companion_radio/ui-lvgl/MapAreas.h index cb79e52c..14a19600 100644 --- a/examples/companion_radio/ui-lvgl/MapAreas.h +++ b/examples/companion_radio/ui-lvgl/MapAreas.h @@ -1,7 +1,8 @@ #pragma once // 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, -// refresh, trails on / off, delete. +// list of downloaded areas (map/AreaStore.h). Picking one frames it on the +// 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 // pans and zooms under it as usual, so an area can be larger than the screen. @@ -14,7 +15,7 @@ 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 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_zoom = 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 lv_obj_t* s_del_lbl = nullptr; 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 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 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 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; } -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_set_height(b, 36); + lv_obj_set_height(b, 46); 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_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_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_center(l); + lv_obj_align(l, LV_ALIGN_BOTTOM_MID, 0, -2); return l; } @@ -358,6 +367,14 @@ void UITask::mapAreasPopup() { snprintf(t, sizeof(t), "Deleting... %lu files", (unsigned long)mapview::s_areas.deleted()); 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) { lv_obj_t* g = group(list, nullptr); for (int i = n - 1; i >= 0; i--) { // newest first @@ -386,28 +403,52 @@ void UITask::mapAreasPopup() { void UITask::mapAreaPopup(int idx) { using namespace areas; if (idx < 0 || idx >= mapview::s_areas.count()) return; - s_idx = idx; const mapview::MapArea& m = mapview::s_areas.at(idx); - lv_obj_t* panel = navPopupPanel(m.name, false); - char info[128], date[16]; + lv_obj_t* panel = navPopupPanel(m.name, false, true); // ends an earlier preview first + s_idx = idx; + char info[96], date[16]; fmtDate(date, sizeof(date), m.created, _prefs); 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_COMPLETE) ? "Downloaded" : "Unfinished - Fill gaps completes it"); - lv_obj_t* l = label(panel, info, THEME_FONT_SMALL, theme::TEXT); + (m.flags & mapview::AreaStore::F_COMPLETE) ? "" : "\nUnfinished - Fill gaps completes it"); + lv_obj_t* l = label(panel, info, THEME_FONT_SMALL, theme::TEXT_MUTED); lv_label_set_long_mode(l, LV_LABEL_LONG_WRAP); lv_obj_set_width(l, LV_PCT(100)); lv_obj_t* r = toolRow(panel); - actButton(r, UI_SYMBOL_MAP " Show", A_SHOW, true); - actButton(r, LV_SYMBOL_EDIT " Rename", A_RENAME, false); - r = toolRow(panel); - actButton(r, LV_SYMBOL_DOWNLOAD " Fill gaps", A_FILL, false); - actButton(r, LV_SYMBOL_REFRESH " Refresh", A_REFRESH, false); - 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); + actButton(r, LV_SYMBOL_EDIT, "Rename", A_RENAME); + actButton(r, LV_SYMBOL_DOWNLOAD, "Fill", A_FILL); + actButton(r, LV_SYMBOL_REFRESH, "Refresh", A_REFRESH); + actButton(r, (m.flags & mapview::AreaStore::F_TRAILS) ? LV_SYMBOL_MINUS : LV_SYMBOL_PLUS, "Trails", A_TRAILS); + s_del_lbl = actButton(r, LV_SYMBOL_TRASH, "Delete", A_DELETE); 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) { @@ -416,20 +457,6 @@ void UITask::mapAreaAction(uint8_t act) { mapview::MapArea m = mapview::s_areas.at(s_idx); bool trails = m.flags & mapview::AreaStore::F_TRAILS; 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: { lv_obj_t* panel = navPopupPanel("Area name", false); lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4); // above the keyboard diff --git a/examples/companion_radio/ui-lvgl/NavMap.h b/examples/companion_radio/ui-lvgl/NavMap.h index 1a11f61d..ce3b48bb 100644 --- a/examples/companion_radio/ui-lvgl/NavMap.h +++ b/examples/companion_radio/ui-lvgl/NavMap.h @@ -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 ──────────────────────────────── -lv_obj_t* UITask::navPopupPanel(const char* title, bool full) { +lv_obj_t* UITask::navPopupPanel(const char* title, bool full, bool bottom) { 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_t* hdr = lv_obj_create(panel); @@ -492,6 +492,7 @@ lv_obj_t* UITask::navPopupPanel(const char* title, bool full) { } 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 _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; @@ -755,14 +756,16 @@ void UITask::shareToMessage(const char* text) { showToast("Pick a conversation to share it in"); } -// ── Tools panel: trail recording, live share, arrival alert, map download, -// and their options (the schema's PG_NAV page) -- all of it lives with the map. +// ── Tools panel: trail recording, live share, arrival alert, each with its +// options (a section of the schema's PG_NAV page); offline maps; layers -- +// all of it lives with the map. namespace navmap { 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_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. 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); 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); + listRow(group(list, nullptr), "Trail options", "Point spacing, auto-pause, low battery", onNavTool, + (void*)(uintptr_t)navmap::TL_OPT_TRAIL); sectionTitle(list, "LIVE SHARE"); _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); toolButton(r, LV_SYMBOL_UPLOAD " Send once", navmap::TL_SHARE_ONCE, false); - sectionTitle(list, "ARRIVAL ALERT"); - char al[64], rad[12]; + listRow(group(list, nullptr), "Share options", "How often, for how long", onNavTool, (void*)(uintptr_t)navmap::TL_OPT_SHARE); + + 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); 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); - else snprintf(al, sizeof(al), "Off"); - label(list, al, THEME_FONT_SMALL, theme::TEXT); + snprintf(al, sizeof(al), "%s radius, %s", rad, NodePrefs::locatorModeLabel(_prefs->locator_mode)); + listRow(g, "Alert options", al, onNavTool, (void*)(uintptr_t)navmap::TL_OPT_ALERT); - 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); if (lvport::liveTiles()) lv_obj_add_state(lsw, LV_STATE_CHECKED); 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); 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_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); 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]; int nv = mapview::s_vpacks.count(); 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, "Map options", "Trail, live sharing, arrival alert", onNavTool, (void*)(uintptr_t)navmap::TL_OPTIONS); _nav_reset_armed_ms = 0; refreshNavTools(); @@ -1266,8 +1273,11 @@ void UITask::navToolAction(uint8_t act) { case navmap::TL_REGIONS: mapRegionsPopup(); return; - case navmap::TL_OPTIONS: // back returns to the map - showSchemaSettings(settings::PG_NAV); + case navmap::TL_OPT_TRAIL: // one section of the Map options; back returns to the map + 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; case navmap::TL_WP_HERE: navClosePopup(); diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index c390f678..05681a3a 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -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 // 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. -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) { overlay = dimOverlay(parent); 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) { lv_obj_set_height(panel, LV_SIZE_CONTENT); 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 { lv_obj_set_height(panel, LV_SIZE_CONTENT); 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_row(panel, 4, 0); 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; } @@ -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 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 // 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_SETTINGS: if (_nav_overlay) navClosePopup(); else showHome(); break; 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; case SCR_QUICK: // back to Messages & contacts' bottom, where the row is 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), 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 onPruneContacts(lv_event_t* e) { (void)e; s_ui->pruneContacts(); } 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) { if (page == settings::PG_NAV && !_nav_back) s_opts_from_map = _screen == SCR_MAP; _screen = SCR_SETTINGS_NAV; @@ -3146,6 +3166,9 @@ void UITask::setTapWake(bool on) { void UITask::buildSchemaSettings() { 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); + 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); _prune_lbl = nullptr; _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++) { const settings::Setting& st = settings::ALL[i]; 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) { 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 } schemaRow(card, i); diff --git a/examples/companion_radio/ui-lvgl/UITask.h b/examples/companion_radio/ui-lvgl/UITask.h index 421d5efc..af80e1d0 100644 --- a/examples/companion_radio/ui-lvgl/UITask.h +++ b/examples/companion_radio/ui-lvgl/UITask.h @@ -88,6 +88,7 @@ public: void mapRegionPopup(int idx); void mapRegionAction(uint8_t act); void mapAreaRenameDone(bool ok); + void areaPreviewEnd(); // back to the view from before a map area's preview int mapZoomLevel() const; void setTapWake(bool on); void mapDownloadClose(); @@ -317,6 +318,7 @@ private: void buildSettings(); void buildSchemaSettings(); void schemaRows(lv_obj_t* body, uint8_t page); + void showMapOptions(uint8_t section); void schemaRow(lv_obj_t* card, int idx); void buildKeyboardPage(lv_obj_t* body); void buildAboutPage(lv_obj_t* body); @@ -347,7 +349,7 @@ private: void refreshNavBar(); void navFrameTarget(); 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 navAddWaypoint(int32_t lat, int32_t lon); void navDropAt(int x, int y);