Scene2D actors and platform accessibility services: where should widget roles, labels, and focus order live in a libgdx game?
0 reputation · 07 Apr 2021, 11:51 UTC
0 reputation · 07 Apr 2021, 11:51 UTC
I'm designing the UI layer of a libgdx game built on Scene2D (labels, text buttons, sliders, and a few custom actors rendered with textures). I want the user-facing workflow to be usable with screen readers and keyboard/controller focus navigation across Android, iOS, desktop, and GWT backends.
My understanding is that libgdx has no unified, backend-neutral accessibility API for Scene2D: Android exposes a mature accessibility model, desktop and iOS differ, and GWT renders to canvas, which the browser cannot interpret semantically on its own. So any accessible name, role, state, or focus order has to be modeled on my side and translated per backend.
The unresolved design decision is where that metadata should live. Option A: attach role/label/state data to Scene2D actors and write per-backend adapters that publish it to the OS. Option B: keep Scene2D purely visual and maintain a parallel accessibility tree at the application layer that mirrors the scene graph and forwards focus and change events.
Questions:
35275 reputation · 07 Apr 2021, 21:47 UTC
Keep role, label, state, and focus-order metadata on the Scene2D actors themselves (Option A), and write thin per-backend adapters that read that metadata and publish it to each platform's accessibility API. Your premise is correct: libgdx has no backend-neutral accessibility layer, so the model must live in your code regardless — the question is only whether it shadows the scene graph or rides inside it.
Actor subclass, or setUserObject()) that only adapters read.Option B is only worth considering if you need an accessibility representation that deliberately diverges from the scene graph (e.g., grouping several visual actors into one logical control). Even then, a hybrid — descriptors on actors plus a small override layer — is usually simpler than a full mirror.
Yes — this is the right sequencing. Implement logical navigation with libgdx's existing input handling (InputProcessor, Stage#setKeyboardFocus, gamepad polling) and verify tab/arrow/D-pad order purely in-game. Only after that order is stable should you wire platform announcements (TalkBack, VoiceOver, NVDA). Focus order is testable without any screen reader, and debugging announcement timing on top of a broken focus model is miserable.
Yes, mirroring into DOM is the standard approach for canvas-rendered UI: browsers expose semantics through the DOM accessibility tree, and a canvas is an opaque bitmap without it. The mirror elements carry role, aria-label, and state attributes, are visually concealed (off-screen positioning rather than display:none, which removes them from the accessibility tree), and are updated whenever actor state changes. Manage focus on the mirror elements so keyboard and screen-reader navigation stay aligned with your in-game focus model.
The real maintenance cost of Option A is synchronization: whenever text, enabled state, or visibility changes, the descriptor and the adapter must be notified. Route updates through a single change listener or a dirty-flag pass in act() rather than scattering update calls.
Assumption: this reflects long-standing platform behavior (TalkBack, UIAccessibility, ARIA); adapter API details should be checked against your libgdx version.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.