feat(companion): highlight the active target on the map

Setting a Locator/Nav target had no spatial feedback — you could arm a
destination but never see where it is. Draw it as a flag marker (ICON_MAP_TARGET)
on both the home Map preview and the full Trail map, folded into each view's
bounding box so a destination you set never sits off-frame, and drawn last so it
stays legible atop a coincident waypoint or contact.

activeTargetPos() resolves the target once (waypoint coords, or a person via
resolvePersonPos), gated only on a target being set — independent of whether the
Locator alert is enabled — so it doubles as the engine's resolver
(locatorDistance now delegates to it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarekZegare4
2026-06-24 17:56:45 +02:00
parent 83d139a62d
commit e5887f1d01
4 changed files with 48 additions and 7 deletions

View File

@@ -601,6 +601,8 @@ private:
for (int i = 0; i < LiveTrackStore::CAPACITY; i++)
if (lt.isActive(i, now)) fold(lt.slotAt(i).lat_1e6, lt.slotAt(i).lon_1e6);
}
// Fold in the active target too, so a destination you set never sits off-frame.
{ int32_t tla, tlo; if (_task->activeTargetPos(tla, tlo)) fold(tla, tlo); }
return init;
}
@@ -648,6 +650,16 @@ private:
int ex, ey; proj.project(_store->last().lat_1e6, _store->last().lon_1e6, ex, ey);
drawCurrentMarker(display, ex, ey);
}
// Active target flag, last so it stays legible atop any waypoint/contact it
// coincides with. Clamped to the frame like the other off-edge markers.
int32_t tla, tlo;
if (_task->activeTargetPos(tla, tlo)) {
int tx, ty; proj.project(tla, tlo, tx, ty);
if (tx < area_x) tx = area_x; else if (tx > wp_x_max) tx = wp_x_max;
if (ty < area_y) ty = area_y; else if (ty > wp_y_max) ty = wp_y_max;
drawTargetMarker(display, tx, ty);
}
}
void renderMap(DisplayDriver& display) {
@@ -663,8 +675,10 @@ private:
int32_t my_lat, my_lon;
const bool have_gps = ownPos(my_lat, my_lon);
const bool have_live = _task->liveTrack().active(rtc_clock.getCurrentTime()) > 0;
int32_t tgt_lat, tgt_lon;
const bool have_tgt = _task->activeTargetPos(tgt_lat, tgt_lon);
if (!have_trail && nwp == 0 && !have_gps && !have_live) {
if (!have_trail && nwp == 0 && !have_gps && !have_live && !have_tgt) {
display.drawTextCentered(display.width() / 2, (top + bottom) / 2, "No GPS / no trail");
return;
}
@@ -694,6 +708,7 @@ private:
drawWaypointLabel(display, s, ccx, ccy, area_x, area_y, area_w, area_h);
}
if (have_gps || have_trail) drawCurrentMarker(display, ccx, ccy);
{ int32_t tla, tlo; if (_task->activeTargetPos(tla, tlo)) drawTargetMarker(display, ccx, ccy); }
return;
}
@@ -939,4 +954,9 @@ private:
static void drawContactMarker(DisplayDriver& d, int cx, int cy) {
miniIconDrawCentered(d, cx, cy, ICON_MAP_CONTACT);
}
// Flag — the active Locator/Nav target. Drawn last (on top) so it stays
// legible even when it lands on the same spot as a waypoint or contact.
static void drawTargetMarker(DisplayDriver& d, int cx, int cy) {
miniIconDrawCentered(d, cx, cy, ICON_MAP_TARGET);
}
};

View File

@@ -574,6 +574,9 @@ public:
int32_t mla, mlo;
bool have_gps = _task->currentLocation(mla, mlo);
if (have_gps) fold(mla, mlo);
int32_t tla, tlo;
bool have_tgt = _task->activeTargetPos(tla, tlo); // active Locator/Nav target
if (have_tgt) fold(tla, tlo);
if (!init) return false;
// North marker — top-right, the same mini-icon as the full Trail map.
@@ -589,6 +592,7 @@ public:
for (int i = 0; i < LiveTrackStore::CAPACITY; i++)
if (lt.isActive(i, now)) { miniIconDrawCentered(display, cx, cy, ICON_MAP_CONTACT); break; }
if (have_gps || !tr.empty()) miniIconDrawCentered(display, cx, cy, ICON_MAP_CURRENT);
if (have_tgt) miniIconDrawCentered(display, cx, cy, ICON_MAP_TARGET); // highlight on top
return true;
}
float avg_lat_rad = ((mnla + mxla) / 2.0e6f) * (float)M_PI / 180.0f;
@@ -615,6 +619,8 @@ public:
miniIconDrawCentered(display, px, py, ICON_MAP_CONTACT);
}
if (have_gps) { int px, py; project(mla, mlo, px, py); miniIconDrawCentered(display, px, py, ICON_MAP_CURRENT); }
// Active target flag drawn last so it stays legible even atop a contact/own dot.
if (have_tgt) { int px, py; project(tla, tlo, px, py); miniIconDrawCentered(display, px, py, ICON_MAP_TARGET); }
// Bottom-left scale reference, always shown — distance to the nearest
// live-tracked contact now lives on the status line below instead (see
@@ -2298,14 +2304,18 @@ bool UITask::resolvePersonPos(const uint8_t* key, int32_t& lat, int32_t& lon,
return false;
}
bool UITask::locatorDistance(float& dist_m, float& radius_m) const {
bool UITask::activeTargetPos(int32_t& lat, int32_t& lon) const {
if (!_node_prefs || !_node_prefs->locator_has_target) return false;
if (_node_prefs->locator_target_kind == 1)
return resolvePersonPos(_node_prefs->locator_key, lat, lon);
lat = _node_prefs->locator_lat_1e6;
lon = _node_prefs->locator_lon_1e6;
return true;
}
bool UITask::locatorDistance(float& dist_m, float& radius_m) const {
int32_t tlat, tlon;
if (_node_prefs->locator_target_kind == 1) {
if (!resolvePersonPos(_node_prefs->locator_key, tlat, tlon)) return false;
} else {
tlat = _node_prefs->locator_lat_1e6; tlon = _node_prefs->locator_lon_1e6;
}
if (!activeTargetPos(tlat, tlon)) return false;
int32_t lat, lon;
if (!currentLocation(lat, lon)) return false;
dist_m = geo::haversineKm(lat, lon, tlat, tlon) * 1000.0f;

View File

@@ -211,6 +211,11 @@ public:
// Locator engine (locatorDistance) and the target picker.
bool resolvePersonPos(const uint8_t* key, int32_t& lat, int32_t& lon,
bool* live = nullptr, uint32_t* ts = nullptr) const;
// Resolved position of the active target — a waypoint's coords, or a person
// via resolvePersonPos(). Gated only on a target being set, independent of
// whether the Locator alert is enabled, so a destination you set still shows
// on the map. Used by locatorDistance() and the map renderers.
bool activeTargetPos(int32_t& lat, int32_t& lon) const;
void gotoTrailScreen();
void gotoMapScreen(); // opens the Trail screen directly in its Map view
void gotoCompassScreen();

View File

@@ -334,6 +334,12 @@ MINI_ICON(ICON_MAP_ARROW, 5, // → distance-to-nearest-tracked-contact ind
packRow("#####"),
packRow("...#."),
packRow("..#.."));
MINI_ICON(ICON_MAP_TARGET, 5, // ⚑ flag on a pole — the active Locator/Nav target
packRow("#...."),
packRow("####."),
packRow("#..#."),
packRow("####."),
packRow("#...."));
// Keyboard special-key glyphs.
MINI_ICON(ICON_SHIFT, 7, // ⇧ caps