feat(home-pages): make Map and Shutdown reorderable; v1.22 notes

The Map page had no Settings entry and Shutdown was pinned to the end of the
carousel because page_order held only 11 slots — full in the common
GPS+SENSORS config, so both pages fell back to being appended at nav time and
couldn't be moved. Grow page_order to 13 (== HPB_COUNT) so every page has a
reorderable slot, and add Map + Shutdown to the default order and the
required-append list.

To keep the persisted format backward-compatible (page_order sits mid-record,
not at the tail), the on-disk head stays the original 11 bytes at its existing
offset and the 2 new slots are appended at the file tail, matching the
append-only schema design. Bump SCHEMA_SENTINEL to 0xC0DE0019; on a pre-0x19
save the tail bytes are the old sentinel/EOF, so they're clamped to 0 and
ensurePageOrderInit re-appends Map/Shutdown into the freed slots on first use.

Drop the now-dead Shutdown-eviction path (13 slots fit all pages).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarekZegare4
2026-07-02 12:01:40 +02:00
co-authored by Claude Opus 4.8
parent 088bbe61ba
commit 948076560b
4 changed files with 52 additions and 36 deletions
+15 -8
View File
@@ -125,9 +125,11 @@ struct NodePrefs { // persisted to file
// Home screen page order: each byte = HomePageBit + 1. 0 terminates the list.
// Validity gated by page_order_set magic (see below) — not by entry value range,
// so a junk byte in 1..HPB_COUNT cannot trigger custom-order mode.
// (Array length = HPB_COUNT, declared as literal so the field offset is stable
// across builds that add new HomePageBit entries.)
uint8_t page_order[11];
// Declared as a literal (not HPB_COUNT) so the field offset stays stable across
// builds that add HomePageBit entries. The first PAGE_ORDER_LEN_V1 bytes persist
// at this offset (backward-compatible); the remaining slots live at the file
// tail (see DataStore) so pre-0x0019 saves still load without shifting.
uint8_t page_order[13];
uint8_t joystick_rotation; // 0-3 steps CW; independent of display_rotation
uint8_t eink_full_refresh_every; // index into {0,5,10,20,30}: full refresh every N partials (0=off)
uint8_t page_order_set; // 0xA5 = page_order is user-configured; anything else = use default
@@ -344,7 +346,7 @@ struct NodePrefs { // persisted to file
// adding/removing/reordering fields in DataStore::savePrefs/loadPrefsInt so
// older saves are detected on load and skipped (zero-init defaults kept).
// High 24 bits identify the file format; low byte is the schema revision.
static const uint32_t SCHEMA_SENTINEL = 0xC0DE0018;
static const uint32_t SCHEMA_SENTINEL = 0xC0DE0019;
// Bit-index for each home page. Used by page_order (entries store bit+1) and
// by home_pages_mask. Single source of truth — both HomeScreen::pageBit/bitToPage
@@ -366,10 +368,15 @@ struct NodePrefs { // persisted to file
HPB_COUNT = 13,
};
// Length of the persisted page_order[] array. Stable across firmware versions
// (don't grow without a schema migration). When HPB_COUNT exceeds this, extra
// visible pages are appended at navigation time via buildVisibleOrder fallback.
static const uint8_t PAGE_ORDER_LEN = 11;
// Number of usable page_order[] slots — one per home page (== HPB_COUNT), so
// every page can be reordered. Any page still missing from a saved order is
// appended at navigation time via buildVisibleOrder's fallback.
static const uint8_t PAGE_ORDER_LEN = 13;
// Bytes of page_order persisted at the original file offset. Slots beyond this
// (PAGE_ORDER_LEN - PAGE_ORDER_LEN_V1) are stored at the file tail, so a
// pre-0x0019 save — whose order was exactly this long — loads without shifting
// every field written after page_order.
static const uint8_t PAGE_ORDER_LEN_V1 = 11;
// Bitmasks for home_pages_mask (bit=1 → page visible; 0 field = all visible).
// SETTINGS and QUICK_MSG have no mask bit — they're always visible.