diff --git a/Backends/HTML5/kha/SystemImpl.hx b/Backends/HTML5/kha/SystemImpl.hx index f9960ea42..3202f6449 100644 --- a/Backends/HTML5/kha/SystemImpl.hx +++ b/Backends/HTML5/kha/SystemImpl.hx @@ -7,6 +7,7 @@ import js.html.ClipboardEvent; import js.html.DeviceMotionEvent; import js.html.DeviceOrientationEvent; import js.html.DragEvent; +import js.html.FocusEvent; import js.html.KeyboardEvent; import js.html.MouseEvent; import js.html.PointerEvent; @@ -20,7 +21,9 @@ import kha.graphics4.TextureFormat; import kha.input.Gamepad; import kha.input.KeyCode; import kha.input.Keyboard; +import kha.input.KeyboardImpl; import kha.input.Mouse; +import kha.input.MouseImpl; import kha.input.Sensor; import kha.input.Surface; import kha.js.AudioElementAudio; @@ -215,7 +218,8 @@ class SystemImpl { } public static function vibrate(ms: Int): Void { - if (Browser.navigator.vibrate == null) return; + if (Browser.navigator.vibrate == null) + return; Browser.navigator.vibrate(ms); } @@ -247,9 +251,9 @@ class SystemImpl { static function init2(defaultWidth: Int, defaultHeight: Int, ?backbufferFormat: TextureFormat) { #if !kha_no_keyboard - keyboard = new Keyboard(); + keyboard = new KeyboardImpl(); #end - mouse = new kha.input.MouseImpl(); + mouse = new MouseImpl(); surface = new Surface(); gamepads = new Array(); gamepadStates = new Array(); @@ -494,7 +498,6 @@ class SystemImpl { if (keyboard != null) { canvas.onkeydown = keyDown; canvas.onkeyup = keyUp; - canvas.onkeypress = keyPress; } canvas.onblur = onBlur; canvas.onfocus = onFocus; @@ -669,7 +672,7 @@ class SystemImpl { static var iosSoundEnabled: Bool = false; static function unlockiOSSound(): Void { - if (!ios || iosSoundEnabled) + if (!ios || iosSoundEnabled || MobileWebAudio._context == null) return; var buffer = MobileWebAudio._context.createBuffer(1, 1, 22050); @@ -929,6 +932,8 @@ class SystemImpl { var id = touch.identifier; if (ios) { id = iosTouchs.indexOf(id); + if (id == -1) + continue; iosTouchs[id] = -1; } @@ -974,8 +979,12 @@ class SystemImpl { for (touch in event.changedTouches) { var id = touch.identifier; - if (ios) + if (ios) { id = iosTouchs.indexOf(id); + if (id == -1) + continue; + iosTouchs[id] = -1; + } setTouchXY(touch); if (!Surface.listenedEventsBefore) { @@ -983,147 +992,25 @@ class SystemImpl { } surface.sendTouchEndEvent(id, touchX, touchY); } - iosTouchs = []; insideInputEvent = false; } - static function onBlur() { + static function onBlur(event: FocusEvent) { + final input = KeyboardImpl.input; + if (input != null && event.relatedTarget == input) + return; // System.pause(); System.background(); } - static function onFocus() { + static function onFocus(event: FocusEvent) { + final input = KeyboardImpl.input; + if (input != null && event.relatedTarget == input) + return; // System.resume(); System.foreground(); } - static function keycodeToChar(key: String, keycode: Int, shift: Bool): String { - if (key != null) { - if (key.length == 1) - return key; - switch (key) { - case "Add": - return "+"; - case "Subtract": - return "-"; - case "Multiply": - return "*"; - case "Divide": - return "/"; - } - } - switch (keycode) { - case 187: - if (shift) - return "*"; - else - return "+"; - case 188: - if (shift) - return ";"; - else - return ","; - case 189: - if (shift) - return "_"; - else - return "-"; - case 190: - if (shift) - return ":"; - else - return "."; - case 191: - if (shift) - return "'"; - else - return "#"; - case 226: - if (shift) - return ">"; - else - return "<"; - case 106: - return "*"; - case 107: - return "+"; - case 109: - return "-"; - case 111: - return "/"; - case 49: - if (shift) - return "!"; - else - return "1"; - case 50: - if (shift) - return "\""; - else - return "2"; - case 51: - if (shift) - return "§"; - else - return "3"; - case 52: - if (shift) - return "$"; - else - return "4"; - case 53: - if (shift) - return "%"; - else - return "5"; - case 54: - if (shift) - return "&"; - else - return "6"; - case 55: - if (shift) - return "/"; - else - return "7"; - case 56: - if (shift) - return "("; - else - return "8"; - case 57: - if (shift) - return ")"; - else - return "9"; - case 48: - if (shift) - return "="; - else - return "0"; - case 219: - if (shift) - return "?"; - else - return "ß"; - case 212: - if (shift) - return "`"; - else - return "´"; - } - if (keycode >= 96 && keycode <= 105) { // num block - return String.fromCharCode("0".code - 96 + keycode); - } - if (keycode >= "A".code && keycode <= "Z".code) { - if (shift) - return String.fromCharCode(keycode); - else - return String.fromCharCode(keycode - "A".code + "a".code); - } - return String.fromCharCode(keycode); - } - static function keyDown(event: KeyboardEvent): Void { insideInputEvent = true; activeKeyEvent = event; @@ -1135,10 +1022,17 @@ class SystemImpl { // prevent key repeat if (event.repeat) { event.preventDefault(); + activeKeyEvent = null; + insideInputEvent = false; return; } - var keyCode = fixedKeyCode(event); + final keyCode = fixedKeyCode(event); keyboard.sendDownEvent(keyCode); + + if (event.key.length == 1) { + keyboard.sendPressEvent(event.key); + } + activeKeyEvent = null; insideInputEvent = false; } @@ -1168,24 +1062,26 @@ class SystemImpl { } static function defaultKeyBlock(e: KeyboardEvent): Void { + final keyCode: KeyCode = cast e.keyCode; // block if ctrl key pressed if (e.ctrlKey || e.metaKey) { // except for cut-copy-paste - if (e.keyCode == 67 || e.keyCode == 88 || e.keyCode == 86) { + if (keyCode == C || keyCode == X || keyCode == V) { return; } // and quit on macOS - if (e.metaKey && e.keyCode == 81) { + if (e.metaKey && keyCode == Q) { return; } e.preventDefault(); return; } + // allow F-keys - if (e.keyCode >= 112 && e.keyCode <= 123) + if (e.keyCode >= F1.toInt() && e.keyCode <= F12.toInt()) return; // allow char keys - if (e.key == null || e.key.length == 1) + if (e.key.length == 1) return; e.preventDefault(); } @@ -1205,21 +1101,6 @@ class SystemImpl { insideInputEvent = false; } - static function keyPress(event: KeyboardEvent): Void { - insideInputEvent = true; - activeKeyEvent = event; - unlockSound(); - - if (event.which == 0) - return; // for Firefox and Safari - preventDefaultKeyBehavior(event); - event.stopPropagation(); - keyboard.sendPressEvent(String.fromCharCode(event.which)); - - activeKeyEvent = null; - insideInputEvent = false; - } - public static function canSwitchFullscreen(): Bool { return Syntax.code("'fullscreenElement ' in document || 'mozFullScreenElement' in document || diff --git a/Backends/HTML5/kha/input/KeyboardImpl.hx b/Backends/HTML5/kha/input/KeyboardImpl.hx new file mode 100644 index 000000000..9edfb1e14 --- /dev/null +++ b/Backends/HTML5/kha/input/KeyboardImpl.hx @@ -0,0 +1,92 @@ +package kha.input; + +import js.Browser.document; +import js.html.InputElement; +import js.html.InputEvent; +import js.html.KeyboardEvent; +import kha.SystemImpl; +import kha.input.Keyboard; + +using StringTools; + +class KeyboardImpl extends kha.input.Keyboard { + static var virtualKeyboardId = "kha-virtual-keyboard"; + public static var input: InputElement; + + public function new() { + super(); + } + + override function show(): Void { + input = cast document.getElementById(virtualKeyboardId); + input ??= createKeyboardInputElement(); + document.body.appendChild(input); + input.focus(); + } + + static function createKeyboardInputElement(): InputElement { + final input = document.createInputElement(); + input.id = virtualKeyboardId; + input.style.position = "fixed"; + input.style.left = "0px"; + input.style.top = "0px"; + input.style.opacity = "0.0"; + input.style.pointerEvents = "none"; + input.type = "text"; + input.autocomplete = "off"; + input.spellcheck = false; + input.setAttribute("autocorrect", "off"); + input.setAttribute("autocapitalize", "off"); + + input.onkeydown = (e: KeyboardEvent) -> { + if (e.repeat) { + e.preventDefault(); + return; + } + // virtual keyboards do not report pressed chars in most cases + // so we only redirect non-printable keys to canvas there + if (e.key.length == 1) + return; + final keyEvent = copyKeyboardEventFrom(e); + SystemImpl.khanvas.dispatchEvent(keyEvent); + } + input.onkeyup = (e: KeyboardEvent) -> { + final keyEvent = copyKeyboardEventFrom(e); + SystemImpl.khanvas.dispatchEvent(keyEvent); + } + input.oninput = (e: InputEvent) -> { + // IME-composition, key is not final yet + if (e.isComposing) + return; + final str: String = (e : Dynamic).data ?? return; + final kb = Keyboard.get() ?? return; + for (code in str) { + final char = String.fromCharCode(code); + kb.sendPressEvent(char); + } + input.value = ""; + } + return input; + } + + static function copyKeyboardEventFrom(e: KeyboardEvent): KeyboardEvent { + return new KeyboardEvent(e.type, { + key: e.key, + code: e.code, + keyCode: e.keyCode, + location: e.location, + ctrlKey: e.ctrlKey, + shiftKey: e.shiftKey, + altKey: e.altKey, + metaKey: e.metaKey, + bubbles: true, + cancelable: true + }); + } + + override function hide(): Void { + SystemImpl.khanvas.focus(); + if (input != null) + document.body.removeChild(input); + } +} diff --git a/Sources/kha/input/KeyCode.hx b/Sources/kha/input/KeyCode.hx index df3a4af76..427ef9c41 100644 --- a/Sources/kha/input/KeyCode.hx +++ b/Sources/kha/input/KeyCode.hx @@ -188,4 +188,8 @@ enum abstract KeyCode(Int) to Int { var Zoom = 251; var PA1 = 253; var WinOemClear = 254; + + public inline function toInt(): Int { + return this; + } }