Summary
On the reference 1.3″ 128×64 OLED, patch and category names are hard-truncated (~17–20 chars on the main line). Many Surge patches have longer descriptive names, so users can't read the full title while browsing.
Suggestion from Dave @ PragmaFlow: use horizontal scrolling (marquee) with a virtual viewport — render the full text once to an off-screen buffer, then shift a 128px window over time. Avoids re-rendering text every frame and keeps CPU/I2C load reasonable on the Pi.
Current behavior
PatchDisplay.show_patch() in patch_browser_ui.py truncates with string slicing:
- Current selection (large font): patch names → 17 chars, categories → 19 chars
- Header: ~14 chars (leaves room for
[n/total] count)
- Preview lines: ~20 chars
Example: "Warm Analog Bass - Filter Sweep v2" displays as "Warm Analog Bass".
Proposed behavior
- Only scroll when text overflows the visible width — short names stay static (no annoying motion on everything).
- Primary target: the
> Current Patch / > Current Category line (line 2, large font).
- Optional follow-up: header (
Category [n/total]) and loading screen patch name.
- Pause while browsing: disable or freeze marquee while the user is actively rotating the encoder (avoid visual noise during fast scroll + load debounce).
- Marquee timing: brief pause at start → scroll left → brief pause at end → repeat (or single pass then stop — TBD during implementation).
Implementation sketch
In PatchDisplay (patch_browser_ui.py):
- Measure rendered text width with PIL (
font.getbbox() / textlength).
- If width ≤ 128px, draw normally (current behavior).
- If width > 128px:
- Render full string once to an off-screen
Image (width = text width + padding).
- Each animation tick, blit a 128px horizontal slice at offset
x into the canvas.
- Increment
x slowly (e.g. 1–2 px per frame, ~10 fps timer — tune for readability vs I2C bandwidth).
No change to encoder logic; this is display-layer only.
Why virtual viewport
Re-drawing text via draw.text() every frame is wasteful. Pre-rendering once and shifting a crop is the standard embedded/OLED marquee pattern — lower CPU, predictable I2C transfer size.
Acceptance criteria
Priority
Medium — meaningful readability improvement, but after encoder/navigation reliability work (see README / PATCH_BROWSER_UI.md).
Credit
Suggested by Dave @ PragmaFlow.
Summary
On the reference 1.3″ 128×64 OLED, patch and category names are hard-truncated (~17–20 chars on the main line). Many Surge patches have longer descriptive names, so users can't read the full title while browsing.
Suggestion from Dave @ PragmaFlow: use horizontal scrolling (marquee) with a virtual viewport — render the full text once to an off-screen buffer, then shift a 128px window over time. Avoids re-rendering text every frame and keeps CPU/I2C load reasonable on the Pi.
Current behavior
PatchDisplay.show_patch()inpatch_browser_ui.pytruncates with string slicing:[n/total]count)Example:
"Warm Analog Bass - Filter Sweep v2"displays as"Warm Analog Bass".Proposed behavior
> Current Patch/> Current Categoryline (line 2, large font).Category [n/total]) and loading screen patch name.Implementation sketch
In
PatchDisplay(patch_browser_ui.py):font.getbbox()/textlength).Image(width = text width + padding).xinto the canvas.xslowly (e.g. 1–2 px per frame, ~10 fps timer — tune for readability vs I2C bandwidth).No change to encoder logic; this is display-layer only.
Why virtual viewport
Re-drawing text via
draw.text()every frame is wasteful. Pre-rendering once and shifting a crop is the standard embedded/OLED marquee pattern — lower CPU, predictable I2C transfer size.Acceptance criteria
Priority
Medium — meaningful readability improvement, but after encoder/navigation reliability work (see README / PATCH_BROWSER_UI.md).
Credit
Suggested by Dave @ PragmaFlow.