From f7e259698dbbc05283c8b26f08246cb5328cc121 Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Fri, 14 Jun 2024 14:12:51 +0200 Subject: [PATCH] [web] KeyEvent resolved via code rather than keyCode (#1404) --- .../compose/ui/input/key/KeyEvent.web.kt | 142 ++++++++++++++++-- .../ui/input/KeyEventConversionTests.kt | 42 +++--- .../ui/window/CanvasBasedWindowTests.kt | 3 +- 3 files changed, 151 insertions(+), 36 deletions(-) diff --git a/compose/ui/ui/src/webCommonW3C/kotlin/androidx/compose/ui/input/key/KeyEvent.web.kt b/compose/ui/ui/src/webCommonW3C/kotlin/androidx/compose/ui/input/key/KeyEvent.web.kt index b677e732878c0..0a3a88793dd39 100644 --- a/compose/ui/ui/src/webCommonW3C/kotlin/androidx/compose/ui/input/key/KeyEvent.web.kt +++ b/compose/ui/ui/src/webCommonW3C/kotlin/androidx/compose/ui/input/key/KeyEvent.web.kt @@ -19,19 +19,6 @@ package androidx.compose.ui.input.key import androidx.compose.ui.input.pointer.PointerKeyboardModifiers import org.w3c.dom.events.KeyboardEvent -private fun Key(keyCode: Long, location: Int) = Key( - keyCode = if (location == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { - when (keyCode) { - Key.CtrlLeft.keyCode, - Key.ShiftLeft.keyCode, - Key.MetaLeft.keyCode -> keyCode or 0x80000000 - else -> keyCode - } - } else { - keyCode - } -) - private fun KeyboardEvent.toInputModifiers(): PointerKeyboardModifiers { return PointerKeyboardModifiers( isAltPressed = altKey, @@ -42,7 +29,7 @@ private fun KeyboardEvent.toInputModifiers(): PointerKeyboardModifiers { } internal fun KeyboardEvent.toComposeEvent(): KeyEvent { - val composeKey = Key(keyCode.toLong(), location) + val composeKey = toKey() return KeyEvent( nativeKeyEvent = InternalKeyEvent( key = composeKey, @@ -81,3 +68,130 @@ internal fun String.codePointAt(index: Int): CodePoint { } return high.code } + +private val codeMap = mapOf( + "KeyA" to Key.A, + "KeyB" to Key.B, + "KeyC" to Key.C, + "KeyD" to Key.D, + "KeyE" to Key.E, + "KeyF" to Key.F, + "KeyG" to Key.G, + "KeyH" to Key.H, + "KeyI" to Key.I, + "KeyJ" to Key.J, + "KeyK" to Key.K, + "KeyL" to Key.L, + "KeyM" to Key.M, + "KeyN" to Key.N, + "KeyO" to Key.O, + "KeyP" to Key.P, + "KeyQ" to Key.Q, + "KeyR" to Key.R, + "KeyS" to Key.S, + "KeyT" to Key.T, + "KeyU" to Key.U, + "KeyV" to Key.V, + "KeyW" to Key.W, + "KeyX" to Key.X, + "KeyY" to Key.Y, + "KeyZ" to Key.Z, + + "Digit0" to Key.Zero, + "Digit1" to Key.One, + "Digit2" to Key.Two, + "Digit3" to Key.Three, + "Digit4" to Key.Four, + "Digit5" to Key.Five, + "Digit6" to Key.Six, + "Digit7" to Key.Seven, + "Digit8" to Key.Eight, + "Digit9" to Key.Nine, + + "Numpad0" to Key.NumPad0, + "Numpad1" to Key.NumPad1, + "Numpad2" to Key.NumPad2, + "Numpad3" to Key.NumPad3, + "Numpad4" to Key.NumPad4, + "Numpad5" to Key.NumPad5, + "Numpad6" to Key.NumPad6, + "Numpad7" to Key.NumPad7, + "Numpad8" to Key.NumPad8, + "Numpad9" to Key.NumPad9, + + "NumpadDivide" to Key.NumPadDivide, + "NumpadMultiply" to Key.NumPadMultiply, + "NumpadSubtract" to Key.NumPadSubtract, + "NumpadAdd" to Key.NumPadAdd, + "NumpadEnter" to Key.NumPadEnter, + "NumpadEqual" to Key.NumPadEquals, + "NumpadDecimal" to Key.NumPadDot, + + "NumLock" to Key.NumLock, + + "Minus" to Key.Minus, + "Equal" to Key.Equals, + "Backspace" to Key.Backspace, + "BracketLeft" to Key.LeftBracket, + "BracketRight" to Key.RightBracket, + "Backslash" to Key.Backslash, + "Semicolon" to Key.Semicolon, + "Enter" to Key.Enter, + "Comma" to Key.Comma, + "Period" to Key.Period, + "Slash" to Key.Slash, + + "ArrowLeft" to Key.DirectionLeft, + "ArrowUp" to Key.DirectionUp, + "ArrowRight" to Key.DirectionRight, + "ArrowDown" to Key.DirectionDown, + + "Home" to Key.MoveHome, + "PageUp" to Key.PageUp, + "PageDown" to Key.PageDown, + "Delete" to Key.Delete, + "End" to Key.MoveEnd, + + "Backquote" to Key.Grave, + "Tab" to Key.Tab, + "CapsLock" to Key.CapsLock, + + "ShiftLeft" to Key.ShiftLeft, + "ControlLeft" to Key.CtrlLeft, + "AltLeft" to Key.AltLeft, + "MetaLeft" to Key.MetaLeft, + + "ShiftRight" to Key.ShiftRight, + "ControlRight" to Key.CtrlRight, + "AltRight" to Key.AltRight, + "MetaRight" to Key.MetaRight, + "Insert" to Key.Insert, + + "Escape" to Key.Escape, + + "F1" to Key.F1, + "F2" to Key.F2, + "F3" to Key.F3, + "F4" to Key.F4, + "F5" to Key.F5, + "F6" to Key.F6, + "F7" to Key.F7, + "F8" to Key.F8, + "F9" to Key.F9, + "F10" to Key.F10, + "F11" to Key.F11, + "F12" to Key.F12, + + "Space" to Key.Spacebar, +) + +private fun KeyboardEvent.toKey(): Key { + // code is empty string if the actual keyboard event actually is generated by virtual keyboard + val keyResolved = if (code.isEmpty()) { + codeMap[key] + } else { + codeMap[code] + } + + return keyResolved ?: Key.Unknown +} diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/input/KeyEventConversionTests.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/input/KeyEventConversionTests.kt index 5363488852ea6..354910e29f9c2 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/input/KeyEventConversionTests.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/input/KeyEventConversionTests.kt @@ -80,27 +80,16 @@ class KeyEventConversionTests { keyDownEvent("8", code = "Digit8").assertEquivalence(key = Key.Eight) keyDownEvent("9", code = "Digit9").assertEquivalence(key = Key.Nine) - keyDownEvent("0", code = "Numpad0").assertEquivalence(key = Key.Zero) - keyDownEvent("1", code = "Numpad1").assertEquivalence(key = Key.One) - keyDownEvent("2", code = "Numpad2").assertEquivalence(key = Key.Two) - keyDownEvent("3", code = "Numpad3").assertEquivalence(key = Key.Three) - keyDownEvent("4", code = "Numpad4").assertEquivalence(key = Key.Four) - keyDownEvent("5", code = "Numpad5").assertEquivalence(key = Key.Five) - keyDownEvent("6", code = "Numpad6").assertEquivalence(key = Key.Six) - keyDownEvent("7", code = "Numpad7").assertEquivalence(key = Key.Seven) - keyDownEvent("8", code = "Numpad8").assertEquivalence(key = Key.Eight) - keyDownEvent("9", code = "Numpad9").assertEquivalence(key = Key.Nine) - - keyDownEvent("0", code = "Numpad0").assertEquivalence(key = Key.Zero, codePoint = 48) - keyDownEvent("1", code = "Numpad1").assertEquivalence(key = Key.One, codePoint = 49) - keyDownEvent("2", code = "Numpad2").assertEquivalence(key = Key.Two, codePoint = 50) - keyDownEvent("3", code = "Numpad3").assertEquivalence(key = Key.Three, codePoint = 51) - keyDownEvent("4", code = "Numpad4").assertEquivalence(key = Key.Four, codePoint = 52) - keyDownEvent("5", code = "Numpad5").assertEquivalence(key = Key.Five, codePoint = 53) - keyDownEvent("6", code = "Numpad6").assertEquivalence(key = Key.Six, codePoint = 54) - keyDownEvent("7", code = "Numpad7").assertEquivalence(key = Key.Seven, codePoint = 55) - keyDownEvent("8", code = "Numpad8").assertEquivalence(key = Key.Eight, codePoint = 56) - keyDownEvent("9", code = "Numpad9").assertEquivalence(key = Key.Nine, codePoint = 57) + keyDownEvent("0", code = "Numpad0").assertEquivalence(key = Key.NumPad0, codePoint = 48) + keyDownEvent("1", code = "Numpad1").assertEquivalence(key = Key.NumPad1, codePoint = 49) + keyDownEvent("2", code = "Numpad2").assertEquivalence(key = Key.NumPad2, codePoint = 50) + keyDownEvent("3", code = "Numpad3").assertEquivalence(key = Key.NumPad3, codePoint = 51) + keyDownEvent("4", code = "Numpad4").assertEquivalence(key = Key.NumPad4, codePoint = 52) + keyDownEvent("5", code = "Numpad5").assertEquivalence(key = Key.NumPad5, codePoint = 53) + keyDownEvent("6", code = "Numpad6").assertEquivalence(key = Key.NumPad6, codePoint = 54) + keyDownEvent("7", code = "Numpad7").assertEquivalence(key = Key.NumPad7, codePoint = 55) + keyDownEvent("8", code = "Numpad8").assertEquivalence(key = Key.NumPad8, codePoint = 56) + keyDownEvent("9", code = "Numpad9").assertEquivalence(key = Key.NumPad9, codePoint = 57) keyDownEvent("=", code = "NumpadEqual", keyCode = Key.NumPadEquals.keyCode.toInt()).assertEquivalence(key = Key.NumPadEquals, codePoint = 61) keyDownEvent("/", code = "NumpadDivide", keyCode = Key.NumPadDivide.keyCode.toInt()).assertEquivalence(key = Key.NumPadDivide, codePoint = 47) @@ -164,6 +153,7 @@ class KeyEventConversionTests { keyDownEvent("F11", code = "F11", keyCode = Key.F11.keyCode.toInt()).assertEquivalence(key = Key.F11) keyDownEvent("F12", code = "F12", keyCode = Key.F12.keyCode.toInt()).assertEquivalence(key = Key.F12) + keyDownEvent("", code = "Space", keyCode = Key.Spacebar.keyCode.toInt()).assertEquivalence(key = Key.Spacebar) } @Test @@ -210,5 +200,15 @@ class KeyEventConversionTests { keyDownEvent("+", code = "Equal", keyCode = Key.Equals.keyCode.toInt()).assertEquivalence(key = Key.Equals, codePoint = 43) } + @Test + fun standardVirtualKeyboardLayout() { + // Virtual keyboard generates actual keyboard events for some of the keys pressed + // This keyboard events, however, actually differ - the code is always "" while key contains the value that we need + keyDownEvent("ArrowRight", code = "", keyCode = Key.DirectionRight.keyCode.toInt()).assertEquivalence(key = Key.DirectionRight) + keyDownEvent("ArrowLeft", code = "", keyCode = Key.DirectionLeft.keyCode.toInt()).assertEquivalence(key = Key.DirectionLeft) + keyDownEvent("Delete", code = "", keyCode = Key.Delete.keyCode.toInt()).assertEquivalence(key = Key.Delete) + keyDownEvent("Backspace", code = "", keyCode = Key.Backspace.keyCode.toInt()).assertEquivalence(key = Key.Backspace) + keyDownEvent("Enter", code = "", keyCode = Key.Enter.keyCode.toInt()).assertEquivalence(key = Key.Enter) + } } diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/window/CanvasBasedWindowTests.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/window/CanvasBasedWindowTests.kt index 37d0cbed99a4b..8b9719cf8deba 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/window/CanvasBasedWindowTests.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/window/CanvasBasedWindowTests.kt @@ -142,7 +142,8 @@ class CanvasBasedWindowTests { ) ('0'..'9').forEachIndexed { index, c -> - canvasElement.dispatchEvent(keyDownEvent(c.toString())) + val id = c.toString() + canvasElement.dispatchEvent(keyDownEvent(id, code = "Digit${id}" )) assertEquals(listOfNumbers[index], k) } }