diff --git a/examples/companion_radio/ui-new/LocatorScreen.h b/examples/companion_radio/ui-new/LocatorScreen.h index 55f75439..349b900b 100644 --- a/examples/companion_radio/ui-new/LocatorScreen.h +++ b/examples/companion_radio/ui-new/LocatorScreen.h @@ -6,8 +6,9 @@ // of (leave/away) the radius. A waypoint target is snapshotted (coord + label); // a person target follows their latest shared position. The crossing engine // lives in UITask::evaluateLocator(). The Target row's Enter opens a picker -// (favourites first — offered even with no known position yet, so you can arm -// ahead of time — then any other contact with a currently-known position: +// ("None" first — the only way to unset a target once chosen — then +// favourites, offered even with no known position yet so you can arm ahead +// of time, then any other contact with a currently-known position: // live-sharing or just last-advertised, e.g. a repeater; then waypoints). // LEFT/RIGHT quick-cycles the same set. The same active target can also be set // directly from Nearby Nodes' or Waypoints' own "Set as target" menu item @@ -34,7 +35,7 @@ class LocatorScreen : public UIScreen { bool _picking = false; int _pick_sel = 0, _pick_scroll = 0; struct Target { - uint8_t kind; // 0=waypoint 1=person + uint8_t kind; // 0=waypoint 1=person 2=none (clear target) int32_t lat, lon; uint8_t key[6]; char name[20]; @@ -170,7 +171,8 @@ public: return true; } - // Build the selectable target set into _targets: favourites first (the quick + // Build the selectable target set into _targets: "None" first (clears the + // target — the only way to unset it once chosen), then favourites (the quick // path you pin ahead of time, offered even with no known position yet), then // any other contact with a currently-known position — live-sharing or just // last-advertised (a repeater, a room, or someone who shared a fix once) — @@ -178,6 +180,10 @@ public: // re-resolves their position each evaluation rather than trusting a snapshot. void buildTargets() { _target_n = 0; + Target& none = _targets[_target_n++]; + none.kind = 2; none.lat = 0; none.lon = 0; none.ts = 0; none.live = false; + memset(none.key, 0, 6); + snprintf(none.name, sizeof(none.name), "(none)"); for (int i = 0; i < NodePrefs::FAVOURITES_COUNT; i++) { const uint8_t* pre = _prefs->favourite_contacts[i]; bool empty = true; @@ -203,7 +209,9 @@ public: } // Index of the currently-configured target within _targets, or -1. + // No target set at all maps to the synthetic "None" entry (always index 0). int currentTargetIndex() const { + if (!_prefs->locator_has_target) return 0; for (int i = 0; i < _target_n; i++) { if (_targets[i].kind == 1 && _prefs->locator_target_kind == 1) { if (memcmp(_targets[i].key, _prefs->locator_key, 6) == 0) return i; @@ -216,6 +224,7 @@ public: } void applyTarget(const Target& t) { + if (t.kind == 2) { _task->clearTarget(); _dirty = true; return; } // Same target definition as every other entry point (UITask::setTarget), // but the save is deferred to screen exit (_dirty) so LEFT/RIGHT cycling // through candidates doesn't write flash on each step. @@ -223,10 +232,9 @@ public: _dirty = true; } - // LEFT/RIGHT quick-cycle over the same set the picker shows. + // LEFT/RIGHT quick-cycle over the same set the picker shows (includes "None"). void cycleTarget(int dir) { buildTargets(); - if (_target_n == 0) { _task->showAlert("No targets available", 1400); return; } int cur = currentTargetIndex(); int nx = (cur < 0) ? (dir >= 0 ? 0 : _target_n - 1) : ((cur + (dir >= 0 ? 1 : _target_n - 1)) % _target_n); @@ -236,7 +244,6 @@ public: void openPicker() { if (!_prefs) return; buildTargets(); - if (_target_n == 0) { _task->showAlert("No targets available", 1400); return; } int cur = currentTargetIndex(); _pick_sel = (cur >= 0) ? cur : 0; _pick_scroll = 0; diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index d221efeb..ee0efece 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -2375,6 +2375,12 @@ void UITask::setTargetNow(uint8_t kind, const uint8_t* key, int32_t lat, int32_t showAlert("Target set", 1200); } +void UITask::clearTarget() { + if (!_node_prefs) return; + _node_prefs->locator_has_target = 0; + resetLocator(); +} + // Homing beeper: while armed with a target and inside the radius, emit a short // tick whose interval shrinks linearly with distance — slow at the edge, rapid // near the centre. Polls distance a few times a second; silent outside the diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 3ec3a983..07517965 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -204,6 +204,9 @@ public: // such hook uses setTargetNow() to save + confirm on the spot. void setTarget(uint8_t kind, const uint8_t* key, int32_t lat, int32_t lon, const char* name); void setTargetNow(uint8_t kind, const uint8_t* key, int32_t lat, int32_t lon, const char* name); + // Unset the active target (locator_has_target = 0). Distinct from setTarget() + // because there's no "kind" for nothing — clearing is its own operation. + void clearTarget(); // Resolve a person target (6-byte pubkey prefix) to a current position: // prefers an active [LOC] live share, falls back to their last-advertised // GPS fix. Returns false when neither is known. Optional live/ts report