Skip to content

Horizontal marquee scroll for long patch/category names on OLED #3

Description

@MitchSchwartz

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):

  1. Measure rendered text width with PIL (font.getbbox() / textlength).
  2. If width ≤ 128px, draw normally (current behavior).
  3. 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

  • Long patch names show full text via horizontal scroll on the main selection line
  • Short names (fit in 128px) do not scroll
  • Scrolling pauses or stops while encoder is actively rotating
  • No visible flicker or frame drops during idle marquee on Pi 5 + reference OLED
  • Loading indicator (dot) and loaded indicator (circle) still render correctly alongside scrolling text

Priority

Medium — meaningful readability improvement, but after encoder/navigation reliability work (see README / PATCH_BROWSER_UI.md).

Credit

Suggested by Dave @ PragmaFlow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions