mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-03 18:56:15 +00:00
feat(ui): status-bar icons drop by priority instead of crushing the node name
renderBatteryIndicator() drew the secondary icons in a fixed sequence, so a busy bar (many background modes on) shrank the node-name area to a couple of ellipsised characters. Rework it into a priority-ordered list laid out right->left with a reserved minimum name width: once an icon won't fit above the reserve, every lower-priority icon after it is dropped too. Priority: BT > GPS fix > alarm > mute > auto-advert > trail > live-share > repeater (battery stays rightmost). Blinking icons still reserve their slot while off, so the name width doesn't flicker with the blink. Also -39 lines (eight if-blocks collapse to one table + loop). Both solo envs build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -469,84 +469,47 @@ class HomeScreen : public UIScreen {
|
|||||||
display.fillRect(battLeftX + bm, bm, fillW, iconH - 2 * bm);
|
display.fillRect(battLeftX + bm, bm, fillW, iconH - 2 * bm);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PIN_BUZZER
|
// Secondary status icons, laid out right→left in PRIORITY order so a crowded
|
||||||
if (_task->isBuzzerQuiet()) {
|
// bar sheds its least-important cues instead of crushing the node name. Once
|
||||||
int mx = battLeftX - ind - ind_gap;
|
// an icon won't fit above the reserved name area, every lower-priority icon
|
||||||
drawBoxedIcon(display, mx, ind, ind_h, ICON_MUTE);
|
// after it is dropped too (the list is ordered high→low). A blinking icon
|
||||||
battLeftX = mx;
|
// still reserves its slot while off, so the name width doesn't flicker.
|
||||||
}
|
//
|
||||||
#endif
|
// Priority: BT > GPS fix > alarm > mute > auto-advert > trail > live-share >
|
||||||
|
// repeater. Battery (drawn above) is always rightmost. The background modes
|
||||||
// Alarm armed — a persistent status cue (like mute), always shown (no blink).
|
// (advert / trail / live-share / repeater) stay outside any BT gate — they
|
||||||
if (_node_prefs && _node_prefs->alarm_on) {
|
// keep running with Bluetooth off, so their cue must not vanish with it.
|
||||||
int alX = battLeftX - ind - ind_gap;
|
|
||||||
drawBoxedIcon(display, alX, ind, ind_h, ICON_ALARM);
|
|
||||||
battLeftX = alX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// BT connection indicator (left of muted/battery icons)
|
|
||||||
int leftmostX = battLeftX;
|
|
||||||
if (_task->isSerialEnabled()) {
|
|
||||||
int btX = battLeftX - ind - ind_gap;
|
|
||||||
if (_task->isBLEConnected()) // BT icon reflects BLE link, not USB
|
|
||||||
drawBoxedIcon(display, btX, ind, ind_h, ICON_BLUETOOTH);
|
|
||||||
else
|
|
||||||
drawSlotIcon(display, btX, ind, ind_h, ICON_BLUETOOTH); // plain glyph: available, not linked
|
|
||||||
leftmostX = btX - ind_gap;
|
|
||||||
} else {
|
|
||||||
leftmostX = battLeftX - ind_gap; // no BT icon; keep the same first-slot spacing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background-mode indicators — deliberately OUTSIDE the isSerialEnabled()
|
|
||||||
// gate above: auto-advert, live share, the trail and the repeater all keep
|
|
||||||
// running with Bluetooth switched off, so their at-a-glance cues must not
|
|
||||||
// vanish with the BT icon.
|
|
||||||
|
|
||||||
// "A" indicator — blinks on OLED, always shown on e-ink
|
|
||||||
if (_node_prefs && _node_prefs->advert_auto_interval_sec > 0) {
|
|
||||||
int aX = leftmostX - ind;
|
|
||||||
if (blinkOn()) drawBoxedIcon(display, aX, ind, ind_h, ICON_ADVERT);
|
|
||||||
leftmostX = aX - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Live location sharing active. Same blink convention — another
|
|
||||||
// "leave it on and forget" broadcast, like auto-advert above. Reuses
|
|
||||||
// the diamond the map uses for a live-tracked contact, so the glyph
|
|
||||||
// already means "sharing position" elsewhere in the UI.
|
|
||||||
if (_node_prefs && _node_prefs->loc_share_enabled) {
|
|
||||||
int lsX = leftmostX - ind;
|
|
||||||
if (blinkOn()) drawBoxedIcon(display, lsX, ind, ind_h, ICON_MAP_CONTACT);
|
|
||||||
leftmostX = lsX - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GPS trail logging active. Same blink convention.
|
|
||||||
if (_task->trail().isActive()) {
|
|
||||||
int gX = leftmostX - ind;
|
|
||||||
if (blinkOn()) drawBoxedIcon(display, gX, ind, ind_h, ICON_TRAIL);
|
|
||||||
leftmostX = gX - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repeater relaying. Same blink convention — a "leave it on and forget"
|
|
||||||
// mode, so an at-a-glance cue matters (especially on a Custom profile,
|
|
||||||
// where the device is off your own network while this is shown).
|
|
||||||
if (_node_prefs && _node_prefs->client_repeat) {
|
|
||||||
int rX = leftmostX - ind;
|
|
||||||
if (blinkOn()) drawBoxedIcon(display, rX, ind, ind_h, ICON_REPEATER);
|
|
||||||
leftmostX = rX - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GPS fix status — boxed (lit) when the receiver has a valid fix, plain
|
|
||||||
// glyph while searching. Hidden entirely on boards with no GPS hardware
|
|
||||||
// and while the GPS setting itself is off, so it doesn't sit there as a
|
|
||||||
// permanently-empty slot or imply a search that isn't happening.
|
|
||||||
LocationProvider* loc = _sensors ? _sensors->getLocationProvider() : nullptr;
|
LocationProvider* loc = _sensors ? _sensors->getLocationProvider() : nullptr;
|
||||||
if (loc && _node_prefs && _node_prefs->gps_enabled) {
|
bool gps_on = loc && _node_prefs && _node_prefs->gps_enabled;
|
||||||
int gX = leftmostX - ind - ind_gap;
|
bool mute_on = false;
|
||||||
if (loc->isValid()) drawBoxedIcon(display, gX, ind, ind_h, ICON_GPS);
|
#ifdef PIN_BUZZER
|
||||||
else drawSlotIcon(display, gX, ind, ind_h, ICON_GPS);
|
mute_on = _task->isBuzzerQuiet();
|
||||||
leftmostX = gX - 1;
|
#endif
|
||||||
|
struct Sicon { bool active; const MiniIcon* icon; bool boxed; bool blink; };
|
||||||
|
const Sicon icons[] = {
|
||||||
|
{ _task->isSerialEnabled(), &ICON_BLUETOOTH, _task->isSerialEnabled() && _task->isBLEConnected(), false },
|
||||||
|
{ gps_on, &ICON_GPS, gps_on && loc->isValid(), false },
|
||||||
|
{ _node_prefs && _node_prefs->alarm_on, &ICON_ALARM, true, false },
|
||||||
|
{ mute_on, &ICON_MUTE, true, false },
|
||||||
|
{ _node_prefs && _node_prefs->advert_auto_interval_sec > 0, &ICON_ADVERT, true, true },
|
||||||
|
{ _task->trail().isActive(), &ICON_TRAIL, true, true },
|
||||||
|
{ _node_prefs && _node_prefs->loc_share_enabled, &ICON_MAP_CONTACT, true, true },
|
||||||
|
{ _node_prefs && _node_prefs->client_repeat, &ICON_REPEATER, true, true },
|
||||||
|
};
|
||||||
|
|
||||||
|
int x = battLeftX;
|
||||||
|
const int name_min = display.getCharWidth() * 5; // always keep ~5 chars for the name
|
||||||
|
for (const Sicon& s : icons) {
|
||||||
|
if (!s.active) continue;
|
||||||
|
int ix = x - ind - ind_gap;
|
||||||
|
if (ix < name_min) break; // out of room — drop this + all lower priority
|
||||||
|
if (!s.blink || blinkOn()) {
|
||||||
|
if (s.boxed) drawBoxedIcon(display, ix, ind, ind_h, *s.icon);
|
||||||
|
else drawSlotIcon(display, ix, ind, ind_h, *s.icon);
|
||||||
}
|
}
|
||||||
return leftmostX;
|
x = ix;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
CayenneLPP sensors_lpp;
|
CayenneLPP sensors_lpp;
|
||||||
|
|||||||
Reference in New Issue
Block a user