@makefully/platypus 4.1.20 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/platypus",
3
- "version": "4.1.20",
3
+ "version": "4.1.21",
4
4
  "description": "2D tile-based HTML5 game engine with a component-based entity model",
5
5
  "license": "MIT",
6
6
  "contributors": [
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
- preventDefault(event); // default 'none' may be too aggressive - if problems arise, we may need to limit this to certain key combos that get in the way of game-play. Example: (event.metaKey && event.keyCode == 37) causes an accidental cmd key press to send the browser back a page while playing and hitting the left arrow button.
187
- };
188
- this.callbackKeyUp = (event) => {
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();