@makefully/platypus 4.1.19 → 4.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/lib/platypus.js +1 -1
- package/package.json +1 -1
- package/src/Component.js +36 -9
- package/src/Game.js +3 -0
- package/src/components/HandlerController.js +55 -10
package/package.json
CHANGED
package/src/Component.js
CHANGED
|
@@ -64,9 +64,15 @@ export default class Component {
|
|
|
64
64
|
if (this.listener) {
|
|
65
65
|
const
|
|
66
66
|
publicMethods = this.publicMethods,
|
|
67
|
-
keys = Object.keys(publicMethods)
|
|
67
|
+
keys = Object.keys(publicMethods),
|
|
68
|
+
// Detach first so re-entrant removeEventListener during teardown
|
|
69
|
+
// cannot read a recycled listener's deleted `events` array.
|
|
70
|
+
listener = this.listener,
|
|
71
|
+
{events, messages} = listener;
|
|
68
72
|
let i = keys.length;
|
|
69
73
|
|
|
74
|
+
this.listener = null;
|
|
75
|
+
|
|
70
76
|
// Handle component's destroy method before removing messaging and methods.
|
|
71
77
|
if (this._destroy) {
|
|
72
78
|
this._destroy();
|
|
@@ -77,12 +83,21 @@ export default class Component {
|
|
|
77
83
|
this.removeMethod(keys[i]);
|
|
78
84
|
}
|
|
79
85
|
this.publicMethods.recycle();
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
|
|
87
|
+
if (events && messages) {
|
|
88
|
+
const
|
|
89
|
+
{owner} = this,
|
|
90
|
+
{length} = events;
|
|
91
|
+
|
|
92
|
+
for (let j = 0; j < length; j++) {
|
|
93
|
+
owner.off(events[j], messages[j]);
|
|
94
|
+
}
|
|
95
|
+
events.length = 0;
|
|
96
|
+
messages.length = 0;
|
|
97
|
+
arrayCache.recycle(events);
|
|
98
|
+
arrayCache.recycle(messages);
|
|
99
|
+
}
|
|
100
|
+
listener.recycle();
|
|
86
101
|
}
|
|
87
102
|
}
|
|
88
103
|
|
|
@@ -96,7 +111,13 @@ export default class Component {
|
|
|
96
111
|
removeEventListeners (listeners) {
|
|
97
112
|
if (!listeners) {
|
|
98
113
|
const
|
|
99
|
-
{listener, owner} = this
|
|
114
|
+
{listener, owner} = this;
|
|
115
|
+
|
|
116
|
+
if (!listener?.events || !listener.messages) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const
|
|
100
121
|
{events, messages} = listener,
|
|
101
122
|
{length} = events;
|
|
102
123
|
|
|
@@ -188,7 +209,13 @@ export default class Component {
|
|
|
188
209
|
*/
|
|
189
210
|
removeEventListener (event, callback) {
|
|
190
211
|
const
|
|
191
|
-
{listener, owner} = this
|
|
212
|
+
{listener, owner} = this;
|
|
213
|
+
|
|
214
|
+
if (!listener?.events || !listener.messages) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const
|
|
192
219
|
{events, messages} = listener;
|
|
193
220
|
|
|
194
221
|
for (let i = events.length - 1; i >= 0; i--) {
|
package/src/Game.js
CHANGED
|
@@ -224,6 +224,9 @@ class Game extends Messenger {
|
|
|
224
224
|
this.ticker.remove(this.tickInstance);
|
|
225
225
|
this.paused = true;
|
|
226
226
|
sound.pauseAll();
|
|
227
|
+
// Release held keys so pause does not leave sticky input,
|
|
228
|
+
// and so HandlerController can pass keyboard through to HTML.
|
|
229
|
+
this.triggerOnChildren('blur-control');
|
|
227
230
|
}
|
|
228
231
|
} else {
|
|
229
232
|
if (this.paused) {
|
|
@@ -4,6 +4,43 @@ import Gamepad from '../Gamepad.js';
|
|
|
4
4
|
import createComponentClass from '../factory.js';
|
|
5
5
|
|
|
6
6
|
const
|
|
7
|
+
/**
|
|
8
|
+
* Whether the event target is (or is inside) an editable field. Used so
|
|
9
|
+
* capture-phase game key handlers do not call preventDefault / emit game
|
|
10
|
+
* input while the user types in HTML overlays (forms, Notefully, etc.).
|
|
11
|
+
*
|
|
12
|
+
* @param {Event} event
|
|
13
|
+
* @return {Boolean}
|
|
14
|
+
*/
|
|
15
|
+
isEditableKeyboardTarget = function (event) {
|
|
16
|
+
const target = event?.target;
|
|
17
|
+
|
|
18
|
+
if (!target || typeof target.closest !== 'function') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (target.isContentEditable) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const tag = target.tagName;
|
|
27
|
+
|
|
28
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return !!target.closest('input, textarea, select, [contenteditable=""], [contenteditable="true"]');
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* When the game is paused or focus is in an editable field, let the browser
|
|
36
|
+
* handle keys (no game events, no preventDefault).
|
|
37
|
+
*
|
|
38
|
+
* @param {Event} event
|
|
39
|
+
* @return {Boolean}
|
|
40
|
+
*/
|
|
41
|
+
shouldPassThroughKeyboard = function (event) {
|
|
42
|
+
return !!(platypus.game?.paused || isEditableKeyboardTarget(event));
|
|
43
|
+
},
|
|
7
44
|
broadcastHandleController = function (tick) {
|
|
8
45
|
if (hasGamepads) {
|
|
9
46
|
const
|
|
@@ -170,25 +207,33 @@ export default createComponentClass(/** @lends platypus.components.HandlerContro
|
|
|
170
207
|
*/
|
|
171
208
|
initialize: function () {
|
|
172
209
|
const
|
|
173
|
-
{enableDefaultEvents} = this
|
|
210
|
+
{enableDefaultEvents} = this,
|
|
211
|
+
wrapKeyboard = (handler) => (event) => {
|
|
212
|
+
if (shouldPassThroughKeyboard(event)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
handler(event);
|
|
217
|
+
};
|
|
174
218
|
|
|
175
219
|
this.upsToBlur = [];
|
|
176
220
|
|
|
177
221
|
if (platypus.game.settings.debug || enableDefaultEvents === 'all') { // If this is a test build, leave in the browser key combinations so debug tools can be opened as expected.
|
|
178
|
-
this.callbackKeyDown = onDown.bind(this, 'keyboard');
|
|
179
|
-
this.callbackKeyUp = onUp.bind(this, 'keyboard');
|
|
222
|
+
this.callbackKeyDown = wrapKeyboard(onDown.bind(this, 'keyboard'));
|
|
223
|
+
this.callbackKeyUp = wrapKeyboard(onUp.bind(this, 'keyboard'));
|
|
180
224
|
} else { // Otherwise remove default browser behavior for key inputs so that they do not interfere with game-play.
|
|
181
225
|
const
|
|
182
226
|
preventDefault = Array.isArray(enableDefaultEvents) ? (event) => enableDefaultEvents.indexOf(event.code) === -1 ? event.preventDefault() : null : (event) => event.preventDefault();
|
|
183
227
|
|
|
184
|
-
this.callbackKeyDown = (event) => {
|
|
228
|
+
this.callbackKeyDown = wrapKeyboard((event) => {
|
|
185
229
|
onDown.call(this, 'keyboard', event);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
230
|
+
// Still aggressive for gameplay keys, but not while paused or typing in HTML.
|
|
231
|
+
preventDefault(event);
|
|
232
|
+
});
|
|
233
|
+
this.callbackKeyUp = wrapKeyboard((event) => {
|
|
189
234
|
onUp.call(this, 'keyboard', event);
|
|
190
235
|
preventDefault(event);
|
|
191
|
-
};
|
|
236
|
+
});
|
|
192
237
|
}
|
|
193
238
|
|
|
194
239
|
window.addEventListener('keydown', this.callbackKeyDown, true);
|
|
@@ -256,8 +301,8 @@ export default createComponentClass(/** @lends platypus.components.HandlerContro
|
|
|
256
301
|
},
|
|
257
302
|
methods: {
|
|
258
303
|
destroy: function () {
|
|
259
|
-
window.removeEventListener('keydown', this.callbackKeyDown);
|
|
260
|
-
window.removeEventListener('keyup', this.callbackKeyUp);
|
|
304
|
+
window.removeEventListener('keydown', this.callbackKeyDown, true);
|
|
305
|
+
window.removeEventListener('keyup', this.callbackKeyUp, true);
|
|
261
306
|
for (let i = 0; i < this.gamepads.length; i++) {
|
|
262
307
|
if (this.gamepads[i]) {
|
|
263
308
|
this.gamepads[i].recycle();
|