gamepad-focus

Drives an SVG UI's focus traversal from a VirtualGamepad — the missing wire between "the D-pad registers" and "the D-pad moves the highlight".

Everything either side of it already existed: box/surface expose focusMove / focusActivate / focusBack, and HardwareGamepadSource / XrGamepadSource produce a VirtualGamepad each frame. Nothing polled one and called the other, so a gamepad could light up the on-screen pad and still not touch the UI.

import { gamepadFocus, HardwareGamepadSource } from 'tosijs-3d'

const pad = new HardwareGamepadSource()
const stop = gamepadFocus({ poll: () => pad.poll(), target: panel })
// …later
stop()

Which controls, and why those

The D-pad and the menu button, deliberately: per the control conventions those are the inputs a VR controller set doesn't otherwise spend, so a UI can claim them without fighting locomotion or the gameplay bindings. The left stick is not used — it's how you walk.

control does
D-pad ↑↓←→ focusMove(dx, dy)
menu / buttonA focusActivate()
buttonB focusBack()

Edge-triggered, with a repeat

A gamepad reports held, not pressed, so polling raw state would fire every frame and send focus rocketing across the panel. Presses are edge-detected, then repeat on a delay-then-interval ramp (the typematic behaviour every keyboard has) so holding a direction walks steadily instead of either sprinting or stopping dead.

The timing is passed in as now rather than read from a clock, so the ramp is deterministic and testable — same discipline as the rest of the pure models here.