mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
feat: home screen ordering — reorder all screens from Settings
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user