diff --git a/examples/companion_radio/ui-new/MessagesScreen.h b/examples/companion_radio/ui-new/MessagesScreen.h index 571803d8..48a97935 100644 --- a/examples/companion_radio/ui-new/MessagesScreen.h +++ b/examples/companion_radio/ui-new/MessagesScreen.h @@ -420,10 +420,17 @@ class MessagesScreen : public UIScreen { // Delivery marker, drawn with the current ink colour and auto-scaled to the // font (see icons.h). Pending = a row of dots, one per send (so it grows with // each auto-resend); delivered = ✓; failed = ✗; ACK_NONE = nothing. - static void drawAckGlyph(DisplayDriver& d, int x, int top_y, AckState s, int sends = 1) { + // relay_count (channel sends only): when > 0, replaces the ✓ with the + // distinct-repeater echo count as tiny digit icons -- the count alone + // already says "confirmed", so showing the checkmark too is redundant; a + // DM's ✓ (no such count) leaves it 0 and keeps the plain checkmark. + static void drawAckGlyph(DisplayDriver& d, int x, int top_y, AckState s, int sends = 1, int relay_count = 0) { switch (s) { case ACK_PENDING: miniIconDotRow(d, x, top_y, sends); break; - case ACK_OK: miniIconDraw(d, x, top_y, ICON_CHECK); break; + case ACK_OK: + if (relay_count > 0) miniIconDrawNumber(d, x, top_y, relay_count); + else miniIconDraw(d, x, top_y, ICON_CHECK); + break; case ACK_FAIL: miniIconDraw(d, x, top_y, ICON_CROSS); break; default: break; // ACK_NONE → nothing } @@ -447,11 +454,11 @@ class MessagesScreen : public UIScreen { // Width of an ack/delivery glyph (see drawAckGlyph) — needed up front to size // an outgoing bubble before it's drawn. - static int ackGlyphWidth(DisplayDriver& d, AckState s, int sends) { + static int ackGlyphWidth(DisplayDriver& d, AckState s, int sends, int relay_count = 0) { const int sc = miniIconScale(d); switch (s) { case ACK_PENDING: return sends * 3 * sc; // dot+gap pitch (icons.h), slightly generous - case ACK_OK: return ICON_CHECK.w * sc; + case ACK_OK: return relay_count > 0 ? miniIconNumberWidth(d, relay_count) : ICON_CHECK.w * sc; case ACK_FAIL: return ICON_CROSS.w * sc; default: return 0; } @@ -1334,10 +1341,13 @@ public: int ret = _fs.render(display, fsender, fmsg, _hist_sel < fs_hist_count - 1, _hist_sel > 0); - // Channels: ✓ only once a repeater echo confirms relay (see list view). + // Channels: ✓ only once a repeater echo confirms relay (see list view), + // plus the distinct-echoing-repeater count as tiny digits (path_len's + // hop_count -- see markChannelRelayed/showPathDetail's "Relayed by"). if (strcmp(fsender, "Me") == 0 && _history.chAtPos(ring_pos).relay_status == ACK_OK) { display.setColor(DisplayDriver::DARK); - drawAckGlyph(display, 2 + display.getTextWidth(fsender) + 3, 1, ACK_OK); + int relay_count = _history.chAtPos(ring_pos).path_len & 63; + drawAckGlyph(display, 2 + display.getTextWidth(fsender) + 3, 1, ACK_OK, 1, relay_count); display.setColor(DisplayDriver::LIGHT); } if (_ctx_menu.active) _ctx_menu.render(display); @@ -1437,8 +1447,9 @@ public: // was relayed into the mesh; otherwise no marker (absence is normal). bool outgoing = strcmp(sender, "Me") == 0; bool show_ack = outgoing && _history.chAtPos(ring_pos).relay_status == ACK_OK; + int relay_count = show_ack ? (_history.chAtPos(ring_pos).path_len & 63) : 0; int full_avail = display.width() - reserve; - int ack_w = show_ack ? (3 + ackGlyphWidth(display, ACK_OK, 1)) : 0; + int ack_w = show_ack ? (3 + ackGlyphWidth(display, ACK_OK, 1, relay_count)) : 0; int header_w = 3 + display.getTextWidth(sender) + ack_w + age_w + 3; int body_w, nl = 0; if (portrait_expand) { @@ -1458,7 +1469,7 @@ public: display.drawTextEllipsized(box.x + 3, y + 1, box.w - 6 - age_w, sender); if (show_ack) { int gx = box.x + 3 + display.getTextWidth(sender) + 3; - drawAckGlyph(display, gx, y + 1, ACK_OK); + drawAckGlyph(display, gx, y + 1, ACK_OK, 1, relay_count); } if (age[0]) { display.setCursor(box.x + box.w - age_w, y + 1); display.print(age); } if (!sel) display.setColor(DisplayDriver::LIGHT); diff --git a/examples/companion_radio/ui-new/icons.h b/examples/companion_radio/ui-new/icons.h index 2c0e6352..f6b6a063 100644 --- a/examples/companion_radio/ui-new/icons.h +++ b/examples/companion_radio/ui-new/icons.h @@ -125,6 +125,96 @@ MINI_ICON(ICON_CROSS, 4, // ✗ packRow(".##."), packRow("#..#")); +// Tiny 3×5 digits — for a small count that needs to sit in an icon-sized slot +// (e.g. next to ICON_CHECK) where the normal font is too tall to fit. See +// miniIconDrawNumber/miniIconNumberWidth below. +MINI_ICON(ICON_DIGIT_0, 3, + packRow("###"), + packRow("#.#"), + packRow("#.#"), + packRow("#.#"), + packRow("###")); +MINI_ICON(ICON_DIGIT_1, 3, + packRow(".#."), + packRow("##."), + packRow(".#."), + packRow(".#."), + packRow("###")); +MINI_ICON(ICON_DIGIT_2, 3, + packRow("###"), + packRow("..#"), + packRow("###"), + packRow("#.."), + packRow("###")); +MINI_ICON(ICON_DIGIT_3, 3, + packRow("###"), + packRow("..#"), + packRow("###"), + packRow("..#"), + packRow("###")); +MINI_ICON(ICON_DIGIT_4, 3, + packRow("#.#"), + packRow("#.#"), + packRow("###"), + packRow("..#"), + packRow("..#")); +MINI_ICON(ICON_DIGIT_5, 3, + packRow("###"), + packRow("#.."), + packRow("###"), + packRow("..#"), + packRow("###")); +MINI_ICON(ICON_DIGIT_6, 3, + packRow("###"), + packRow("#.."), + packRow("###"), + packRow("#.#"), + packRow("###")); +MINI_ICON(ICON_DIGIT_7, 3, + packRow("###"), + packRow("..#"), + packRow("..#"), + packRow("..#"), + packRow("..#")); +MINI_ICON(ICON_DIGIT_8, 3, + packRow("###"), + packRow("#.#"), + packRow("###"), + packRow("#.#"), + packRow("###")); +MINI_ICON(ICON_DIGIT_9, 3, + packRow("###"), + packRow("#.#"), + packRow("###"), + packRow("..#"), + packRow("###")); + +static constexpr const MiniIcon* MINI_ICON_DIGITS[10] = { + &ICON_DIGIT_0, &ICON_DIGIT_1, &ICON_DIGIT_2, &ICON_DIGIT_3, &ICON_DIGIT_4, + &ICON_DIGIT_5, &ICON_DIGIT_6, &ICON_DIGIT_7, &ICON_DIGIT_8, &ICON_DIGIT_9, +}; + +// Width a count would occupy via miniIconDrawNumber (digit width + 1px gap +// between digits, scaled) — needed up front to size a header around it. +// Clamped to 2 digits (0-99): callers showing a repeater/echo count never see +// more than MAX_HIST_PATH_BYTES distinct hashes anyway (16 max). +inline int miniIconNumberWidth(DisplayDriver& d, int n) { + const int s = miniIconScale(d); + int digits = (n >= 10) ? 2 : 1; + return digits * 3 * s + (digits - 1) * s; +} + +// Draws `n` (clamped to 0-99) as a left-to-right run of tiny digit icons — +// e.g. a repeater/echo count too small a slot for the normal font to fit +// legibly. Vertically centred in the text line the same way miniIconDraw is. +inline void miniIconDrawNumber(DisplayDriver& d, int x, int top_y, int n) { + const int s = miniIconScale(d); + if (n < 0) n = 0; + if (n > 99) n = 99; + if (n >= 10) { miniIconDraw(d, x, top_y, *MINI_ICON_DIGITS[n / 10]); x += 3 * s + s; } + miniIconDraw(d, x, top_y, *MINI_ICON_DIGITS[n % 10]); +} + // Top-bar status glyphs (replace the single-letter M / B / A indicators). MINI_ICON(ICON_MUTE, 6, // speaker + cross (sound off) packRow("..#..."),