mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
refactor(companion): hoist screen entry into virtual UIScreen::onShow()
Replace the ad-hoc enter()/markClean() entry methods (which lived outside the UIScreen interface and were invoked via casts from each gotoX) with a virtual onShow() lifecycle hook called centrally by setCurrScreen(). This removes the "forgot to call enter() in a new navigator" footgun and the unchecked cast smell: 12 navigators collapse to one-line setCurrScreen(x) calls, and override enforces signature match. Two entries that carry a parameter/variant keep an explicit typed call after setCurrScreen(): RingtoneEditor::selectSlot(slot) and TrailScreen::showMapView(). Behaviour-preserving: only screens that previously had enter() get an onShow() override; Splash/Home/QuickMsg/Diag keep no reset as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ public:
|
|||||||
virtual int render(DisplayDriver& display) = 0; // returns ms until the next render
|
virtual int render(DisplayDriver& display) = 0; // returns ms until the next render
|
||||||
virtual bool handleInput(char c) { return false; }
|
virtual bool handleInput(char c) { return false; }
|
||||||
virtual void poll() { }
|
virtual void poll() { }
|
||||||
|
virtual void onShow() { } // reset per-visit state
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -44,23 +45,23 @@ public:
|
|||||||
- **`handleInput(c)`** gets one key (`KEY_*`, see §7). Return `true` if consumed.
|
- **`handleInput(c)`** gets one key (`KEY_*`, see §7). Return `true` if consumed.
|
||||||
- **`poll()`** runs every loop tick regardless of focus — rare, for background
|
- **`poll()`** runs every loop tick regardless of focus — rare, for background
|
||||||
housekeeping (e.g. the shutdown button).
|
housekeeping (e.g. the shutdown button).
|
||||||
|
- **`onShow()`** is called by `setCurrScreen()` every time the screen becomes
|
||||||
Most screens also define a non-virtual **`enter()`** that resets per-visit state
|
current — override it to reset per-visit state (`_sel = 0`, `_dirty = false`,
|
||||||
(`_sel = 0`, `_dirty = false`, …). It is called by the screen's `goto…()` wrapper,
|
sub-views). Default no-op for screens that keep state across visits. Because
|
||||||
not by the base class.
|
it's invoked centrally, a navigator can't forget to reset on show.
|
||||||
|
|
||||||
### Wiring a screen into UITask
|
### Wiring a screen into UITask
|
||||||
|
|
||||||
1. Add a `UIScreen* my_screen;` member in `UITask.h` (near the others).
|
1. Add a `UIScreen* my_screen;` member in `UITask.h` (near the others).
|
||||||
2. Construct it in `UITask::begin()` (`UITask.cpp`):
|
2. Construct it in `UITask::begin()` (`UITask.cpp`):
|
||||||
`my_screen = new MyScreen(this, …);`
|
`my_screen = new MyScreen(this, …);`
|
||||||
3. Add a navigator:
|
3. Add a navigator — usually just the one line (the cast-free `onShow()` runs
|
||||||
|
inside `setCurrScreen`):
|
||||||
```cpp
|
```cpp
|
||||||
void UITask::gotoMyScreen() {
|
void UITask::gotoMyScreen() { setCurrScreen(my_screen); }
|
||||||
((MyScreen*)my_screen)->enter();
|
|
||||||
setCurrScreen(my_screen); // sets curr + schedules an immediate redraw
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
Only screens needing a *parameter* at entry add a typed call after it (e.g.
|
||||||
|
`gotoRingtoneEditor` → `selectSlot(slot)`, `gotoMapScreen` → `showMapView()`).
|
||||||
4. Reach it from somewhere — usually a row in `ToolsScreen.h` (add an `Action`
|
4. Reach it from somewhere — usually a row in `ToolsScreen.h` (add an `Action`
|
||||||
enum value, a row in the right section table, and a `dispatch()` case).
|
enum value, a row in the right section table, and a `dispatch()` case).
|
||||||
|
|
||||||
@@ -292,7 +293,7 @@ class MyToolScreen : public UIScreen {
|
|||||||
static const int ROWS = 3;
|
static const int ROWS = 3;
|
||||||
public:
|
public:
|
||||||
MyToolScreen(UITask* t, NodePrefs* p) : _task(t), _prefs(p) {}
|
MyToolScreen(UITask* t, NodePrefs* p) : _task(t), _prefs(p) {}
|
||||||
void enter() { _sel = 0; _scroll = 0; _dirty = false; }
|
void onShow() override { _sel = 0; _scroll = 0; _dirty = false; }
|
||||||
|
|
||||||
int render(DisplayDriver& d) override {
|
int render(DisplayDriver& d) override {
|
||||||
d.setTextSize(1);
|
d.setTextSize(1);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class AutoAdvertScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
AutoAdvertScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
AutoAdvertScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||||
|
|
||||||
void enter() { _dirty = false; }
|
void onShow() override { _dirty = false; }
|
||||||
|
|
||||||
int render(DisplayDriver& display) override {
|
int render(DisplayDriver& display) override {
|
||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class BotScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
BotScreen(UITask* task, NodePrefs* prefs, KeyboardWidget* kb) : _task(task), _prefs(prefs), _kb(kb) {}
|
BotScreen(UITask* task, NodePrefs* prefs, KeyboardWidget* kb) : _task(task), _prefs(prefs), _kb(kb) {}
|
||||||
|
|
||||||
void enter() {
|
void onShow() override {
|
||||||
_sel = 0;
|
_sel = 0;
|
||||||
_scroll = 0;
|
_scroll = 0;
|
||||||
_kb_field = -1;
|
_kb_field = -1;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class CompassScreen : public UIScreen {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
CompassScreen(UITask* task) : _task(task) {}
|
CompassScreen(UITask* task) : _task(task) {}
|
||||||
void enter() {}
|
void onShow() override {}
|
||||||
|
|
||||||
int render(DisplayDriver& display) override {
|
int render(DisplayDriver& display) override {
|
||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class DashboardConfigScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
DashboardConfigScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
DashboardConfigScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||||
|
|
||||||
void enter() { _sel = 0; _dirty = false; }
|
void onShow() override { _sel = 0; _dirty = false; }
|
||||||
|
|
||||||
int render(DisplayDriver& display) override {
|
int render(DisplayDriver& display) override {
|
||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class LiveShareScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
LiveShareScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
LiveShareScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||||
|
|
||||||
void enter() { _dirty = false; _sel = 0; _scroll = 0; }
|
void onShow() override { _dirty = false; _sel = 0; _scroll = 0; }
|
||||||
|
|
||||||
// Resolve the configured target's display name (channel name or contact name).
|
// Resolve the configured target's display name (channel name or contact name).
|
||||||
void currentTargetName(char* buf, int n) {
|
void currentTargetName(char* buf, int n) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class LocatorScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
LocatorScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
LocatorScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||||
|
|
||||||
void enter() { _dirty = false; _sel = 0; _scroll = 0; _picking = false; }
|
void onShow() override { _dirty = false; _sel = 0; _scroll = 0; _picking = false; }
|
||||||
|
|
||||||
void valueLabel(Kind k, char* buf, int n) {
|
void valueLabel(Kind k, char* buf, int n) {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ public:
|
|||||||
_sort_label[0] = '\0';
|
_sort_label[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void enter() {
|
void onShow() override {
|
||||||
_sel = _scroll = 0;
|
_sel = _scroll = 0;
|
||||||
_detail = false;
|
_detail = false;
|
||||||
_nav = false;
|
_nav = false;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class RepeaterScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
RepeaterScreen(UITask* task) : _task(task), _dirty(false), _sel(0), _scroll(0), _item_count(1) {}
|
RepeaterScreen(UITask* task) : _task(task), _dirty(false), _sel(0), _scroll(0), _item_count(1) {}
|
||||||
|
|
||||||
void enter() {
|
void onShow() override {
|
||||||
_dirty = false; _sel = 0; _scroll = 0;
|
_dirty = false; _sel = 0; _scroll = 0;
|
||||||
_picker.menu.active = false; _editor.freq.active = false;
|
_picker.menu.active = false; _editor.freq.active = false;
|
||||||
_picker.saving = false; _picker.deleting = false;
|
_picker.saving = false; _picker.deleting = false;
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ class RingtoneEditorScreen : public UIScreen {
|
|||||||
public:
|
public:
|
||||||
RingtoneEditorScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs), _slot(0) {}
|
RingtoneEditorScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs), _slot(0) {}
|
||||||
|
|
||||||
void enter(int slot = 0) {
|
// Load a melody slot for editing. Called by gotoRingtoneEditor() right after
|
||||||
|
// setCurrScreen() (whose onShow() can't carry the slot argument).
|
||||||
|
void selectSlot(int slot = 0) {
|
||||||
_slot = (slot == 1) ? 1 : 0;
|
_slot = (slot == 1) ? 1 : 0;
|
||||||
bool s2 = (_slot == 1);
|
bool s2 = (_slot == 1);
|
||||||
_bpm_idx = (_prefs && (s2 ? _prefs->ringtone2_bpm_idx : _prefs->ringtone_bpm_idx) < 5)
|
_bpm_idx = (_prefs && (s2 ? _prefs->ringtone2_bpm_idx : _prefs->ringtone_bpm_idx) < 5)
|
||||||
@@ -197,7 +199,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
case MI_SWITCH:
|
case MI_SWITCH:
|
||||||
_task->stopMelody();
|
_task->stopMelody();
|
||||||
this->enter(1 - _slot);
|
this->selectSlot(1 - _slot);
|
||||||
break;
|
break;
|
||||||
case MI_DURATION: break; // already handled by LEFT/RIGHT
|
case MI_DURATION: break; // already handled by LEFT/RIGHT
|
||||||
case MI_BPM: break; // already handled by LEFT/RIGHT
|
case MI_BPM: break; // already handled by LEFT/RIGHT
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void markClean() {
|
void onShow() override {
|
||||||
_dirty = false;
|
_dirty = false;
|
||||||
resetList();
|
resetList();
|
||||||
_editor.freq.active = false;
|
_editor.freq.active = false;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public:
|
|||||||
ToolsScreen(UITask* task) : _task(task) {}
|
ToolsScreen(UITask* task) : _task(task) {}
|
||||||
|
|
||||||
// Open folded at the section list each time Tools is entered from Home.
|
// Open folded at the section list each time Tools is entered from Home.
|
||||||
void enter() {
|
void onShow() override {
|
||||||
static uint8_t sizes[SECTION_COUNT];
|
static uint8_t sizes[SECTION_COUNT];
|
||||||
for (int i = 0; i < SECTION_COUNT; i++) sizes[i] = SECTIONS[i].count;
|
for (int i = 0; i < SECTION_COUNT; i++) sizes[i] = SECTIONS[i].count;
|
||||||
_acc.begin(sizes, SECTION_COUNT);
|
_acc.begin(sizes, SECTION_COUNT);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public:
|
|||||||
TrailScreen(UITask* task, TrailStore* store)
|
TrailScreen(UITask* task, TrailStore* store)
|
||||||
: _task(task), _store(store), _wp(task, store) {}
|
: _task(task), _store(store), _wp(task, store) {}
|
||||||
|
|
||||||
void enter() {
|
void onShow() override {
|
||||||
_view = V_SUMMARY;
|
_view = V_SUMMARY;
|
||||||
_summary_scroll = 0;
|
_summary_scroll = 0;
|
||||||
_list_scroll = 0;
|
_list_scroll = 0;
|
||||||
@@ -107,9 +107,10 @@ public:
|
|||||||
_wp.reset();
|
_wp.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter straight into the Map view (used by the Home "Map" page). Remembers
|
// Switch straight into the Map view (used by the Home "Map" page). Layered on
|
||||||
// that we came from Home so KEY_CANCEL returns there, not to Tools.
|
// top of onShow()'s reset by gotoMapScreen(); remembers we came from Home so
|
||||||
void enterMap() { enter(); _view = V_MAP; _return_home = true; }
|
// KEY_CANCEL returns there, not to Tools.
|
||||||
|
void showMapView() { _view = V_MAP; _return_home = true; }
|
||||||
|
|
||||||
int render(DisplayDriver& display) override {
|
int render(DisplayDriver& display) override {
|
||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
|
|||||||
@@ -1473,74 +1473,34 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
|||||||
setCurrScreen(splash);
|
setCurrScreen(splash);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::gotoSettingsScreen() {
|
// onShow() is invoked by setCurrScreen(), so most navigators are just that.
|
||||||
((SettingsScreen*)settings)->markClean();
|
void UITask::gotoSettingsScreen() { setCurrScreen(settings); }
|
||||||
setCurrScreen(settings);
|
void UITask::gotoToolsScreen() { setCurrScreen(tools_screen); }
|
||||||
}
|
void UITask::gotoBotScreen() { setCurrScreen(bot_screen); }
|
||||||
|
void UITask::gotoNearbyScreen() { setCurrScreen(nearby_screen); }
|
||||||
void UITask::gotoToolsScreen() {
|
void UITask::gotoDashboardConfig() { setCurrScreen(dashboard_config); }
|
||||||
((ToolsScreen*)tools_screen)->enter();
|
void UITask::gotoTrailScreen() { setCurrScreen(trail_screen); }
|
||||||
setCurrScreen(tools_screen);
|
void UITask::gotoCompassScreen() { setCurrScreen(compass_screen); }
|
||||||
}
|
void UITask::gotoDiagnosticsScreen() { setCurrScreen(diag_screen); }
|
||||||
|
void UITask::gotoRepeaterScreen() { setCurrScreen(repeater_screen); }
|
||||||
|
void UITask::gotoLiveShareScreen() { setCurrScreen(live_share_screen); }
|
||||||
|
|
||||||
|
// Ringtone takes a slot argument that onShow() can't carry — pass it after the
|
||||||
|
// reset (setCurrScreen's onShow runs first, then this layers the slot on top).
|
||||||
void UITask::gotoRingtoneEditor(int slot) {
|
void UITask::gotoRingtoneEditor(int slot) {
|
||||||
((RingtoneEditorScreen*)ringtone_edit)->enter(slot);
|
|
||||||
setCurrScreen(ringtone_edit);
|
setCurrScreen(ringtone_edit);
|
||||||
|
((RingtoneEditorScreen*)ringtone_edit)->selectSlot(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::gotoBotScreen() {
|
// Map is a sub-view variant of the Trail screen: reset via onShow(), then
|
||||||
((BotScreen*)bot_screen)->enter();
|
// switch into the map view.
|
||||||
setCurrScreen(bot_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoNearbyScreen() {
|
|
||||||
((NearbyScreen*)nearby_screen)->enter();
|
|
||||||
setCurrScreen(nearby_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoDashboardConfig() {
|
|
||||||
((DashboardConfigScreen*)dashboard_config)->enter();
|
|
||||||
setCurrScreen(dashboard_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoTrailScreen() {
|
|
||||||
((TrailScreen*)trail_screen)->enter();
|
|
||||||
setCurrScreen(trail_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoMapScreen() {
|
void UITask::gotoMapScreen() {
|
||||||
((TrailScreen*)trail_screen)->enterMap();
|
|
||||||
setCurrScreen(trail_screen);
|
setCurrScreen(trail_screen);
|
||||||
|
((TrailScreen*)trail_screen)->showMapView();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::gotoCompassScreen() {
|
void UITask::gotoLocatorScreen() { setCurrScreen(locator_screen); }
|
||||||
((CompassScreen*)compass_screen)->enter();
|
void UITask::gotoAutoAdvertScreen() { setCurrScreen(auto_advert_screen); }
|
||||||
setCurrScreen(compass_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoDiagnosticsScreen() {
|
|
||||||
setCurrScreen(diag_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoRepeaterScreen() {
|
|
||||||
((RepeaterScreen*)repeater_screen)->enter();
|
|
||||||
setCurrScreen(repeater_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoLiveShareScreen() {
|
|
||||||
((LiveShareScreen*)live_share_screen)->enter();
|
|
||||||
setCurrScreen(live_share_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoLocatorScreen() {
|
|
||||||
((LocatorScreen*)locator_screen)->enter();
|
|
||||||
setCurrScreen(locator_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoAutoAdvertScreen() {
|
|
||||||
((AutoAdvertScreen*)auto_advert_screen)->enter();
|
|
||||||
setCurrScreen(auto_advert_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public method to handle ping result callback
|
// Public method to handle ping result callback
|
||||||
void UITask::handlePingResult(uint32_t tag, int16_t snr_out_x4, int16_t snr_back_x4, uint32_t rtt_ms) {
|
void UITask::handlePingResult(uint32_t tag, int16_t snr_out_x4, int16_t snr_back_x4, uint32_t rtt_ms) {
|
||||||
@@ -1789,6 +1749,7 @@ void UITask::userLedHandler() {
|
|||||||
|
|
||||||
void UITask::setCurrScreen(UIScreen* c) {
|
void UITask::setCurrScreen(UIScreen* c) {
|
||||||
curr = c;
|
curr = c;
|
||||||
|
if (c) c->onShow(); // central per-visit reset hook (see UIScreen::onShow)
|
||||||
_next_refresh = 100;
|
_next_refresh = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,5 +57,10 @@ public:
|
|||||||
virtual int render(DisplayDriver& display) =0; // return value is number of millis until next render
|
virtual int render(DisplayDriver& display) =0; // return value is number of millis until next render
|
||||||
virtual bool handleInput(char c) { return false; }
|
virtual bool handleInput(char c) { return false; }
|
||||||
virtual void poll() { }
|
virtual void poll() { }
|
||||||
|
// Called by UITask::setCurrScreen() each time this screen becomes current —
|
||||||
|
// the place to reset per-visit state (selection, dirty flag, sub-views).
|
||||||
|
// Default no-op for screens that keep state across visits. Because it's
|
||||||
|
// invoked centrally, a new screen can't "forget" to be reset on show.
|
||||||
|
virtual void onShow() { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user