From bb94a6f81f12afef0fea435494b3897ba2e44777 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Fri, 22 May 2026 23:33:06 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20home=20screen=20ordering=20=E2=80=94=20?= =?UTF-8?q?reorder=20all=20screens=20from=20Settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NodePrefs: add page_order[11] (1-indexed bit-index, 0=end/legacy). DataStore: persist page_order appended after use_lemon_font. UITask/HomeScreen: - bitToPage(): maps stable bit indices 0-10 to HomePage enum values - buildVisibleOrder(): respects page_order when initialised, else falls back to default enum sequence - navPage() and navigation dots use buildVisibleOrder() SettingsScreen Home Pages section: - Add Settings (bit 9) and Messages (bit 10) — show as "always" - LEFT/RIGHT on any home page item moves it earlier/later in order - ENTER toggles ON/OFF for pages that can be disabled - Position number shown before label once custom order is active - ensurePageOrderInit() seeds default order on first reorder action Co-Authored-By: Claude Sonnet 4.6 --- examples/companion_radio/DataStore.cpp | 4 + examples/companion_radio/NodePrefs.h | 3 + .../companion_radio/ui-new/SettingsScreen.h | 144 +++++++++++++++--- examples/companion_radio/ui-new/UITask.cpp | 79 +++++++--- 4 files changed, 191 insertions(+), 39 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index afb9c748..e879dba7 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -289,6 +289,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.clock_12h, sizeof(_prefs.clock_12h)); if (file.available()) { file.read((uint8_t *)&_prefs.use_lemon_font, sizeof(_prefs.use_lemon_font)); + if (file.available()) { + file.read((uint8_t *)_prefs.page_order, sizeof(_prefs.page_order)); + } } } } @@ -383,6 +386,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.auto_lock, sizeof(_prefs.auto_lock)); file.write((uint8_t *)&_prefs.clock_12h, sizeof(_prefs.clock_12h)); file.write((uint8_t *)&_prefs.use_lemon_font, sizeof(_prefs.use_lemon_font)); + file.write((uint8_t *)_prefs.page_order, sizeof(_prefs.page_order)); file.close(); } diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 4f5b878a..086b70a4 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -79,4 +79,7 @@ struct NodePrefs { // persisted to file static const int DM_MELODY_TABLE_MAX = 16; DmMelodyEntry dm_melody[DM_MELODY_TABLE_MAX]; uint8_t use_lemon_font; // 0=default Adafruit font, 1=Lemon font (Unicode, pixel-accurate wrap) + // Home screen page order: each byte = bit-index+1 (see HP_* bit positions + 9=Settings, 10=Messages). + // 0 = end of list (also uninitialized legacy). hasCustomOrder iff page_order[0] in 1..11. + uint8_t page_order[11]; }; \ No newline at end of file diff --git a/examples/companion_radio/ui-new/SettingsScreen.h b/examples/companion_radio/ui-new/SettingsScreen.h index 5102c8b1..27fc45cb 100644 --- a/examples/companion_radio/ui-new/SettingsScreen.h +++ b/examples/companion_radio/ui-new/SettingsScreen.h @@ -30,6 +30,7 @@ class SettingsScreen : public UIScreen { #if UI_SENSORS_PAGE == 1 HOME_SENSORS, #endif + HOME_SETTINGS, HOME_QUICK_MSG, HOME_TOOLS, HOME_SHUTDOWN, // Radio section SECTION_RADIO, @@ -137,9 +138,9 @@ class SettingsScreen : public UIScreen { } bool isHomePage(int item) const { - return item == HOME_CLOCK || item == HOME_RECENT || item == HOME_RADIO || - item == HOME_BT || item == HOME_ADVERT || item == HOME_TOOLS || - item == HOME_SHUTDOWN + return item == HOME_CLOCK || item == HOME_RECENT || item == HOME_RADIO || + item == HOME_BT || item == HOME_ADVERT || item == HOME_TOOLS || + item == HOME_SHUTDOWN || item == HOME_SETTINGS || item == HOME_QUICK_MSG #if ENV_INCLUDE_GPS == 1 || item == HOME_GPS #endif @@ -155,41 +156,126 @@ class SettingsScreen : public UIScreen { if (item == HOME_RADIO) return HP_RADIO; if (item == HOME_BT) return HP_BLUETOOTH; if (item == HOME_ADVERT) return HP_ADVERT; - if (item == HOME_TOOLS) return HP_TOOLS; - if (item == HOME_SHUTDOWN) return HP_SHUTDOWN; + if (item == HOME_TOOLS) return HP_TOOLS; + if (item == HOME_SHUTDOWN) return HP_SHUTDOWN; + // HOME_SETTINGS and HOME_QUICK_MSG have no mask bit — always visible #if ENV_INCLUDE_GPS == 1 - if (item == HOME_GPS) return HP_GPS; + if (item == HOME_GPS) return HP_GPS; #endif #if UI_SENSORS_PAGE == 1 - if (item == HOME_SENSORS) return HP_SENSORS; + if (item == HOME_SENSORS) return HP_SENSORS; #endif return 0; } const char* homePageLabel(int item) const { - if (item == HOME_CLOCK) return "Clock"; - if (item == HOME_RECENT) return "Recent"; - if (item == HOME_RADIO) return "Radio"; - if (item == HOME_BT) return "Bluetooth"; - if (item == HOME_ADVERT) return "Advert"; - if (item == HOME_TOOLS) return "Tools"; - if (item == HOME_SHUTDOWN) return "Shutdown"; + if (item == HOME_CLOCK) return "Clock"; + if (item == HOME_RECENT) return "Recent"; + if (item == HOME_RADIO) return "Radio"; + if (item == HOME_BT) return "Bluetooth"; + if (item == HOME_ADVERT) return "Advert"; + if (item == HOME_TOOLS) return "Tools"; + if (item == HOME_SHUTDOWN) return "Shutdown"; + if (item == HOME_SETTINGS) return "Settings"; + if (item == HOME_QUICK_MSG) return "Messages"; #if ENV_INCLUDE_GPS == 1 - if (item == HOME_GPS) return "GPS"; + if (item == HOME_GPS) return "GPS"; #endif #if UI_SENSORS_PAGE == 1 - if (item == HOME_SENSORS) return "Sensors"; + if (item == HOME_SENSORS) return "Sensors"; #endif return ""; } bool homePageVisible(int item, const NodePrefs* p) const { + if (item == HOME_SETTINGS || item == HOME_QUICK_MSG) return true; uint16_t bit = homePageBit(item); if (!bit) return false; uint16_t mask = (p && p->home_pages_mask) ? p->home_pages_mask : HP_ALL; return (mask & bit) != 0; } + bool homePageToggleable(int item) const { + return item != HOME_SETTINGS && item != HOME_QUICK_MSG; + } + + // Returns the bit-index (0-10) used in page_order for this SettingItem, or -1. + int homePageBitIndex(int item) const { + if (item == HOME_CLOCK) return 0; + if (item == HOME_RECENT) return 1; + if (item == HOME_RADIO) return 2; + if (item == HOME_BT) return 3; + if (item == HOME_ADVERT) return 4; +#if ENV_INCLUDE_GPS == 1 + if (item == HOME_GPS) return 5; +#endif +#if UI_SENSORS_PAGE == 1 + if (item == HOME_SENSORS) return 6; +#endif + if (item == HOME_TOOLS) return 7; + if (item == HOME_SHUTDOWN) return 8; + if (item == HOME_SETTINGS) return 9; + if (item == HOME_QUICK_MSG) return 10; + return -1; + } + + // Returns 1-based position of item in page_order, or 0 if no custom order / not found. + int homePagePosition(int item, const NodePrefs* p) const { + if (!p || p->page_order[0] < 1 || p->page_order[0] > 11) return 0; + int bit = homePageBitIndex(item); + if (bit < 0) return 0; + for (int i = 0; i < 11; i++) { + uint8_t v = p->page_order[i]; + if (v < 1 || v > 11) break; + if ((int)(v - 1) == bit) return i + 1; + } + return 0; + } + + // Initialises page_order to the default display sequence if not already set. + void ensurePageOrderInit(NodePrefs* p) const { + if (!p) return; + if (p->page_order[0] >= 1 && p->page_order[0] <= 11) return; + // Default: CLOCK RECENT RADIO BT ADVERT [GPS] [SENSORS] SETTINGS TOOLS MESSAGES SHUTDOWN + int j = 0; + p->page_order[j++] = 0 + 1; // CLOCK + p->page_order[j++] = 1 + 1; // RECENT + p->page_order[j++] = 2 + 1; // RADIO + p->page_order[j++] = 3 + 1; // BLUETOOTH + p->page_order[j++] = 4 + 1; // ADVERT +#if ENV_INCLUDE_GPS == 1 + p->page_order[j++] = 5 + 1; // GPS +#endif +#if UI_SENSORS_PAGE == 1 + p->page_order[j++] = 6 + 1; // SENSORS +#endif + p->page_order[j++] = 9 + 1; // SETTINGS + p->page_order[j++] = 7 + 1; // TOOLS + p->page_order[j++] = 10 + 1; // MESSAGES (QUICK_MSG) + p->page_order[j++] = 8 + 1; // SHUTDOWN + while (j < 11) p->page_order[j++] = 0; + } + + // Swaps item's page_order slot with its neighbour in the given direction (-1=earlier, +1=later). + void movePageInOrder(int item, int delta, NodePrefs* p) { + ensurePageOrderInit(p); + int bit = homePageBitIndex(item); + if (bit < 0) return; + int cur = -1, total = 0; + for (int i = 0; i < 11; i++) { + uint8_t v = p->page_order[i]; + if (v < 1 || v > 11) break; + if ((int)(v - 1) == bit) cur = i; + total++; + } + if (cur < 0) return; + int next = cur + delta; + if (next < 0 || next >= total) return; + uint8_t tmp = p->page_order[cur]; + p->page_order[cur] = p->page_order[next]; + p->page_order[next] = tmp; + } + bool isMsgSlot(int item) const { return item >= MSG_SLOT_0 && item <= MSG_SLOT_9; } @@ -269,9 +355,17 @@ class SettingsScreen : public UIScreen { uint8_t v = p ? p->notif_melody_ch : 0; display.print(L[v < 3 ? v : 0]); } } else if (isHomePage(item)) { + int pos = homePagePosition(item, p); + if (pos > 0) { + char pb[4]; snprintf(pb, sizeof(pb), "%d ", pos); + display.print(pb); + } display.print(homePageLabel(item)); display.setCursor(VAL_X, y); - display.print(homePageVisible(item, p) ? "ON" : "OFF"); + if (!homePageToggleable(item)) + display.print("always"); + else + display.print(homePageVisible(item, p) ? "ON" : "OFF"); } else if (item == TX_POWER) { display.print("TX Pwr"); char buf[8]; @@ -452,10 +546,18 @@ public: p->notif_melody_ch = (p->notif_melody_ch + (left ? 2 : 1)) % 3; _dirty = true; return true; } - if (isHomePage(_selected) && p && (left || right || enter)) { - p->home_pages_mask ^= homePageBit(_selected); - _dirty = true; - return true; + if (isHomePage(_selected) && p) { + if (left || right) { + movePageInOrder(_selected, left ? -1 : 1, p); + _dirty = true; + return true; + } + if (enter && homePageToggleable(_selected)) { + p->home_pages_mask ^= homePageBit(_selected); + _dirty = true; + return true; + } + return enter; } if (_selected == TX_POWER && p) { if (right && p->tx_power_dbm < 22) { p->tx_power_dbm++; _task->applyTxPower(); _dirty = true; return true; } diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index ecf78738..20a617a0 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -174,6 +174,35 @@ class HomeScreen : public UIScreen { return -1; // SETTINGS, QUICK_MSG always visible } + // Maps page_order bit-index (0-10) back to the HomePage enum value for this build. + // Returns -1 if the page is not compiled in. + int bitToPage(int bit) const { + switch (bit) { + case 0: return CLOCK; + case 1: return RECENT; + case 2: return RADIO; + case 3: return BLUETOOTH; + case 4: return ADVERT; + case 5: +#if ENV_INCLUDE_GPS == 1 + return GPS; +#else + return -1; +#endif + case 6: +#if UI_SENSORS_PAGE == 1 + return SENSORS; +#else + return -1; +#endif + case 7: return TOOLS; + case 8: return SHUTDOWN; + case 9: return SETTINGS; + case 10: return QUICK_MSG; + default: return -1; + } + } + bool isPageVisible(int page) const { int bit = pageBit(page); if (bit < 0) return true; @@ -181,12 +210,31 @@ class HomeScreen : public UIScreen { return (mask >> bit) & 1; } - int navPage(int from, int dir) const { - for (int i = 1; i < (int)Count; i++) { - int next = ((from + dir * i) % (int)Count + (int)Count) % (int)Count; - if (isPageVisible(next)) return next; + // Build ordered list of all visible pages, respecting page_order when set. + // Returns count; out[] receives HomePage enum values. + int buildVisibleOrder(int* out) const { + int n = 0; + bool custom = _node_prefs && _node_prefs->page_order[0] >= 1 && _node_prefs->page_order[0] <= 11; + if (custom) { + for (int i = 0; i < 11; i++) { + uint8_t v = _node_prefs->page_order[i]; + if (v < 1 || v > 11) break; + int pg = bitToPage(v - 1); + if (pg >= 0 && pg < (int)Count && isPageVisible(pg)) out[n++] = pg; + } + } else { + for (int pg = 0; pg < (int)Count; pg++) + if (isPageVisible(pg)) out[n++] = pg; } - return from; + return n; + } + + int navPage(int from, int dir) const { + int order[11]; int n = buildVisibleOrder(order); + if (n == 0) return from; + int cur = 0; + for (int i = 0; i < n; i++) if (order[i] == from) { cur = i; break; } + return order[((cur + dir) % n + n) % n]; } int renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { @@ -354,20 +402,15 @@ public: // curr page indicator — hidden on CLOCK page (full screen used for dashboard) if (_page != CLOCK) { - int vis_count = 0, curr_vis = 0; - for (int i = 0; i < (int)Count; i++) { - if (!isPageVisible(i)) continue; - if (i == _page) curr_vis = vis_count; - vis_count++; - } + int order[11]; int n = buildVisibleOrder(order); + int curr_vis = 0; + for (int i = 0; i < n; i++) if (order[i] == _page) { curr_vis = i; break; } int y = 14; - int x = display.width() / 2 - 5 * (vis_count - 1); - int vi = 0; - for (int i = 0; i < (int)Count; i++) { - if (!isPageVisible(i)) continue; - if (vi == curr_vis) display.fillRect(x-1, y-1, 3, 3); - else display.fillRect(x, y, 1, 1); - x += 10; vi++; + int x = display.width() / 2 - 5 * (n - 1); + for (int i = 0; i < n; i++) { + if (i == curr_vis) display.fillRect(x-1, y-1, 3, 3); + else display.fillRect(x, y, 1, 1); + x += 10; } }