From e300db790811f60f9aa71c38ed7cf1d26e2c9f20 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:12:23 +0200 Subject: [PATCH] fix(ui): marquee's hold no longer dominates a barely-overflowing label User report: a repeater path popup's short "?A1B2C3D4"-style hop tags (often just a couple pixels over the box width) felt stuck rather than scrolling, while longer names (Nodes list) already felt right after the recent marqueeHoldMs() speedup. Root cause: the swing marquee's hold applies uniformly regardless of how much text is actually hidden. When only 1-2 codepoints overflow, the full cycle is hold + a single, barely-there step + hold + step back -- nearly all hold, for almost no new information revealed each side. New marqueeHoldForSkip(skip_cp) scales the hold down (to 1/3 or 1/2) when the computed reveal distance is small; longer overflows keep the full hold, since there's real text worth pausing to read. Co-Authored-By: Claude Sonnet 5 --- src/helpers/ui/DisplayDriver.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index 969d94d7..b119e9bb 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -348,6 +348,20 @@ public: virtual unsigned long marqueeHoldMs() { return isEink() ? 1200 : 700; } virtual uint8_t marqueeStepChars() { return isEink() ? 3 : 1; } + // Scales the start/end hold down when only a codepoint or two is actually + // hidden -- with a fixed hold, a label that overflows by just that much + // (e.g. a short "?A1B2C3D4" hex hop tag squeezed by a couple of pixels) + // spends nearly its whole cycle sitting still either side of a single, + // barely-there step, which reads as "stuck" rather than "scrolling" for + // how little extra text it actually reveals. Longer overflows keep the + // full hold, since there's real content worth pausing to read. + unsigned long marqueeHoldForSkip(uint16_t skip_cp) { + unsigned long full = marqueeHoldMs(); + if (skip_cp <= 2) return full / 3; + if (skip_cp <= 4) return full / 2; + return full; + } + // draw text with ellipsis if it exceeds max_width. Pass selected=true for // the row currently highlighted/focused by the user: instead of a static // ellipsis, an overflowing label then animates a "swing" marquee — holds at @@ -417,7 +431,6 @@ public: _marquee_max_w = max_width; _marquee_skip_cp = 0; _marquee_phase = 0; // hold at start - _marquee_next_at = now + marqueeHoldMs(); // Find the codepoint skip at which the remaining suffix's own width // already fits max_width — i.e. the fully-scrolled end position. @@ -431,6 +444,7 @@ public: if (full_width - removed_w <= max_width) break; } _marquee_max_skip_cp = cp_count; + _marquee_next_at = now + marqueeHoldForSkip(cp_count); } // Advance the state machine at most once per elapsed step/hold interval. @@ -444,7 +458,7 @@ public: if (_marquee_skip_cp >= _marquee_max_skip_cp) { _marquee_skip_cp = _marquee_max_skip_cp; _marquee_phase = 2; - _marquee_next_at = now + marqueeHoldMs(); + _marquee_next_at = now + marqueeHoldForSkip(_marquee_max_skip_cp); } else { _marquee_next_at = now + marqueeStepMs(); } @@ -455,7 +469,7 @@ public: if (_marquee_skip_cp <= step) { _marquee_skip_cp = 0; _marquee_phase = 0; - _marquee_next_at = now + marqueeHoldMs(); + _marquee_next_at = now + marqueeHoldForSkip(_marquee_max_skip_cp); } else { _marquee_skip_cp -= step; _marquee_next_at = now + marqueeStepMs();