mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-27 23:38:12 +00:00
fix(companion): Live Share UX + map labels
- Live Share tool: add scrollbar, drop non-clickable section headers and the one-shot "Share now" (keep Auto-share toggle; manual share stays on the Map). Ellipsize the "To" target so long names don't wrap. - Move "Track loc" (receive) from Settings into Live Share. - Map: 2-char contact labels (distinguishable); drop "press" hint from the Home Map page status line so it fits on OLED. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
#pragma once
|
||||
// Live location sharing config tool. Tools › Live Share.
|
||||
// Periodically broadcasts a [LOC] message to a chosen channel/contact while
|
||||
// moving. Independent of (and able to run alongside) the 0-hop Auto-Advert.
|
||||
// One place for both directions of position sharing:
|
||||
// Track loc — receive [LOC] shares from others (LiveTrackStore).
|
||||
// Auto share — periodically broadcast my own [LOC] to a channel/contact
|
||||
// while moving (movement-gated engine in UITask).
|
||||
// Independent of (and able to run alongside) the 0-hop Auto-Advert. The
|
||||
// one-shot "Share my pos" lives on the Map screen.
|
||||
// Included by UITask.cpp after AutoAdvertScreen.h.
|
||||
|
||||
#include "../GeoUtils.h" // LOCATION_MSG_TAG
|
||||
#include "../NodePrefs.h"
|
||||
#include "icons.h" // scrollIndicatorReserve / drawScrollIndicator
|
||||
|
||||
class LiveShareScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
@@ -14,43 +18,27 @@ class LiveShareScreen : public UIScreen {
|
||||
int _sel = 0;
|
||||
int _scroll = 0;
|
||||
|
||||
// Row model — headers are non-selectable section separators.
|
||||
enum Kind : uint8_t { K_HEADER, K_TRACK, K_AUTO, K_TARGET, K_MOVE, K_GAP, K_HB, K_SHARE_NOW };
|
||||
enum Kind : uint8_t { K_TRACK, K_AUTO, K_TARGET, K_MOVE, K_GAP, K_HB };
|
||||
struct Row { Kind kind; const char* label; };
|
||||
static const int ROW_COUNT = 12;
|
||||
static const int ROW_COUNT = 6;
|
||||
static Row rows(int i) {
|
||||
static const Row R[ROW_COUNT] = {
|
||||
{ K_HEADER, "Receive" },
|
||||
{ K_TRACK, "Track loc" },
|
||||
{ K_HEADER, "Send" },
|
||||
{ K_AUTO, "Auto share" },
|
||||
{ K_HEADER, "Target" },
|
||||
{ K_TARGET, "To" },
|
||||
{ K_HEADER, "Triggers" },
|
||||
{ K_MOVE, "Move" },
|
||||
{ K_GAP, "Min gap" },
|
||||
{ K_HB, "Heartbeat" },
|
||||
{ K_HEADER, "" },
|
||||
{ K_SHARE_NOW, "Share now" },
|
||||
{ K_TRACK, "Track loc" },
|
||||
{ K_AUTO, "Auto share" },
|
||||
{ K_TARGET, "To" },
|
||||
{ K_MOVE, "Move" },
|
||||
{ K_GAP, "Min gap" },
|
||||
{ K_HB, "Heartbeat" },
|
||||
};
|
||||
return R[i];
|
||||
}
|
||||
static bool selectable(Kind k) { return k != K_HEADER; }
|
||||
|
||||
public:
|
||||
LiveShareScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||
|
||||
void enter() {
|
||||
_dirty = false;
|
||||
if (!selectable(rows(_sel).kind)) _sel = firstSelectable();
|
||||
}
|
||||
void enter() { _dirty = false; _sel = 0; _scroll = 0; }
|
||||
|
||||
int firstSelectable() const {
|
||||
for (int i = 0; i < ROW_COUNT; i++) if (selectable(rows(i).kind)) return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ── target helpers ─────────────────────────────────────────────────────────
|
||||
// Resolve the configured target's display name (channel name or contact name).
|
||||
void currentTargetName(char* buf, int n) {
|
||||
if (!_prefs) { snprintf(buf, n, "none"); return; }
|
||||
if (_prefs->loc_share_target_type == 0) {
|
||||
@@ -66,7 +54,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// ── value label for a row ──────────────────────────────────────────────────
|
||||
void valueLabel(Kind k, char* buf, int n) {
|
||||
switch (k) {
|
||||
case K_TRACK:
|
||||
@@ -104,57 +91,41 @@ public:
|
||||
|
||||
const int y0 = display.listStart();
|
||||
const int step = display.lineStep();
|
||||
const int valx = display.width() / 2 + 6;
|
||||
|
||||
// Scroll window: keep the selected row visible.
|
||||
int vis = (display.height() - y0) / step;
|
||||
if (vis < 1) vis = 1;
|
||||
if (_sel < _scroll) _scroll = _sel;
|
||||
if (_sel >= _scroll + vis) _scroll = _sel - vis + 1;
|
||||
if (_scroll < 0) _scroll = 0;
|
||||
if (_scroll > ROW_COUNT - vis) _scroll = (ROW_COUNT > vis) ? ROW_COUNT - vis : 0;
|
||||
|
||||
const int reserve = scrollIndicatorReserve(display, ROW_COUNT, vis);
|
||||
const int valx = display.width() / 2 + 6;
|
||||
|
||||
for (int i = _scroll; i < ROW_COUNT && i < _scroll + vis; i++) {
|
||||
int y = y0 + (i - _scroll) * step;
|
||||
Row r = rows(i);
|
||||
if (r.kind == K_HEADER) {
|
||||
if (r.label[0]) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setCursor(2, y);
|
||||
display.print(r.label);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bool sel = (i == _sel);
|
||||
display.drawSelectionRow(0, y - 1, display.width(), step - 1, sel);
|
||||
display.drawSelectionRow(0, y - 1, display.width() - reserve, step - 1, sel);
|
||||
display.setCursor(4, y);
|
||||
display.print(r.label);
|
||||
char val[24];
|
||||
valueLabel(r.kind, val, sizeof(val));
|
||||
if (val[0]) {
|
||||
display.setCursor(valx, y);
|
||||
display.print(val);
|
||||
}
|
||||
if (val[0]) display.drawTextEllipsized(valx, y, display.width() - valx - reserve, val);
|
||||
}
|
||||
|
||||
drawScrollIndicator(display, y0, vis * step, ROW_COUNT, vis, _scroll);
|
||||
return 500;
|
||||
}
|
||||
|
||||
void moveSel(int dir) {
|
||||
int i = _sel;
|
||||
for (int step = 0; step < ROW_COUNT; step++) {
|
||||
i = (i + dir + ROW_COUNT) % ROW_COUNT;
|
||||
if (selectable(rows(i).kind)) { _sel = i; return; }
|
||||
}
|
||||
}
|
||||
void moveSel(int dir) { _sel = (_sel + dir + ROW_COUNT) % ROW_COUNT; }
|
||||
|
||||
// Cycle/act on the selected row. `enter` distinguishes ENTER (which opens the
|
||||
// target picker / fires Share now) from LEFT/RIGHT (value cycling only).
|
||||
// Cycle/act on the selected row. `enter` opens the target picker (vs LEFT/RIGHT
|
||||
// value cycling).
|
||||
void activate(int dir, bool enter) {
|
||||
if (!_prefs) return;
|
||||
Kind k = rows(_sel).kind;
|
||||
switch (k) {
|
||||
switch (rows(_sel).kind) {
|
||||
case K_TRACK: _prefs->track_shared_loc ^= 1; _dirty = true; break;
|
||||
case K_AUTO: _prefs->loc_share_enabled ^= 1; _dirty = true; break;
|
||||
case K_AUTO: _prefs->loc_share_enabled ^= 1; _dirty = true; break;
|
||||
case K_TARGET:
|
||||
if (enter) { _task->pickLocShareTarget(); return; } // full chooser
|
||||
cycleTarget(dir); _dirty = true; // L/R quick cycle
|
||||
@@ -168,8 +139,6 @@ public:
|
||||
case K_HB:
|
||||
_prefs->loc_share_heartbeat_idx = (uint8_t)((_prefs->loc_share_heartbeat_idx + (dir >= 0 ? 1 : NodePrefs::LOC_SHARE_HEARTBEAT_COUNT - 1)) % NodePrefs::LOC_SHARE_HEARTBEAT_COUNT);
|
||||
_dirty = true; break;
|
||||
case K_SHARE_NOW: shareNow(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,14 +171,6 @@ public:
|
||||
else memcpy(_prefs->loc_share_dm_prefix, list[nx].prefix, NodePrefs::FAVOURITE_PREFIX_LEN);
|
||||
}
|
||||
|
||||
void shareNow() {
|
||||
int32_t lat, lon;
|
||||
if (!_task->currentLocation(lat, lon)) { _task->showAlert("No GPS fix", 1000); return; }
|
||||
char text[40];
|
||||
snprintf(text, sizeof(text), LOCATION_MSG_TAG "%.5f,%.5f", lat / 1e6, lon / 1e6);
|
||||
_task->shareToMessage(text);
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU) {
|
||||
if (_dirty) { the_mesh.savePrefs(); _dirty = false; }
|
||||
|
||||
@@ -584,7 +584,7 @@ private:
|
||||
if (cx < area_x) cx = area_x; else if (cx > wp_x_max) cx = wp_x_max;
|
||||
if (cy < area_y) cy = area_y; else if (cy > wp_y_max) cy = wp_y_max;
|
||||
drawContactMarker(display, cx, cy);
|
||||
char s2[2] = { s.name[0], 0 };
|
||||
char s2[3] = { s.name[0], s.name[0] ? s.name[1] : (char)0, 0 };
|
||||
drawWaypointLabel(display, s2, cx, cy, area_x, area_y, area_w, area_h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,8 +963,7 @@ public:
|
||||
int32_t la, lo;
|
||||
bool fix = _task->currentLocation(la, lo);
|
||||
int trk = _task->liveTrack().active(rtc_clock.getCurrentTime());
|
||||
snprintf(info, sizeof(info), "Fix:%s Track:%d %s",
|
||||
fix ? "y" : "n", trk, PRESS_LABEL);
|
||||
snprintf(info, sizeof(info), "Fix:%s Track:%d", fix ? "y" : "n", trk);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
if (!drew)
|
||||
display.drawTextCentered(display.width() / 2, content_y + area_h / 2, "No GPS / no trail");
|
||||
|
||||
Reference in New Issue
Block a user