Skip to content

Add graphical display support#423

Merged
forntoh merged 33 commits into
masterfrom
feat/graphical-display
May 28, 2026
Merged

Add graphical display support#423
forntoh merged 33 commits into
masterfrom
feat/graphical-display

Conversation

@forntoh
Copy link
Copy Markdown
Owner

@forntoh forntoh commented May 27, 2026

Preview

Graphical display demo

Summary

  • add graphical display rendering through GraphicalDisplayRenderer and U8g2-backed display adapters
  • add renderer lifecycle/context extensions, graphical item capabilities, fonts, glyphs, indicators, scrollbars, toggles, and value-selection rendering
  • add graphical examples for SSD1306 and ST7920 displays
  • document graphical display setup, item behavior, and README/homepage demos
  • fix input item value ownership so default-empty inputs do not crash on first typed character

Verification

Closes #382
Resolves #210
Resolves #200

Summary by CodeRabbit

  • New Features

    • Pixel-based graphical display support (U8g2) with custom fonts/glyphs, highlighted rows, checkbox/toggle rendering, list/submenu indicators, scrollbars, and buffered frame rendering.
    • Interactive Arduino examples for SSD1306 I2C and ST7920 SPI demonstrating menus, dynamic items, and telemetry.
  • Documentation

    • README and docs expanded with a graphical-renderer guide and side-by-side character/graphical examples and usage notes.

Review Change Stack

forntoh added 30 commits April 3, 2026 01:46
## Summary
- Route board-scoped example paths through a new `Prepare sketch paths`
step in `compile-arduino.yml` so hardware-specific sketches can be
conditionally included.
- Compile `examples/ST7920_SPI` only for ESP32 jobs (and only when the
example exists on the branch), while preserving universal sketch
compilation for all boards.
- Add a rendering docs note describing that CI compiles
hardware-specific examples only on compatible board jobs.

## Verification
- Workflow/config and docs only change; no unit-test or runtime code
paths were modified in this iteration.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added clarification on how continuous integration compiles
hardware-specific rendering examples exclusively on compatible board
jobs.

* **Chores**
* Improved CI/CD workflow to conditionally include hardware-specific
example sketches in compilation based on target board type.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Add RTTI-free extension hooks to core renderer/item APIs via
`MenuRenderer::queryExtension()` and `MenuItem::queryCapability()`.
- Introduce optional extension interfaces for frame lifecycle and
graphical context/capabilities (`FrameLifecycleRenderer`,
`GraphicalRendererContext`, `GraphicalMenuItem`) without adding
pixel-specific methods to base classes.
- Wire frame lifecycle hooks into menu flow: `MenuScreen::draw()` calls
`beginFrame()` and `LcdMenu` now calls `endFrame()` after handled
draw-triggering actions (`setScreen`, `show`, `refresh`, handled
`process`).
- Add unit tests verifying frame flush behavior on `refresh()` and
handled `process()` commands.
- Document the extension model in rendering docs.

## Verification
- Attempted to run Arduino unit tests locally (`bundle exec
arduino_ci.rb --skip-examples-compilation`), but local Ruby gem install
requires privileged system paths on this machine.
- Performed static diff review and kept PR scope to extension-core +
unit tests + docs only.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

# Release Notes

* **New Features**
* Introduced an extension mechanism allowing renderers to expose
optional capabilities and frame lifecycle hooks.
* Added support for menu items to discover and expose
renderer-compatible capabilities.
* Enabled specialized renderers to participate in frame lifecycle
management for optimized buffered drawing.

* **Documentation**
* Added documentation defining the extension mechanism and capability
query approach for advanced renderers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- add a pixel-display foundation with `GraphicalDisplayInterface`,
`U8g2DisplayAdapter`, and `GraphicalDisplayRenderer`
- introduce graphical renderer extension helpers
(`GraphicalIndicatorRenderer`, `GraphicalValueSelectionRenderer`,
`GraphicalItemFont`) and extend `GraphicalMenuItem`
- wire `MenuScreen` to dynamic row/column context
(`getMaxRows/getMaxCols`) so graphical renderers can compute
viewport/value layout
- add graphical renderer docs and new examples for `SSD1306_I2C` and
`ST7920_SPI`
- update README and PlatformIO dependencies to include U8g2 usage for
graphical examples

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added support for pixel-addressable graphical displays via U8g2
library integration
* New interactive menu examples for SSD1306 (I2C) and ST7920 (SPI)
displays with features like font metrics, scrollbars, and per-item
custom fonts

* **Documentation**
* Updated README specifying built-in graphical display support with
concrete module examples
* Added documentation page for graphical display renderer with features
overview and C++ usage example

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- teach `MenuScreen` to populate optional `GraphicalRendererContext`
state (viewport, active item, value-area width) before drawing and
polling
- clamp cursor/view using dynamic `getMaxRows()` / `getMaxCols()` so
graphical font changes cannot desync visible ranges
- preserve draw-owned frame lifecycle (`beginFrame`/`endFrame`) while
keeping poll flush behavior unchanged in `LcdMenu`
- update unit tests for the new clamped-view behavior and add docs note
under graphical renderer integration

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Menu now properly integrates with graphical renderers, providing
correct viewport context and accounting for varying font sizes across
menu items.
* Improved menu navigation to properly handle non-selectable items and
adjust view positioning.

* **Bug Fixes**
  * Enhanced cursor bounds checking and viewport recalculation logic.

* **Documentation**
* Added documentation explaining Menu integration with graphical display
renderers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Add `GraphicalMenuItem` capability support across core items and
widget-backed items, including graphical value-width measurement,
toggle/list indicators, and a `BasicItem` implementation for
`ITEM_BASIC`/`ITEM_LABEL`.
- Improve widget/item drawing safety for graphical rendering by
hardening buffer handling (`ITEM_DRAW_BUFFER_SIZE` growth, null-safe
bool widget formatting) and exposing optional
`GraphicalValueSelectionRenderer` extension hooks.
- Add unit coverage for new item capabilities in `test/ItemValue.cpp`
and document the new graphical item capability model in
`docs/source/overview/rendering/graphical-display.rst`.
## Summary
- Extend `ItemInput` with `GraphicalMenuItem` capability support,
including graphical value-width measurement and tight selection-box
behavior.
- Add optional `GraphicalValueSelectionRenderer` integration for
`ItemInput` and `ItemInputCharset` so graphical renderers can highlight
active character selection during editing.
- Update item docs for `input` and `input-charset`, and add unit
coverage in `test/LcdMenu.cpp` for graphical input capability exposure
and selection-extension edit flow.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Input items now support graphical rendering with visual value
selection highlighting in the display area.

* **Documentation**
* Added documentation describing graphical rendering behavior for input
items and character selection display.

* **Tests**
* Added unit tests for graphical rendering functionality and visual
feedback verification.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Extend `GraphicalDisplayRenderer` to implement and expose
`GraphicalValueSelectionRenderer` via `queryExtension()`.
- Add value-selection state handling in the renderer draw path so
editing overlays can highlight selected value substrings.
- Document the renderer hook in `graphical-display` docs and add unit
coverage in `test/GraphicalDisplayRenderer.cpp` to verify extension
exposure.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added visual selection highlighting support for graphical displays.
Text input and character selection components now display an active
selection indicator with a highlight box, providing clearer visual
feedback when editing text on graphical display interfaces.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Tighten graphical renderer font metric and row-height handling to
match the snapshot behavior.
- Render graphical list/submenu indicators with focus-aware draw colors.
- Add focused renderer tests for row height and indicator color
behavior.

## Checks
- git diff --check
- pio run -e esp32

## Notes
- `pio run -e uno` currently fails because base `src/main.cpp` builds
the graphical/U8g2 showcase and exceeds Uno memory/flash; this branch
does not change `src/main.cpp`.
- `pio test -e uno` is blocked by missing
`ArduinoUnitTests.h`/`Godmode.h` in the current PlatformIO test setup.
## Summary
- Clear the graphical scrollbar track before drawing the handle.
- Clamp scrollbar track and scroll range math to avoid
underflow/division edge cases.
- Add focused renderer coverage for scrollbar track clearing.

## Checks
- git diff --check
- pio run -e esp32

## Notes
- Unrelated local `diagram.json`, `wokwi.toml`, `bin/`, and
`docs/source/reference/samples/` changes were not included.
## Summary
- Account for left padding, scrollbar inset, and cursor icon width in
graphical effective columns.
- Make max column calculation fall back to the active display font width
when cached width is unset.
- Add focused renderer coverage for effective/max column calculations.

## Checks
- git diff --check
- pio run -e esp32
## Summary
- move graphical frame finalization to LcdMenu entry points for
setScreen, handled process calls, show, and refresh
- stop MenuScreen from ending frames directly during draw
- update graphical viewport context before item command handling
- adjust lifecycle tests for centralized ownership

## Checks
- git diff --check
- pio run -e esp32

## Notes
- intentionally skips low-value timeout debounce drift and
doc-comment-only drift
- intentionally skips broad viewport structural churn without clear
behavior value
- only src/LcdMenu.cpp, src/MenuScreen.cpp, and test/LcdMenu.cpp are
included
## Summary
- port remaining graphical renderer drawItem text/value windowing
behavior
- improve cursor placeholder, blinker, toggle, list/submenu indicator,
row-height, and scrollbar handling
- update focused GraphicalDisplayRenderer tests, including review-fix
coverage for short values

## Review fixes
- avoid reserving one-third of the row for short/empty values when
valueAreaWidth is unset
- align draw color capture test stub with its expanded capture buffer

## Checks
- git diff --check
- clang-format --dry-run --Werror
src/renderer/GraphicalDisplayRenderer.cpp
src/renderer/GraphicalDisplayRenderer.h
test/GraphicalDisplayRenderer.cpp
- pio run -e esp32

## Notes
- pio test -e esp32 is still blocked locally by missing
ArduinoUnitTests.h / Godmode.h in the current repo test harness
- intentionally skipped snapshot deletion of
test/GraphicalDisplayRenderer.cpp because focused coverage is valuable
- only GraphicalDisplayRenderer files and
test/GraphicalDisplayRenderer.cpp are included
## Summary
- always clear graphical value selection after input item rendering
- remove stale implementation-specific input graphical docs/comments
- strengthen LcdMenu input graphical selection coverage to verify
selection set/clear behavior

## Checks
- git diff --check
- clang-format --dry-run --Werror src/ItemInput.h src/ItemInputCharset.h
test/LcdMenu.cpp
- pio run -e esp32
- make html (from docs/; succeeded with existing unrelated warnings)

## Notes
- focused pio test path remains blocked locally by missing
ArduinoUnitTests.h / Godmode.h in the current PlatformIO test harness
- intentionally skipped snapshot deletions in test/ItemValue.cpp as
low-value/unrelated to this bundle
- only scoped input headers, item docs pages, and test/LcdMenu.cpp are
included
## Summary
- track whether `ItemInput` owns its value buffer before freeing it
- make default-empty inputs use owned writable storage
- clone external values before mutating them in input and charset edit
paths
- add regression coverage for default-empty, static, and stack-backed
input values

## Verification
- `pio run -e esp32` passed
- `pio test -e esp32` fails before running tests due missing known
harness headers: `ArduinoUnitTests.h` and `Godmode.h`
- `pio run -e uno` fails due existing program/RAM size overflow for the
current app build
## Summary
- expand GraphicalDisplayRenderer documentation with U8g2 display
support, fonts, glyphs, layout, highlighting, indicators, scrollbar, and
frame buffering
- convert existing character-display media examples to tabbed sections
with future graphical-display placeholders
- preserve existing 16x2 I2C character display screenshots/GIFs and
avoid broken image references for missing graphical assets

## Checks
- make html from docs/ succeeded with existing unrelated warnings
- git diff --cached --check before commit

## Notes
- graphical display placeholder tabs describe expected future filenames
only; no missing image directives were added
- unrelated local files remain uncommitted/excluded: diagram.json,
wokwi.toml, examples/Widgets/Widgets.ino, bin/,
docs/source/reference/samples/, solo.yml
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

This PR adds a graphical rendering subsystem for pixel-addressable displays: GraphicalDisplayInterface and U8g2DisplayAdapter, a full GraphicalDisplayRenderer with frame lifecycle and selection highlighting, renderer extension/capability hooks, GraphicalMenuItem support across core items, MenuScreen/Menu lifecycle integration, example Arduino sketches (SSD1306_I2C, ST7920_SPI), platform/CI updates, extensive documentation, and unit tests.

Sequence Diagram(s)

sequenceDiagram
  participant Keyboard as KeyboardAdapter
  participant Menu as MenuScreen / LcdMenu
  participant Renderer as GraphicalDisplayRenderer
  participant Display as U8g2DisplayAdapter
  Keyboard->>Menu: observe()/process input
  Menu->>Renderer: setViewportContext / setActiveItem / draw
  Renderer->>Display: setFont / drawItem / setValueSelection
  Renderer->>Display: beginFrame / clearBuffer / sendBuffer
  Display-->>Renderer: metrics (getTextWidth, fontHeight)
  Renderer-->>Menu: report redraw / cursor moves
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • forntoh/LcdMenu#406: Overlaps CI workflow changes for conditionally compiling the ST7920 example and sketch-path preparation.
  • forntoh/LcdMenu#411: Overlaps ItemInput graphical value-selection integration and related edits.
  • forntoh/LcdMenu#409: Related MenuScreen graphical viewport/value-area integration and test expectation updates.

Suggested reviewers

  • ShishkinDmitriy
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/graphical-display

@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation feature New feature labels May 27, 2026
Comment thread docs/source/overview/rendering/index.rst Outdated
coderabbitai[bot]

This comment was marked as resolved.

@forntoh forntoh requested a review from ShishkinDmitriy May 27, 2026 21:26
coderabbitai[bot]

This comment was marked as resolved.

@github-actions
Copy link
Copy Markdown
Contributor

Memory usage change @ 01b473f

Board flash % RAM for global variables %
STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 🔺 +1128 - +3112 +1.72 - +4.75 0 - 0 0.0 - 0.0
arduino:avr:uno 🔺 +1706 - +4166 +5.29 - +12.92 🔺 +32 - +392 +1.56 - +19.14
arduino:samd:mkr1000 🔺 +1140 - +3624 +0.43 - +1.38 🔺 0 - +368 0.0 - +1.12
esp32:esp32:esp32 🔺 +2036 - +4472 +0.16 - +0.34 0 - 0 0.0 - 0.0
esp8266:esp8266:huzzah N/A N/A N/A N/A
Click for full report table
Board examples/Basic
flash
% examples/Basic
RAM for global variables
% examples/ButtonAdapter
flash
% examples/ButtonAdapter
RAM for global variables
% examples/Callbacks
flash
% examples/Callbacks
RAM for global variables
% examples/InputRotary
flash
% examples/InputRotary
RAM for global variables
% examples/IntFloatValues
flash
% examples/IntFloatValues
RAM for global variables
% examples/KeyboardAdapter
flash
% examples/KeyboardAdapter
RAM for global variables
% examples/List
flash
% examples/List
RAM for global variables
% examples/SimpleRotary
flash
% examples/SimpleRotary
RAM for global variables
% examples/SSD1803A_I2C
flash
% examples/SSD1803A_I2C
RAM for global variables
% examples/UseByRef
flash
% examples/UseByRef
RAM for global variables
% examples/DynamicMenu
flash
% examples/DynamicMenu
RAM for global variables
% examples/Widgets
flash
% examples/Widgets
RAM for global variables
% examples/RTOS
flash
% examples/RTOS
RAM for global variables
% examples/ST7920_SPI
flash
% examples/ST7920_SPI
RAM for global variables
%
STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 1128 1.72 0 0.0 1344 2.05 0 0.0 1344 2.05 0 0.0 3048 4.65 0 0.0 1992 3.04 0 0.0 2828 4.32 0 0.0 1944 2.97 0 0.0 2232 3.41 0 0.0 1128 1.72 0 0.0 3112 4.75 0 0.0 1328 2.03 0 0.0 1952 2.98 0 0.0
arduino:avr:uno 1706 5.29 32 1.56 1938 6.01 62 3.03 1936 6.0 62 3.03 4166 12.92 150 7.32 2714 8.41 176 8.59 3974 12.32 102 4.98 2632 8.16 148 7.23 2932 9.09 206 10.06 1706 5.29 32 1.56 3716 11.52 392 19.14 1902 5.9 80 3.91 2722 8.44 156 7.62 2438 7.56 112 5.47
arduino:samd:mkr1000 1140 0.43 0 0.0 1372 0.52 0 0.0 1368 0.52 0 0.0 3624 1.38 368 1.12 2124 0.81 0 0.0 3376 1.29 368 1.12 2028 0.77 0 0.0 2356 0.9 0 0.0 1140 0.43 0 0.0 3412 1.3 0 0.0 1384 0.53 0 0.0 2064 0.79 0 0.0
esp32:esp32:esp32 2036 0.16 0 0.0 2348 0.18 0 0.0 2344 0.18 0 0.0 4280 0.33 0 0.0 3132 0.24 0 0.0 4008 0.31 0 0.0 3052 0.23 0 0.0 3476 0.27 0 0.0 2036 0.16 0 0.0 4472 0.34 0 0.0 2304 0.18 0 0.0 3068 0.23 0 0.0 2916 0.22 0 0.0 N/A N/A N/A N/A
esp8266:esp8266:huzzah N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A
Click for full report CSV
Board,examples/Basic<br>flash,%,examples/Basic<br>RAM for global variables,%,examples/ButtonAdapter<br>flash,%,examples/ButtonAdapter<br>RAM for global variables,%,examples/Callbacks<br>flash,%,examples/Callbacks<br>RAM for global variables,%,examples/InputRotary<br>flash,%,examples/InputRotary<br>RAM for global variables,%,examples/IntFloatValues<br>flash,%,examples/IntFloatValues<br>RAM for global variables,%,examples/KeyboardAdapter<br>flash,%,examples/KeyboardAdapter<br>RAM for global variables,%,examples/List<br>flash,%,examples/List<br>RAM for global variables,%,examples/SimpleRotary<br>flash,%,examples/SimpleRotary<br>RAM for global variables,%,examples/SSD1803A_I2C<br>flash,%,examples/SSD1803A_I2C<br>RAM for global variables,%,examples/UseByRef<br>flash,%,examples/UseByRef<br>RAM for global variables,%,examples/DynamicMenu<br>flash,%,examples/DynamicMenu<br>RAM for global variables,%,examples/Widgets<br>flash,%,examples/Widgets<br>RAM for global variables,%,examples/RTOS<br>flash,%,examples/RTOS<br>RAM for global variables,%,examples/ST7920_SPI<br>flash,%,examples/ST7920_SPI<br>RAM for global variables,%
STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8,1128,1.72,0,0.0,1344,2.05,0,0.0,1344,2.05,0,0.0,3048,4.65,0,0.0,1992,3.04,0,0.0,2828,4.32,0,0.0,1944,2.97,0,0.0,2232,3.41,0,0.0,1128,1.72,0,0.0,3112,4.75,0,0.0,1328,2.03,0,0.0,1952,2.98,0,0.0
arduino:avr:uno,1706,5.29,32,1.56,1938,6.01,62,3.03,1936,6.0,62,3.03,4166,12.92,150,7.32,2714,8.41,176,8.59,3974,12.32,102,4.98,2632,8.16,148,7.23,2932,9.09,206,10.06,1706,5.29,32,1.56,3716,11.52,392,19.14,1902,5.9,80,3.91,2722,8.44,156,7.62,2438,7.56,112,5.47
arduino:samd:mkr1000,1140,0.43,0,0.0,1372,0.52,0,0.0,1368,0.52,0,0.0,3624,1.38,368,1.12,2124,0.81,0,0.0,3376,1.29,368,1.12,2028,0.77,0,0.0,2356,0.9,0,0.0,1140,0.43,0,0.0,3412,1.3,0,0.0,1384,0.53,0,0.0,2064,0.79,0,0.0,,,,
esp32:esp32:esp32,2036,0.16,0,0.0,2348,0.18,0,0.0,2344,0.18,0,0.0,4280,0.33,0,0.0,3132,0.24,0,0.0,4008,0.31,0,0.0,3052,0.23,0,0.0,3476,0.27,0,0.0,2036,0.16,0,0.0,4472,0.34,0,0.0,2304,0.18,0,0.0,3068,0.23,0,0.0,2916,0.22,0,0.0,N/A,N/A,N/A,N/A
esp8266:esp8266:huzzah,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,,,,,,,,

@forntoh forntoh merged commit e311e03 into master May 28, 2026
15 of 16 checks passed
@forntoh forntoh deleted the feat/graphical-display branch May 28, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for ST7920 LCD Display Add Display Interface implementation for SSD1322 OLED Display

1 participant