From 112a34ccabbf01999cd3c1ef7f3264f3b52b2f2a Mon Sep 17 00:00:00 2001 From: Paolo Brasolin <paolo.brasolin@eurac.edu> Date: Wed, 23 Mar 2022 16:09:02 +0100 Subject: [PATCH] feat: #fe working mobile keyboard (also fixes diacritics and ligatures) --- frontend/src/js/fight_scene.ts | 90 +++++---------------- frontend/src/js/main.ts | 24 ------ frontend/src/js/typewriter.ts | 143 +++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 95 deletions(-) create mode 100644 frontend/src/js/typewriter.ts diff --git a/frontend/src/js/fight_scene.ts b/frontend/src/js/fight_scene.ts index d390b75..58c0019 100644 --- a/frontend/src/js/fight_scene.ts +++ b/frontend/src/js/fight_scene.ts @@ -8,6 +8,7 @@ import levenshtein from "damerau-levenshtein"; import * as Types from "../../../backend/src/types"; import Foe from "./foe"; +import Typewriter from "./typewriter"; interface InputStatus { began_at: string | null; @@ -23,6 +24,7 @@ export default class FightScene extends Phaser.Scene { cluesGroup: Phaser.Physics.Arcade.Group; beGame: Types.Game; inputStatus: InputStatus; + typewriter: Typewriter; constructor() { super("fight"); @@ -274,43 +276,22 @@ export default class FightScene extends Phaser.Scene { font: "bold 64px Courier", color: "#ffffff", }); - this.input.keyboard.on( - Phaser.Input.Keyboard.Events.ANY_KEY_DOWN, - (event: any) => { - if (this.inputStatus.final === "") { - this.inputStatus.began_at = new Date().toISOString(); - } - if (LETTERS_KEYCODES.has(event.keyCode)) { - this.inputStatus.typed += event.key; - this.inputStatus.final += event.key; - textEntry.text = this.inputStatus.final; - } else if ( - event.keyCode === Phaser.Input.Keyboard.KeyCodes.BACKSPACE && - this.inputStatus.final.length > 0 - ) { - this.inputStatus.typed += "\b"; - this.inputStatus.final = this.inputStatus.final.substr( - 0, - this.inputStatus.final.length - 1, - ); - textEntry.text = this.inputStatus.final; - } else if ( - event.keyCode === Phaser.Input.Keyboard.KeyCodes.ENTER && - this.inputStatus.final.length > 0 - ) { - this.inputStatus.typed += "\n"; - this.inputStatus.ended_at = new Date().toISOString(); - this.submitTranscription(this.inputStatus); - this.inputStatus = { - began_at: null, - ended_at: null, - typed: "", - final: "", - }; - textEntry.text = this.inputStatus.final; - } - }, - ); + this.typewriter = new Typewriter(); + this.typewriter.onSubmit = (inputStatus) => { + if (inputStatus.began_at === null) return; + if (inputStatus.ended_at === null) return; + if (inputStatus.final === "") return; + this.submitTranscription({ + began_at: inputStatus.began_at.toISOString(), + ended_at: inputStatus.ended_at.toISOString(), + typed: inputStatus.typed, + final: inputStatus.final, + }); + textEntry.text = ""; + }; + this.typewriter.onChange = (inputStatus) => { + textEntry.text = inputStatus.final; + }; } } @@ -331,46 +312,13 @@ function createAnim(scene: any, key: any, refKey: any, from: any, to: any) { }); } -const LETTERS_KEYCODES = new Set([ - Phaser.Input.Keyboard.KeyCodes.SPACE, - Phaser.Input.Keyboard.KeyCodes.A, - Phaser.Input.Keyboard.KeyCodes.B, - Phaser.Input.Keyboard.KeyCodes.C, - Phaser.Input.Keyboard.KeyCodes.D, - Phaser.Input.Keyboard.KeyCodes.E, - Phaser.Input.Keyboard.KeyCodes.F, - Phaser.Input.Keyboard.KeyCodes.G, - Phaser.Input.Keyboard.KeyCodes.H, - Phaser.Input.Keyboard.KeyCodes.I, - Phaser.Input.Keyboard.KeyCodes.J, - Phaser.Input.Keyboard.KeyCodes.K, - Phaser.Input.Keyboard.KeyCodes.L, - Phaser.Input.Keyboard.KeyCodes.M, - Phaser.Input.Keyboard.KeyCodes.N, - Phaser.Input.Keyboard.KeyCodes.O, - Phaser.Input.Keyboard.KeyCodes.P, - Phaser.Input.Keyboard.KeyCodes.Q, - Phaser.Input.Keyboard.KeyCodes.R, - Phaser.Input.Keyboard.KeyCodes.S, - Phaser.Input.Keyboard.KeyCodes.T, - Phaser.Input.Keyboard.KeyCodes.U, - Phaser.Input.Keyboard.KeyCodes.V, - Phaser.Input.Keyboard.KeyCodes.W, - Phaser.Input.Keyboard.KeyCodes.X, - Phaser.Input.Keyboard.KeyCodes.Y, - Phaser.Input.Keyboard.KeyCodes.Z, - 219, // ß - 186, // ü - 192, // ö - 222, // ä -]); - function gameStart(scene: any) { spawn(scene); } async function spawn(scene: any) { await spawnFoe(scene); + return; scene.time.now; const delay = (8 * 1000 * (60 * 1000 - scene.time.now)) / 60 / 1000 + 2 * 1000; diff --git a/frontend/src/js/main.ts b/frontend/src/js/main.ts index 05cfa2b..3e0695f 100644 --- a/frontend/src/js/main.ts +++ b/frontend/src/js/main.ts @@ -22,27 +22,3 @@ const config = { }; new Phaser.Game(config); - -import Keyboard from "simple-keyboard"; - -document.addEventListener("DOMContentLoaded", () => { - new Keyboard({ - theme: "hg-theme-default hg-theme-oetzi", - physicalKeyboardHighlight: true, - debug: true, - layout: { - default: [ - "q w e r t z u i o p \u00FC {bksp}", - "a s d f g h j k l \u00F6 \u00E4", - "{space} y x c v b n m \u00DF {enter}", - ], - }, - display: { - "{bksp}": "⟵", // "⌫⟵", - "{enter}": "↵", // "⏎↩↵⏎", - "{space}": "␣", // "␣", - }, - // onChange: console.log, - // onKeyPress: console.log, - }); -}); diff --git a/frontend/src/js/typewriter.ts b/frontend/src/js/typewriter.ts new file mode 100644 index 0000000..60f8957 --- /dev/null +++ b/frontend/src/js/typewriter.ts @@ -0,0 +1,143 @@ +import Keyboard from "simple-keyboard"; +import { KeyboardOptions } from "simple-keyboard/build/interfaces"; + +interface InputStatus { + began_at: Date | null; + ended_at: Date | null; + typed: string; + final: string; +} + +enum Key { + Space = "{space}", + Enter = "{enter}", + Backspace = "{backspace}", + A = "a", + B = "b", + C = "c", + D = "d", + E = "e", + F = "f", + G = "g", + H = "h", + I = "i", + J = "j", + K = "k", + L = "l", + M = "m", + N = "n", + O = "o", + P = "p", + Q = "q", + R = "r", + S = "s", + T = "t", + U = "u", + V = "v", + W = "w", + X = "x", + Y = "y", + Z = "z", + Ä = "ä", + Ö = "ö", + Ü = "ü", + ß = "ß", +} + +class Typewriter { + inputStatus: InputStatus; + keyboard: Keyboard; + + onChange: (inputStatus: InputStatus) => unknown; + onSubmit: (inputStatus: InputStatus) => unknown; + + constructor() { + this.onChange = () => {}; + this.onSubmit = () => {}; + + this.inputStatus = { + began_at: null, + ended_at: null, + typed: "", + final: "", + }; + + this.keyboard = new Keyboard({ + // debug: true, + physicalKeyboardHighlight: true, + physicalKeyboardHighlightPress: true, + // autoUseTouchEvents: true, + newLineOnEnter: true, + disableCaretPositioning: true, + layout: { + default: [ + `q w e r t z u i o p ü ${Key.Backspace}`, + `a s d f g h j k l ö ä`, + `${Key.Space} y x c v b n m ß ${Key.Enter}`, + ], + }, + display: { + [Key.Backspace]: "⟵", // "⌫⟵", + [Key.Enter]: "↵", // "⏎↩↵⏎", + [Key.Space]: "␣", // "␣", + }, + onChange: this.keyboardOnChangeHandler.bind(this), + } as KeyboardOptions); + } + + extractKeyfromEvent( + event: KeyboardEvent | PointerEvent | MouseEvent | TouchEvent, + ): Key { + if (event instanceof KeyboardEvent) { + return ( + { + ["Backspace"]: Key.Backspace, + ["Enter"]: Key.Enter, + [" "]: Key.Space, + }[event.key] ?? (event.key as Key) + ); + } else { + const element = event.target as HTMLDivElement; + return element.dataset.skbtn as Key; + } + } + + keyboardOnChangeHandler( + input: string, + event: KeyboardEvent | PointerEvent | MouseEvent | TouchEvent, + ) { + const key = this.extractKeyfromEvent(event); + if (!this.inputStatus.began_at) this.inputStatus.began_at = new Date(); + if (key === Key.Enter) { + this.keyboard.clearInput(); + this.inputStatus.typed += "\n"; + this.inputStatus.ended_at = new Date(); + this.onSubmit(this.inputStatus); + this.resetInputStatus(); + } else if (key === Key.Backspace) { + // NOTE: Backspace events are skipped by simple-keyboard if its internal input is empty! + this.inputStatus.typed += "\b"; + this.inputStatus.final = input; + this.onChange(this.inputStatus); + } else if (key === Key.Space) { + this.inputStatus.typed += " "; + this.inputStatus.final = input; + this.onChange(this.inputStatus); + } else { + this.inputStatus.typed += key; + this.inputStatus.final = input; + this.onChange(this.inputStatus); + } + } + + resetInputStatus() { + this.inputStatus = { + began_at: null, + ended_at: null, + typed: "", + final: "", + }; + } +} + +export default Typewriter; -- GitLab