Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/intercept key handler #247

Merged
merged 7 commits into from
Jan 6, 2025
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ _8 nov 2024_
- Fixed issue with watching nested state variables and global state
- Upgraded to renderer 2.6.2
- Fixed issue with white background for Elements with falsy src attribute
- Fixed issue with calling focus on component that already is focussed
- Fixed issue with calling focus on component that already is focused


## v1.9.2
Expand Down
10 changes: 10 additions & 0 deletions docs/components/user_input.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ When a component handles a key press by having a corresponding function specifie
}
```

## Intercepting key input

In addition to the Event handling chain explained above. Blits offers the option to _intercept_ key presses at the root level of the Application, before they reach the currently focused Component. This can be useful in certain situation where you want to globally disable all key presses, or when implementing an override key press handler.

The `intercept()` input-method can only be implemented in the `Blits.Application`-component. When present, the method acts as a _catch-all_ method, and will be executed for _all_ key presses. It receives the `KeyboardEvent` as its argument, allowing you to execute logic based on the key being pressed.

Only when the `intercept()` input-method returns the `KeyboardEvent` (possibly modified), the keypress will continue to be handled (by the currently focused Component).

The `intercept`-method can also be an asynchronous method.

## Key-up handling

The functions specified in the `input` configuration are invoked when a key is _pressed down_ (i.e. the `keydown` event listener). But sometimes you may also want to execute some logic when a key is _released_ (i.e. the `keyup` event listener).
Expand Down
16 changes: 14 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,26 @@ declare module '@lightningjs/blits' {
}

export interface Input {
[key: string]: (event: KeyboardEvent) => void | undefined,
[key: string]: (event: KeyboardEvent) => void | undefined | unknown,
/**
* Catch all input function
*
* Will be invoked when there is no dedicated function for a certain key
*/
// @ts-ignore
any?: (event: KeyboardEvent) => void
any?: (event: KeyboardEvent) => void,
/**
* Intercept key presses on the root Application component before being handled
* by the currently focused component.
*
* Only when a KeyboardEvent (the original one, or a modified one) is returned from the
* intercept function, the Input event is passed on to the Component with focus.
*
* The intercept function can be asynchronous.
*
* Note: the intercept input handler is only available on the Root App component (i.e. Blits.Application)
*/
intercept?: (event: KeyboardEvent) => KeyboardEvent | Promise<KeyboardEvent | any> | any
}

export interface Log {
Expand Down
9 changes: 8 additions & 1 deletion src/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,15 @@ const Application = (config) => {
config.hooks[symbols.init] = function () {
const keyMap = { ...defaultKeyMap, ...Settings.get('keymap', {}) }

keyDownHandler = (e) => {
keyDownHandler = async (e) => {
const key = keyMap[e.key] || keyMap[e.keyCode] || e.key || e.keyCode
// intercept key press if specified in main Application component
if (this[symbols.inputEvents].intercept !== undefined) {
e = await this[symbols.inputEvents].intercept.call(this, e)
// only pass on the key press to focused component when keyboard event is returned
if (e instanceof KeyboardEvent === false) return
}

Focus.input(key, e)
clearTimeout(holdTimeout)
holdTimeout = setTimeout(() => {
Expand Down