Skip to content

fix(ui): honor UITextButton keyboard bindings - #1732

Merged
obiot merged 1 commit into
melonjs:masterfrom
snowyukitty:fix/text-button-keyboard-binding
Oct 11, 2026
Merged

obiot merged 1 commit into
melonjs:masterfrom
snowyukitty:fix/text-button-keyboard-binding

Conversation

@snowyukitty

Copy link
Copy Markdown
Contributor

UITextButton accepts bindKey but never listens for keyboard input, so configuring a shortcut does not activate the button.

  • Honor an input.KEY keycode or an existing input action name without replacing the game's global bindings.
  • Click once per press and release only on the initiating physical key's keyup. The first input to press owns that press, so pointer and keyboard events cannot release each other.
  • Subscribe while activated and clear pending keyboard presses on blur or removal.

Keyboard onClick() and onRelease() callbacks receive no Pointer, as documented on bindKey. Dragging and tap-and-hold remain pointer operations.

Refs #1722, specifically the existing button's bindKey item.

Validation:

pnpm exec vitest run --config packages/melonjs/vitest.config.ts tests/uitextbutton.spec.js tests/ui-interaction.spec.js tests/keyboard.spec.js
pnpm lint
pnpm build
pnpm test

The new browser regressions exercise numeric and action shortcuts, browser key events, repeat suppression, physical-key release, pointer coexistence, clickability, blur, removal, queued callbacks and reactivation. The initial eight keyboard regressions fail on the upstream base and pass with the fix.

Full suite: 7,919 tests passed, 11 skipped across 325 test files.

@obiot

obiot commented Oct 11, 2026

Copy link
Copy Markdown
Member

Oh nice finding !

@obiot
obiot merged commit 47f801d into melonjs:master Oct 11, 2026
3 checks passed
obiot added a commit that referenced this pull request Oct 11, 2026
The entry from #1732 describes the feature but not the two things a
reader has to act on, and both only bite buttons that already exist. A
shortcut calls the handlers with NO pointer, so an `onClick(event)`
written back when only a pointer could press it throws the first time
someone uses the key. And losing focus cancels a held key without
calling `onRelease()`, so a button that fires while held keeps firing
unless it also stops on `event.BLUR`.

Extended rather than added as a second entry: 20.9 is unreleased, it is
the same change, and @snowyukitty's credit stays on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t
obiot added a commit that referenced this pull request Oct 11, 2026
#1733)

* UI: pin the keyboard unsubscribe, document what a shortcut does not do

Follow-up to #1732.

Thirteen mutations against its suite; it caught eight. The one worth
fixing is that deleting all three `off()` calls in `onDeactivateEvent`
leaves every test green. `keyboardActive` already gates both handlers,
so dropping the unsubscribe changes no BEHAVIOUR at all, which is
exactly why the behavioural tests cannot see it: what it changes is that
each add and remove cycle leaves two listeners on the global bus, and
every later key press walks all of them. Measured at twenty leaked
KEYDOWN listeners over twenty cycles. The new test counts net
subscriptions, which is the only thing that notices.

The four other survivors are guards no current path reaches rather than
defects, so they stay: the repeat suppression is already covered by the
sibling `!this.released`, a default `bindKey` of -1 cannot equal a real
keycode, an empty string cannot equal an action name, and nothing
activates a button twice without deactivating it.

The docs gained the two things a reader would otherwise learn at
runtime. A shortcut calls `onClick()` with NO pointer, so a handler
written against a pointer, reading `event.gameX`, throws the first time
someone presses the key: that is the upgrade hazard for buttons that
predate this, and it is now the first thing the skill says. And losing
focus cancels a held key WITHOUT calling `onRelease()`, so code using
the pair as press and depress has to stop on `event.BLUR` as well or it
keeps firing while the window is away.

Also corrects "respect isClickable" on the property doc: it gates the
press, not the release, which is what stops a button disabled mid-hold
from sticking down. Their own test pins that, the doc said otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t

* CHANGELOG: say what binding a key to an existing button costs

The entry from #1732 describes the feature but not the two things a
reader has to act on, and both only bite buttons that already exist. A
shortcut calls the handlers with NO pointer, so an `onClick(event)`
written back when only a pointer could press it throws the first time
someone uses the key. And losing focus cancels a held key without
calling `onRelease()`, so a button that fires while held keeps firing
unless it also stops on `event.BLUR`.

Extended rather than added as a second entry: 20.9 is unreleased, it is
the same change, and @snowyukitty's credit stays on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants