phaser/src/input/keyboard/KeyboardManager.js
2022-02-28 14:29:51 +00:00

438 lines
13 KiB
JavaScript

/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2022 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var ArrayRemove = require('../../utils/array/Remove');
var Class = require('../../utils/Class');
var GameEvents = require('../../core/events');
var InputEvents = require('../events');
var KeyCodes = require('../../input/keyboard/keys/KeyCodes');
var NOOP = require('../../utils/NOOP');
/**
* @classdesc
* The Keyboard Manager is a helper class that belongs to the global Input Manager.
*
* Its role is to listen for native DOM Keyboard Events and then store them for further processing by the Keyboard Plugin.
*
* You do not need to create this class directly, the Input Manager will create an instance of it automatically if keyboard
* input has been enabled in the Game Config.
*
* @class KeyboardManager
* @memberof Phaser.Input.Keyboard
* @constructor
* @since 3.16.0
*
* @param {Phaser.Input.InputManager} inputManager - A reference to the Input Manager.
*/
var KeyboardManager = new Class({
initialize:
function KeyboardManager (inputManager)
{
/**
* A reference to the Input Manager.
*
* @name Phaser.Input.Keyboard.KeyboardManager#manager
* @type {Phaser.Input.InputManager}
* @since 3.16.0
*/
this.manager = inputManager;
/**
* An internal event queue.
*
* @name Phaser.Input.Keyboard.KeyboardManager#queue
* @type {KeyboardEvent[]}
* @private
* @since 3.16.0
*/
this.queue = [];
/**
* A flag that controls if the non-modified keys, matching those stored in the `captures` array,
* have `preventDefault` called on them or not.
*
* A non-modified key is one that doesn't have a modifier key held down with it. The modifier keys are
* shift, control, alt and the meta key (Command on a Mac, the Windows Key on Windows).
* Therefore, if the user presses shift + r, it won't prevent this combination, because of the modifier.
* However, if the user presses just the r key on its own, it will have its event prevented.
*
* If you wish to stop capturing the keys, for example switching out to a DOM based element, then
* you can toggle this property at run-time.
*
* @name Phaser.Input.Keyboard.KeyboardManager#preventDefault
* @type {boolean}
* @since 3.16.0
*/
this.preventDefault = true;
/**
* An array of Key Code values that will automatically have `preventDefault` called on them,
* as long as the `KeyboardManager.preventDefault` boolean is set to `true`.
*
* By default the array is empty.
*
* The key must be non-modified when pressed in order to be captured.
*
* A non-modified key is one that doesn't have a modifier key held down with it. The modifier keys are
* shift, control, alt and the meta key (Command on a Mac, the Windows Key on Windows).
* Therefore, if the user presses shift + r, it won't prevent this combination, because of the modifier.
* However, if the user presses just the r key on its own, it will have its event prevented.
*
* If you wish to stop capturing the keys, for example switching out to a DOM based element, then
* you can toggle the `KeyboardManager.preventDefault` boolean at run-time.
*
* If you need more specific control, you can create Key objects and set the flag on each of those instead.
*
* This array can be populated via the Game Config by setting the `input.keyboard.capture` array, or you
* can call the `addCapture` method. See also `removeCapture` and `clearCaptures`.
*
* @name Phaser.Input.Keyboard.KeyboardManager#captures
* @type {number[]}
* @since 3.16.0
*/
this.captures = [];
/**
* A boolean that controls if the Keyboard Manager is enabled or not.
* Can be toggled on the fly.
*
* @name Phaser.Input.Keyboard.KeyboardManager#enabled
* @type {boolean}
* @default false
* @since 3.16.0
*/
this.enabled = false;
/**
* The Keyboard Event target, as defined in the Game Config.
* Typically the window in which the game is rendering, but can be any interactive DOM element.
*
* @name Phaser.Input.Keyboard.KeyboardManager#target
* @type {any}
* @since 3.16.0
*/
this.target;
/**
* The Key Down Event handler.
* This function is sent the native DOM KeyEvent.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Keyboard.KeyboardManager#onKeyDown
* @type {function}
* @since 3.16.00
*/
this.onKeyDown = NOOP;
/**
* The Key Up Event handler.
* This function is sent the native DOM KeyEvent.
* Initially empty and bound in the `startListeners` method.
*
* @name Phaser.Input.Keyboard.KeyboardManager#onKeyUp
* @type {function}
* @since 3.16.00
*/
this.onKeyUp = NOOP;
inputManager.events.once(InputEvents.MANAGER_BOOT, this.boot, this);
},
/**
* The Keyboard Manager boot process.
*
* @method Phaser.Input.Keyboard.KeyboardManager#boot
* @private
* @since 3.16.0
*/
boot: function ()
{
var config = this.manager.config;
this.enabled = config.inputKeyboard;
this.target = config.inputKeyboardEventTarget;
this.addCapture(config.inputKeyboardCapture);
if (!this.target && window)
{
this.target = window;
}
if (this.enabled && this.target)
{
this.startListeners();
}
this.manager.game.events.on(GameEvents.POST_STEP, this.postUpdate, this);
},
/**
* Starts the Keyboard Event listeners running.
* This is called automatically and does not need to be manually invoked.
*
* @method Phaser.Input.Keyboard.KeyboardManager#startListeners
* @since 3.16.0
*/
startListeners: function ()
{
var _this = this;
this.onKeyDown = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.queue.push(event);
_this.manager.events.emit(InputEvents.MANAGER_PROCESS);
var modified = (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey);
if (_this.preventDefault && !modified && _this.captures.indexOf(event.keyCode) > -1)
{
event.preventDefault();
}
};
this.onKeyUp = function (event)
{
if (event.defaultPrevented || !_this.enabled || !_this.manager)
{
// Do nothing if event already handled
return;
}
_this.queue.push(event);
_this.manager.events.emit(InputEvents.MANAGER_PROCESS);
var modified = (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey);
if (_this.preventDefault && !modified && _this.captures.indexOf(event.keyCode) > -1)
{
event.preventDefault();
}
};
var target = this.target;
if (target)
{
target.addEventListener('keydown', this.onKeyDown, false);
target.addEventListener('keyup', this.onKeyUp, false);
this.enabled = true;
}
},
/**
* Stops the Key Event listeners.
* This is called automatically and does not need to be manually invoked.
*
* @method Phaser.Input.Keyboard.KeyboardManager#stopListeners
* @since 3.16.0
*/
stopListeners: function ()
{
var target = this.target;
target.removeEventListener('keydown', this.onKeyDown, false);
target.removeEventListener('keyup', this.onKeyUp, false);
this.enabled = false;
},
/**
* Clears the event queue.
* Called automatically by the Input Manager.
*
* @method Phaser.Input.Keyboard.KeyboardManager#postUpdate
* @private
* @since 3.16.0
*/
postUpdate: function ()
{
this.queue = [];
},
/**
* By default when a key is pressed Phaser will not stop the event from propagating up to the browser.
* There are some keys this can be annoying for, like the arrow keys or space bar, which make the browser window scroll.
*
* This `addCapture` method enables consuming keyboard event for specific keys so it doesn't bubble up to the the browser
* and cause the default browser behavior.
*
* Please note that keyboard captures are global. This means that if you call this method from within a Scene, to say prevent
* the SPACE BAR from triggering a page scroll, then it will prevent it for any Scene in your game, not just the calling one.
*
* You can pass in a single key code value, or an array of key codes, or a string:
*
* ```javascript
* this.input.keyboard.addCapture(62);
* ```
*
* An array of key codes:
*
* ```javascript
* this.input.keyboard.addCapture([ 62, 63, 64 ]);
* ```
*
* Or a string:
*
* ```javascript
* this.input.keyboard.addCapture('W,S,A,D');
* ```
*
* To use non-alpha numeric keys, use a string, such as 'UP', 'SPACE' or 'LEFT'.
*
* You can also provide an array mixing both strings and key code integers.
*
* If there are active captures after calling this method, the `preventDefault` property is set to `true`.
*
* @method Phaser.Input.Keyboard.KeyboardManager#addCapture
* @since 3.16.0
*
* @param {(string|number|number[]|any[])} keycode - The Key Codes to enable capture for, preventing them reaching the browser.
*/
addCapture: function (keycode)
{
if (typeof keycode === 'string')
{
keycode = keycode.split(',');
}
if (!Array.isArray(keycode))
{
keycode = [ keycode ];
}
var captures = this.captures;
for (var i = 0; i < keycode.length; i++)
{
var code = keycode[i];
if (typeof code === 'string')
{
code = KeyCodes[code.trim().toUpperCase()];
}
if (captures.indexOf(code) === -1)
{
captures.push(code);
}
}
this.preventDefault = captures.length > 0;
},
/**
* Removes an existing key capture.
*
* Please note that keyboard captures are global. This means that if you call this method from within a Scene, to remove
* the capture of a key, then it will remove it for any Scene in your game, not just the calling one.
*
* You can pass in a single key code value, or an array of key codes, or a string:
*
* ```javascript
* this.input.keyboard.removeCapture(62);
* ```
*
* An array of key codes:
*
* ```javascript
* this.input.keyboard.removeCapture([ 62, 63, 64 ]);
* ```
*
* Or a string:
*
* ```javascript
* this.input.keyboard.removeCapture('W,S,A,D');
* ```
*
* To use non-alpha numeric keys, use a string, such as 'UP', 'SPACE' or 'LEFT'.
*
* You can also provide an array mixing both strings and key code integers.
*
* If there are no captures left after calling this method, the `preventDefault` property is set to `false`.
*
* @method Phaser.Input.Keyboard.KeyboardManager#removeCapture
* @since 3.16.0
*
* @param {(string|number|number[]|any[])} keycode - The Key Codes to disable capture for, allowing them reaching the browser again.
*/
removeCapture: function (keycode)
{
if (typeof keycode === 'string')
{
keycode = keycode.split(',');
}
if (!Array.isArray(keycode))
{
keycode = [ keycode ];
}
var captures = this.captures;
for (var i = 0; i < keycode.length; i++)
{
var code = keycode[i];
if (typeof code === 'string')
{
code = KeyCodes[code.toUpperCase()];
}
ArrayRemove(captures, code);
}
this.preventDefault = captures.length > 0;
},
/**
* Removes all keyboard captures and sets the `preventDefault` property to `false`.
*
* @method Phaser.Input.Keyboard.KeyboardManager#clearCaptures
* @since 3.16.0
*/
clearCaptures: function ()
{
this.captures = [];
this.preventDefault = false;
},
/**
* Destroys this Keyboard Manager instance.
*
* @method Phaser.Input.Keyboard.KeyboardManager#destroy
* @since 3.16.0
*/
destroy: function ()
{
this.stopListeners();
this.clearCaptures();
this.queue = [];
this.manager.game.events.off(GameEvents.POST_RENDER, this.postUpdate, this);
this.target = null;
this.enabled = false;
this.manager = null;
}
});
module.exports = KeyboardManager;