Helix/Input
Virtual mouse/touch interactions and gestures.
Virtual mouse/touch interactions shared by Koya and Syncromesh. UI elements use onPress, onActivate, onHover, onDrag, onScroll, onLongPress, onDoubleActivate and onTransform (or their setOn… setters). Window/game handlers register through Helix/Input. All callbacks receive one event object. Keyboard, text input and OS data drag/drop use their existing APIs.
import * as Input from 'Helix/Input';
await Input.setHandlers(windowId, {
onDrag(event) {
if (event.phase === 'change') moveSelection(event.position);
if (event.phase === 'end' || event.phase === 'cancel') finishSelection();
},
onActivate(event) { select(event.world ?? event.position); }
});setHandlers(windowId, {onPress?, onActivate?, onHover?, onDrag?, onScroll?, onLongPress?, onDoubleActivate?, onTransform?}): Promise<boolean>setOptions(windowId, options): Promise<boolean>| Handler | Phases / recognition |
|---|---|
| onPress | start/change/end/cancel |
| onActivate | Discrete click/tap; release inside, within movement tolerance |
| onHover | enter/move/leave; independent mouse hover ancestry |
| onDrag | start/change/end/cancel after movement threshold |
| onScroll | Two-axis delta with pixel/step unit |
| onLongPress | Discrete sustained press; suppresses activation and later drag |
| onDoubleActivate | Second qualified activation; both singles are delivered immediately |
| onTransform | start/change/end/cancel; two-contact pan/pinch/rotate |
Events contain kind, applicable phase, windowId, interactionId and pointerIds (opaque strings), source, semantic button, targetId, ownerId, inside, modifiers and monotonic timestamp in milliseconds. position/start/delta/displacement use logical-window units; canvas and local use UI units, with local relative to the current owner. Local coordinates may be negative during capture. Syncromesh window handlers additionally receive world and startWorld. Transform events include translation, cumulative scale and rotation in radians; scroll includes scroll:{x,y}, unit and scrollSource. Positive scroll x/y means right/down. Unsupported metadata is absent.
Press owns a contact/button until end or cancel, including outside the element/window when native capture is available. Each started lifecycle has one terminal event. Ancestor drag/transform recognition may take over with an explicit superseded cancellation. Cancellation never activates. Leaving an element changes hover/inside, not ownership. Handler returns do not control routing: recognition and consumption occur natively.
Defaults: buttons:[primary], mouseTolerance:6, touchTolerance:10 (logical-window units), longPressMs:500, doubleActivateMs:300, dragAxis:any, blockInput:false. buttons accepts primary, secondary, middle, back, forward and other. Only registered recognizers run. blockInput allows an otherwise inactive hit target to block game input. Hover-only targets do not claim presses.
Movement beyond tolerance disqualifies activation. Long press requires staying inside and within tolerance; it wins against subsequent activation/drag. Double activation matches owner/device/button/time/distance and uses non-overlapping pairs. Transform uses the earliest two contacts under their nearest eligible common ancestor, consumes extra contacts, and consumes survivors until release after either defining contact ends.
Breaking migration: mouse/pointer global events and setOnMouse… callbacks have been removed. Replace button actions with onActivate, held state with onPress, dragging with onDrag and hover visuals with onHover. Remove mouse-out release workarounds. Registration changes, owner destruction/disable/detachment, window/device/capture loss, board detachment and OS drag handoff produce cancellation. Pending snapshots retain their original live callback; runtime disposal suppresses callbacks.
Single-contact events preserve available device metadata: pressure (SDL touch), contactSize:{major,minor} in logical-window units, and contactOrientation in radians clockwise from positive Y (Wayland touch). Metadata is absent when unsupported.
Upgrading from raw input callbacks? See the 0.5.0 → 0.6.0 migration guide.
Scroll routes from the hit element up its ancestor chain to the nearest registered onScroll handler. Children with press, hover, or activation handlers do not prevent a parent from being a scroll surface. blockInput: true blocks window/game fallback, but does not stop this ancestor lookup. A child with its own onScroll owns that scroll event; it is not also delivered to parent scroll handlers, and callback return values do not forward it. targetId identifies the hit element, while ownerId and local refer to the selected scroll handler’s element. Native scroll containers may pass input to an ancestor when they cannot move at the start of an input sequence; active sequences retain their owner.
Native scrolling is opt-in through element scroll options or UI.setScroll. It adds touch drag inertia, wheel/touchpad scrolling, and start/change/end/cancel notifications to onScroll; see Native scrolling. Without native scrolling, onScroll remains a raw virtual delta callback. Render-only animation transforms still do not move input bounds; native scrolling updates child layout bounds instead.