From 21a34451d9637b87a772b3146c6e81ed93381100 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:18:36 +0200 Subject: [PATCH] refactor(ui): dedupe caps-shift and favourites "+" tile Two duplications surfaced by a framework-consistency pass: - KeyboardWidget applied the a-z shift-uppercase at five draw/commit sites with an inline `if (caps && ch >= 'a' && ch <= 'z')`. Fold them into one `kbApplyCaps(ch, caps)` helper. - The favourites grid drew the empty "+" tile from two branches (the gone-slot prune and the always-empty slot). Route both through a single `has_contact` flag so the "+" and the trailing selection-colour reset each live at one site. No behaviour change. Builds green: WioTrackerL1_companion_solo_dual. Co-Authored-By: Claude Opus 4.8 --- .../companion_radio/ui-new/KeyboardWidget.h | 21 ++++++++++-------- examples/companion_radio/ui-new/UITask.cpp | 22 +++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/examples/companion_radio/ui-new/KeyboardWidget.h b/examples/companion_radio/ui-new/KeyboardWidget.h index b7628a44..5cdf4c47 100644 --- a/examples/companion_radio/ui-new/KeyboardWidget.h +++ b/examples/companion_radio/ui-new/KeyboardWidget.h @@ -51,6 +51,13 @@ static const int KB_MAX_LEN = 160; // very wide display (small font → many chars per line) can't overrun them. static const int KB_PREVIEW_CAP = 46; +// Uppercase one char when Shift (caps) is on. The keyboard applies this at every +// draw and commit site — a-z only, everything else (digits, punctuation) passes +// through unchanged. +static inline char kbApplyCaps(char ch, bool caps) { + return (caps && ch >= 'a' && ch <= 'z') ? (char)(ch - 'a' + 'A') : ch; +} + static const int KB_PH_MAX = 12; // max placeholders in list static const int KB_PH_LEN = 9; // max placeholder string length incl. null static const int KB_PH_VISIBLE = 3; // items shown at once in overlay @@ -175,7 +182,7 @@ struct KeyboardWidget { // (".,!?'-", 6 chars) + digit already fills a narrow OLED cell. char label[10]; snprintf(label, sizeof(label), "%c%s", (char)('1' + cell), KB_T9_GROUPS[page][cell]); - if (caps) for (char* p = label; *p; p++) if (*p >= 'a' && *p <= 'z') *p = *p - 'a' + 'A'; + if (caps) for (char* p = label; *p; p++) *p = kbApplyCaps(*p, caps); int cx = c * cell_w; display.drawSelectionRow(cx, y - 1, cell_w - 1, cell_h, sel); int tw = display.getTextWidth(label); @@ -188,8 +195,7 @@ struct KeyboardWidget { int y = chars_y + r * cell_h; for (int c = 0; c < cols; c++) { bool sel = (row == r && col == c); - char ch = KB_CHARS[page][r][c]; - if (caps && ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A'; + char ch = kbApplyCaps(KB_CHARS[page][r][c], caps); char ch_buf[2] = { ch == ' ' ? '_' : ch, '\0' }; int cx = c * cell_w; display.drawSelectionRow(cx, y - 1, cell_w - 1, cell_h, sel); @@ -301,13 +307,11 @@ struct KeyboardWidget { if (cycling) { t9_cycle = (t9_cycle + 1) % total; if (len > 0) { - char ch = (t9_cycle < glen) ? group[t9_cycle] : (char)('1' + cell); - if (caps && ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A'; + char ch = kbApplyCaps((t9_cycle < glen) ? group[t9_cycle] : (char)('1' + cell), caps); buf[len - 1] = ch; } } else if (len < max_len) { - char ch = group[0]; - if (caps && ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A'; + char ch = kbApplyCaps(group[0], caps); buf[len++] = ch; buf[len] = '\0'; t9_cell = cell; @@ -316,8 +320,7 @@ struct KeyboardWidget { t9_last_ms = millis(); } else if (row < rows) { if (len < max_len) { - char ch = KB_CHARS[page][row][col]; - if (caps && ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A'; + char ch = kbApplyCaps(KB_CHARS[page][row][col], caps); buf[len++] = ch; buf[len] = '\0'; } diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index e703e3f4..a5b74d7e 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1209,29 +1209,27 @@ public: if (prefix[b] != 0) { filled = true; break; } } + ContactInfo ci; + bool has_contact = false; if (filled) { - ContactInfo ci; - bool found = false; for (int idx = 0; ; idx++) { ContactInfo c; if (!the_mesh.getContactByIdx(idx, c)) break; if (memcmp(c.id.pub_key, prefix, NodePrefs::FAVOURITE_PREFIX_LEN) == 0) { - ci = c; found = true; break; + ci = c; has_contact = true; break; } } - if (!found) { + if (!has_contact && _node_prefs) { // Pinned contact is gone — prefs outlived the contact list (e.g. a // wiped /contacts3; onContactRemoved only catches a live delete). - // Clear the stale slot at render time so it reverts to an empty "+" - // tile instead of showing "(gone)". Persisted once after the loop. - if (_node_prefs) - memset(_node_prefs->favourite_contacts[i], 0, NodePrefs::FAVOURITE_PREFIX_LEN); + // Clear the stale slot so it renders as an empty "+" tile (below) + // instead of "(gone)". Persisted once after the loop. + memset(_node_prefs->favourite_contacts[i], 0, NodePrefs::FAVOURITE_PREFIX_LEN); fav_changed = true; - int plus_y = cy + (cell_h - line_h) / 2; - display.drawTextCentered(cx + cell_w / 2, plus_y, "+"); - if (sel) display.setColor(DisplayDriver::LIGHT); - continue; } + } + + if (has_contact) { char name[24]; display.translateUTF8ToBlocks(name, ci.name, sizeof(name));