diff --git a/examples/companion_radio/ui-new/QuickMsgScreen.h b/examples/companion_radio/ui-new/QuickMsgScreen.h index a7a9eebf..bf8ccf34 100644 --- a/examples/companion_radio/ui-new/QuickMsgScreen.h +++ b/examples/companion_radio/ui-new/QuickMsgScreen.h @@ -290,8 +290,8 @@ class QuickMsgScreen : public UIScreen { static void drawAckGlyph(DisplayDriver& d, int x, int top_y, AckState s, int sends = 1) { switch (s) { case ACK_PENDING: miniIconDotRow(d, x, top_y, sends); break; - case ACK_OK: miniIconDraw(d, x, top_y, ICON_CHECK, 5, 4); break; - case ACK_FAIL: miniIconDraw(d, x, top_y, ICON_CROSS, 4, 4); break; + case ACK_OK: miniIconDraw(d, x, top_y, ICON_CHECK); break; + case ACK_FAIL: miniIconDraw(d, x, top_y, ICON_CROSS); break; default: break; // ACK_NONE → nothing } } diff --git a/examples/companion_radio/ui-new/icons.h b/examples/companion_radio/ui-new/icons.h index c766333f..e38aa546 100644 --- a/examples/companion_radio/ui-new/icons.h +++ b/examples/companion_radio/ui-new/icons.h @@ -10,10 +10,36 @@ // further down, which are fixed-size full bitmaps drawn via drawXbm(). // // To add a mini-icon: -// 1. Define a bitmap: H rows top→bottom, one byte per row, bit (1<= 8) ? 0 : (uint8_t)(packBit(s, x) | packRow(s, x + 1)); +} + +// A mini-icon bundles its pixel data with its dimensions, so call sites can't +// pass a stale width/height. Built via the MINI_ICON macro below. +struct MiniIcon { uint8_t w, h; const uint8_t* rows; }; + +// Define a mini-icon: the row count (height) is derived from the initializer, +// the width is stated once. Emits a packed byte array plus a MiniIcon view. +#define MINI_ICON(name, width, ...) \ + static constexpr uint8_t name##_rows[] = { __VA_ARGS__ }; \ + static constexpr MiniIcon name = { (uint8_t)(width), \ + (uint8_t)sizeof(name##_rows), name##_rows } // Pixel scale from the font: 1× on an 8px OLED line, 2× on a 16px landscape // e-ink line, etc. Bitmaps are authored on the 1× grid. @@ -34,6 +60,11 @@ inline void miniIconDraw(DisplayDriver& d, int x, int top_y, if (rows[r] & (1 << c)) d.fillRect(x + c * s, y + r * s, s, s); } +// Preferred overload: dimensions travel with the icon — no magic numbers. +inline void miniIconDraw(DisplayDriver& d, int x, int top_y, const MiniIcon& ic) { + miniIconDraw(d, x, top_y, ic.rows, ic.w, ic.h); +} + // Horizontal row of `count` square dots (scaled, vertically centred). Used by // the "awaiting ACK" marker, where the dot count = number of send attempts. inline void miniIconDotRow(DisplayDriver& d, int x, int top_y, int count) { @@ -44,9 +75,51 @@ inline void miniIconDotRow(DisplayDriver& d, int x, int top_y, int count) { for (int i = 0; i < count; i++) d.fillRect(x + i * pitch, y, dot, dot); } -// Mini-icon bitmaps (authored on the 1× grid). -static const uint8_t ICON_CHECK[4] = { 0x10, 0x08, 0x05, 0x02 }; // ✓ 5×4 -static const uint8_t ICON_CROSS[4] = { 0x09, 0x06, 0x06, 0x09 }; // ✗ 4×4 +// Mini-icon bitmaps (authored on the 1× grid as ASCII-art; see packRow above). +MINI_ICON(ICON_CHECK, 5, // ✓ + packRow("....#"), + packRow("...#."), + packRow("#.#.."), + packRow(".#...")); +MINI_ICON(ICON_CROSS, 4, // ✗ + packRow("#..#"), + packRow(".##."), + packRow(".##."), + packRow("#..#")); + +// ── Big ASCII-art icons (skeleton, not yet used) ───────────────────────────── +// Same authoring idea as the mini-icons but for full page glyphs up to 32 px +// wide: one uint32_t per row. The existing XBM bitmaps below (logo/bluetooth/…) +// stay hand-encoded; reach for this when adding a *new* big icon so it's +// readable in source. Drawn at 1× (page icons aren't font-scaled). +// +// To add one: +// BIG_ICON(MY_ICON, 16, +// packRow32("......####......"), +// packRow32(".....######....."), +// ...); +// bigIconDraw(display, x, y, MY_ICON); +constexpr uint32_t packBit32(const char* s, int x) { + return (s[x] && s[x] != ' ' && s[x] != '.') ? (1u << x) : 0u; +} +constexpr uint32_t packRow32(const char* s, int x = 0) { + return (!s[x] || x >= 32) ? 0u : (packBit32(s, x) | packRow32(s, x + 1)); +} + +struct BigIcon { uint8_t w, h; const uint32_t* rows; }; + +#define BIG_ICON(name, width, ...) \ + static constexpr uint32_t name##_rows[] = { __VA_ARGS__ }; \ + static constexpr BigIcon name = { \ + (uint8_t)(width), \ + (uint8_t)(sizeof(name##_rows) / sizeof(uint32_t)), name##_rows } + +// Draw a packed big icon at 1× with the current ink colour, top-left at (x, y). +inline void bigIconDraw(DisplayDriver& d, int x, int y, const BigIcon& ic) { + for (int r = 0; r < ic.h; r++) + for (int c = 0; c < ic.w; c++) + if (ic.rows[r] & (1u << c)) d.fillRect(x + c, y + r, 1, 1); +} // 'meshcore', 128x13px static const uint8_t meshcore_logo [] = {