refactor(ui): audit-pass fixes + dead-code/redundancy cleanup

Fixes from a full ui-new audit (OLED + e-ink both build green):
- remove dead DisplayDriver::drawScrollArrows (all lists use drawScrollIndicator)
- SettingsScreen selection bar -> lineStep()-1, matching the drawList screens
- HomeScreen: don't force the 1s blink refresh on the CLOCK page (status icons
  are hidden there anyway)
- FullscreenMsgView: clamp KEY_DOWN scroll to a cached _max_scroll instead of
  over-incrementing and leaning on the next render to clamp
- DataStore: clamp use_lemon_font (>1 -> 0) on load, like the other enum fields
- move the ~1.5KB wrap scratch (trans/lines) off the render stack into shared
  file-scope statics in FullscreenMsgView (single-threaded UI; the fullscreen
  view and history list never lay out in the same frame)

Cleanup:
- drop dead members RingtoneEditorScreen::DUR_VALS and HomeScreen::sensors_scroll
- delete redundant manual scroll-clamps in handleInput across the drawList
  screens (drawList already reclamps each render); remove the now-unused _visible
  from QuickMsgScreen/NearbyScreen/BotScreen and BotScreen::scrollToSel()

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-06-16 08:54:11 +02:00
co-authored by Claude Opus 4.8
parent da161d49d2
commit f591cddf4f
10 changed files with 60 additions and 107 deletions
@@ -6,11 +6,22 @@
static const int FS_CHARS_MAX = 80; // max bytes per wrapped line
// Shared, non-reentrant wrap scratch. The UI renders on a single thread, and the
// fullscreen message view and the history list are never laid out in the same
// frame (opening the fullscreen view early-returns before the list loop runs),
// so one static buffer serves both. This keeps ~1.5 KB of line buffers off the
// render-call stack (see CODE_REVIEW.md "Render-path stack peak") at the cost of
// a fixed RAM allocation. Only used inside render()/wrap helpers — never across
// a yield, so the single instance is safe.
static char s_wrap_trans[512];
static char s_wrap_lines[12][FS_CHARS_MAX];
struct FullscreenMsgView {
int scroll;
bool active;
int _max_scroll; // last value computed in render(); bounds KEY_DOWN
FullscreenMsgView() : scroll(0), active(false) {}
FullscreenMsgView() : scroll(0), active(false), _max_scroll(0) {}
enum Result { NONE, PREV, NEXT, CLOSE, REPLY };
@@ -100,22 +111,21 @@ struct FullscreenMsgView {
}
display.setColor(DisplayDriver::LIGHT);
char trans_text[512];
display.translateUTF8ToBlocks(trans_text, body, sizeof(trans_text));
char lines[12][FS_CHARS_MAX];
int lcount = wrapLines(display, trans_text, max_px, lines, 12);
display.translateUTF8ToBlocks(s_wrap_trans, body, sizeof(s_wrap_trans));
int lcount = wrapLines(display, s_wrap_trans, max_px, s_wrap_lines, 12);
// Reserve the right-edge column for the scroll indicator and re-wrap so text
// can't run under it. Reserve appears only once the list overflows, and a
// narrower second pass only ever yields more lines — so it stays overflowing.
int reserve = scrollIndicatorReserve(display, lcount, visible);
if (reserve > 0)
lcount = wrapLines(display, trans_text, max_px - reserve, lines, 12);
lcount = wrapLines(display, s_wrap_trans, max_px - reserve, s_wrap_lines, 12);
int max_scroll = lcount > visible ? lcount - visible : 0;
_max_scroll = max_scroll;
if (scroll > max_scroll) scroll = max_scroll;
for (int i = 0; i < visible && (scroll + i) < lcount; i++) {
display.setCursor(0, startY + i * lineH);
display.print(lines[scroll + i]);
display.print(s_wrap_lines[scroll + i]);
}
drawScrollIndicator(display, startY, visible * lineH, lcount, visible, scroll);
const int nav_y = display.height() - lineH;
@@ -132,7 +142,7 @@ struct FullscreenMsgView {
Result handleInput(char c) {
if (c == KEY_UP) { if (scroll > 0) scroll--; return NONE; }
if (c == KEY_DOWN) { scroll++; return NONE; }
if (c == KEY_DOWN) { if (scroll < _max_scroll) scroll++; return NONE; }
if (c == KEY_LEFT) return NEXT;
if (c == KEY_RIGHT) return PREV;
if (c == KEY_CONTEXT_MENU) return REPLY;