from __future__ import annotations
from typing import ClassVar, Final
class KeyLocation:
"""KeyboardEvent.location constants from the modern UI Events model."""
STANDARD: ClassVar[int] = 0
LEFT: ClassVar[int] = 1
RIGHT: ClassVar[int] = 2
NUMPAD: ClassVar[int] = 3
class Key:
"""Common KeyboardEvent.key values."""
ENTER: ClassVar[str] = "Enter"
TAB: ClassVar[str] = "Tab"
SPACE: ClassVar[str] = " "
ESCAPE: ClassVar[str] = "Escape"
BACKSPACE: ClassVar[str] = "Backspace"
DELETE: ClassVar[str] = "Delete"
INSERT: ClassVar[str] = "Insert"
HOME: ClassVar[str] = "Home"
END: ClassVar[str] = "End"
PAGE_UP: ClassVar[str] = "PageUp"
PAGE_DOWN: ClassVar[str] = "PageDown"
ARROW_LEFT: ClassVar[str] = "ArrowLeft"
ARROW_RIGHT: ClassVar[str] = "ArrowRight"
ARROW_UP: ClassVar[str] = "ArrowUp"
ARROW_DOWN: ClassVar[str] = "ArrowDown"
SHIFT: ClassVar[str] = "Shift"
CONTROL: ClassVar[str] = "Control"
ALT: ClassVar[str] = "Alt"
META: ClassVar[str] = "Meta"
CAPS_LOCK: ClassVar[str] = "CapsLock"
NUM_LOCK: ClassVar[str] = "NumLock"
SCROLL_LOCK: ClassVar[str] = "ScrollLock"
CONTEXT_MENU: ClassVar[str] = "ContextMenu"
F1: ClassVar[str] = "F1"
F2: ClassVar[str] = "F2"
F3: ClassVar[str] = "F3"
F4: ClassVar[str] = "F4"
F5: ClassVar[str] = "F5"
F6: ClassVar[str] = "F6"
F7: ClassVar[str] = "F7"
F8: ClassVar[str] = "F8"
F9: ClassVar[str] = "F9"
F10: ClassVar[str] = "F10"
F11: ClassVar[str] = "F11"
F12: ClassVar[str] = "F12"
A: ClassVar[str] = "a"
B: ClassVar[str] = "b"
C: ClassVar[str] = "c"
D: ClassVar[str] = "d"
E: ClassVar[str] = "e"
F: ClassVar[str] = "f"
G: ClassVar[str] = "g"
H: ClassVar[str] = "h"
I: ClassVar[str] = "i"
J: ClassVar[str] = "j"
K: ClassVar[str] = "k"
L: ClassVar[str] = "l"
M: ClassVar[str] = "m"
N: ClassVar[str] = "n"
O: ClassVar[str] = "o"
P: ClassVar[str] = "p"
Q: ClassVar[str] = "q"
R: ClassVar[str] = "r"
S: ClassVar[str] = "s"
T: ClassVar[str] = "t"
U: ClassVar[str] = "u"
V: ClassVar[str] = "v"
W: ClassVar[str] = "w"
X: ClassVar[str] = "x"
Y: ClassVar[str] = "y"
Z: ClassVar[str] = "z"
DIGIT_0: ClassVar[str] = "0"
DIGIT_1: ClassVar[str] = "1"
DIGIT_2: ClassVar[str] = "2"
DIGIT_3: ClassVar[str] = "3"
DIGIT_4: ClassVar[str] = "4"
DIGIT_5: ClassVar[str] = "5"
DIGIT_6: ClassVar[str] = "6"
DIGIT_7: ClassVar[str] = "7"
DIGIT_8: ClassVar[str] = "8"
DIGIT_9: ClassVar[str] = "9"
class Code:
"""Common KeyboardEvent.code values."""
ENTER: ClassVar[str] = "Enter"
TAB: ClassVar[str] = "Tab"
SPACE: ClassVar[str] = "Space"
ESCAPE: ClassVar[str] = "Escape"
BACKSPACE: ClassVar[str] = "Backspace"
DELETE: ClassVar[str] = "Delete"
INSERT: ClassVar[str] = "Insert"
HOME: ClassVar[str] = "Home"
END: ClassVar[str] = "End"
PAGE_UP: ClassVar[str] = "PageUp"
PAGE_DOWN: ClassVar[str] = "PageDown"
ARROW_LEFT: ClassVar[str] = "ArrowLeft"
ARROW_RIGHT: ClassVar[str] = "ArrowRight"
ARROW_UP: ClassVar[str] = "ArrowUp"
ARROW_DOWN: ClassVar[str] = "ArrowDown"
SHIFT_LEFT: ClassVar[str] = "ShiftLeft"
SHIFT_RIGHT: ClassVar[str] = "ShiftRight"
CONTROL_LEFT: ClassVar[str] = "ControlLeft"
CONTROL_RIGHT: ClassVar[str] = "ControlRight"
ALT_LEFT: ClassVar[str] = "AltLeft"
ALT_RIGHT: ClassVar[str] = "AltRight"
META_LEFT: ClassVar[str] = "MetaLeft"
META_RIGHT: ClassVar[str] = "MetaRight"
CAPS_LOCK: ClassVar[str] = "CapsLock"
NUM_LOCK: ClassVar[str] = "NumLock"
SCROLL_LOCK: ClassVar[str] = "ScrollLock"
CONTEXT_MENU: ClassVar[str] = "ContextMenu"
F1: ClassVar[str] = "F1"
F2: ClassVar[str] = "F2"
F3: ClassVar[str] = "F3"
F4: ClassVar[str] = "F4"
F5: ClassVar[str] = "F5"
F6: ClassVar[str] = "F6"
F7: ClassVar[str] = "F7"
F8: ClassVar[str] = "F8"
F9: ClassVar[str] = "F9"
F10: ClassVar[str] = "F10"
F11: ClassVar[str] = "F11"
F12: ClassVar[str] = "F12"
KEY_A: ClassVar[str] = "KeyA"
KEY_B: ClassVar[str] = "KeyB"
KEY_C: ClassVar[str] = "KeyC"
KEY_D: ClassVar[str] = "KeyD"
KEY_E: ClassVar[str] = "KeyE"
KEY_F: ClassVar[str] = "KeyF"
KEY_G: ClassVar[str] = "KeyG"
KEY_H: ClassVar[str] = "KeyH"
KEY_I: ClassVar[str] = "KeyI"
KEY_J: ClassVar[str] = "KeyJ"
KEY_K: ClassVar[str] = "KeyK"
KEY_L: ClassVar[str] = "KeyL"
KEY_M: ClassVar[str] = "KeyM"
KEY_N: ClassVar[str] = "KeyN"
KEY_O: ClassVar[str] = "KeyO"
KEY_P: ClassVar[str] = "KeyP"
KEY_Q: ClassVar[str] = "KeyQ"
KEY_R: ClassVar[str] = "KeyR"
KEY_S: ClassVar[str] = "KeyS"
KEY_T: ClassVar[str] = "KeyT"
KEY_U: ClassVar[str] = "KeyU"
KEY_V: ClassVar[str] = "KeyV"
KEY_W: ClassVar[str] = "KeyW"
KEY_X: ClassVar[str] = "KeyX"
KEY_Y: ClassVar[str] = "KeyY"
KEY_Z: ClassVar[str] = "KeyZ"
DIGIT_0: ClassVar[str] = "Digit0"
DIGIT_1: ClassVar[str] = "Digit1"
DIGIT_2: ClassVar[str] = "Digit2"
DIGIT_3: ClassVar[str] = "Digit3"
DIGIT_4: ClassVar[str] = "Digit4"
DIGIT_5: ClassVar[str] = "Digit5"
DIGIT_6: ClassVar[str] = "Digit6"
DIGIT_7: ClassVar[str] = "Digit7"
DIGIT_8: ClassVar[str] = "Digit8"
DIGIT_9: ClassVar[str] = "Digit9"
NUMPAD_0: ClassVar[str] = "Numpad0"
NUMPAD_1: ClassVar[str] = "Numpad1"
NUMPAD_2: ClassVar[str] = "Numpad2"
NUMPAD_3: ClassVar[str] = "Numpad3"
NUMPAD_4: ClassVar[str] = "Numpad4"
NUMPAD_5: ClassVar[str] = "Numpad5"
NUMPAD_6: ClassVar[str] = "Numpad6"
NUMPAD_7: ClassVar[str] = "Numpad7"
NUMPAD_8: ClassVar[str] = "Numpad8"
NUMPAD_9: ClassVar[str] = "Numpad9"
NUMPAD_ADD: ClassVar[str] = "NumpadAdd"
NUMPAD_DECIMAL: ClassVar[str] = "NumpadDecimal"
NUMPAD_DIVIDE: ClassVar[str] = "NumpadDivide"
NUMPAD_ENTER: ClassVar[str] = "NumpadEnter"
NUMPAD_MULTIPLY: ClassVar[str] = "NumpadMultiply"
NUMPAD_SUBTRACT: ClassVar[str] = "NumpadSubtract"
_LEGACY_KEYCODE_TO_KEY: Final[dict[str, str]] = {
"8": Key.BACKSPACE,
"9": Key.TAB,
"13": Key.ENTER,
"16": Key.SHIFT,
"17": Key.CONTROL,
"18": Key.ALT,
"20": Key.CAPS_LOCK,
"27": Key.ESCAPE,
"32": Key.SPACE,
"33": Key.PAGE_UP,
"34": Key.PAGE_DOWN,
"35": Key.END,
"36": Key.HOME,
"37": Key.ARROW_LEFT,
"38": Key.ARROW_UP,
"39": Key.ARROW_RIGHT,
"40": Key.ARROW_DOWN,
"45": Key.INSERT,
"46": Key.DELETE,
"91": Key.META,
"93": Key.CONTEXT_MENU,
"144": Key.NUM_LOCK,
"145": Key.SCROLL_LOCK,
}
_LEGACY_KEYCODE_TO_KEY.update({str(48 + i): str(i) for i in range(10)})
_LEGACY_KEYCODE_TO_KEY.update({str(65 + i): chr(97 + i) for i in range(26)})
_LEGACY_KEYCODE_TO_KEY.update({str(111 + i): f"F{i}" for i in range(1, 13)})
_KEY_TO_CODE: Final[dict[str, str]] = {
Key.ENTER: Code.ENTER,
Key.TAB: Code.TAB,
Key.SPACE: Code.SPACE,
Key.ESCAPE: Code.ESCAPE,
Key.BACKSPACE: Code.BACKSPACE,
Key.DELETE: Code.DELETE,
Key.INSERT: Code.INSERT,
Key.HOME: Code.HOME,
Key.END: Code.END,
Key.PAGE_UP: Code.PAGE_UP,
Key.PAGE_DOWN: Code.PAGE_DOWN,
Key.ARROW_LEFT: Code.ARROW_LEFT,
Key.ARROW_RIGHT: Code.ARROW_RIGHT,
Key.ARROW_UP: Code.ARROW_UP,
Key.ARROW_DOWN: Code.ARROW_DOWN,
Key.SHIFT: Code.SHIFT_LEFT,
Key.CONTROL: Code.CONTROL_LEFT,
Key.ALT: Code.ALT_LEFT,
Key.META: Code.META_LEFT,
Key.CAPS_LOCK: Code.CAPS_LOCK,
Key.NUM_LOCK: Code.NUM_LOCK,
Key.SCROLL_LOCK: Code.SCROLL_LOCK,
Key.CONTEXT_MENU: Code.CONTEXT_MENU,
}
_KEY_TO_CODE.update({chr(97 + i): f"Key{chr(65 + i)}" for i in range(26)})
_KEY_TO_CODE.update({str(i): f"Digit{i}" for i in range(10)})
_KEY_TO_CODE.update({f"F{i}": f"F{i}" for i in range(1, 13)})
_KEY_TO_LEGACY_KEYCODE: Final[dict[str, int]] = {
key: int(code) for code, key in _LEGACY_KEYCODE_TO_KEY.items()
}
[docs]
class KeyCode:
"""Legacy KeyboardEvent.keyCode constants kept for compatibility."""
A: ClassVar[str] = "65"
B: ClassVar[str] = "66"
C: ClassVar[str] = "67"
D: ClassVar[str] = "68"
E: ClassVar[str] = "69"
F: ClassVar[str] = "70"
G: ClassVar[str] = "71"
H: ClassVar[str] = "72"
I: ClassVar[str] = "73"
J: ClassVar[str] = "74"
K: ClassVar[str] = "75"
L: ClassVar[str] = "76"
M: ClassVar[str] = "77"
N: ClassVar[str] = "78"
O: ClassVar[str] = "79"
P: ClassVar[str] = "80"
Q: ClassVar[str] = "81"
R: ClassVar[str] = "82"
S: ClassVar[str] = "83"
T: ClassVar[str] = "84"
U: ClassVar[str] = "85"
V: ClassVar[str] = "86"
W: ClassVar[str] = "87"
X: ClassVar[str] = "88"
Y: ClassVar[str] = "89"
Z: ClassVar[str] = "90"
NUMBER_0: ClassVar[str] = "48"
NUMBER_1: ClassVar[str] = "49"
NUMBER_2: ClassVar[str] = "50"
NUMBER_3: ClassVar[str] = "51"
NUMBER_4: ClassVar[str] = "52"
NUMBER_5: ClassVar[str] = "53"
NUMBER_6: ClassVar[str] = "54"
NUMBER_7: ClassVar[str] = "55"
NUMBER_8: ClassVar[str] = "56"
NUMBER_9: ClassVar[str] = "57"
NUMPAD_0: ClassVar[str] = "96"
NUMPAD_1: ClassVar[str] = "97"
NUMPAD_2: ClassVar[str] = "98"
NUMPAD_3: ClassVar[str] = "99"
NUMPAD_4: ClassVar[str] = "100"
NUMPAD_5: ClassVar[str] = "101"
NUMPAD_6: ClassVar[str] = "102"
NUMPAD_7: ClassVar[str] = "103"
NUMPAD_8: ClassVar[str] = "104"
NUMPAD_9: ClassVar[str] = "105"
NUMPAD_MULTIPLY: ClassVar[str] = "106"
NUMPAD_ADD: ClassVar[str] = "107"
NUMPAD_ENTER: ClassVar[str] = "108"
NUMPAD_SUBTRACT: ClassVar[str] = "109"
NUMPAD_DECIMAL: ClassVar[str] = "110"
NUMPAD_DIVIDE: ClassVar[str] = "111"
F1: ClassVar[str] = "112"
F2: ClassVar[str] = "113"
F3: ClassVar[str] = "114"
F4: ClassVar[str] = "115"
F5: ClassVar[str] = "116"
F6: ClassVar[str] = "117"
F7: ClassVar[str] = "118"
F8: ClassVar[str] = "119"
F9: ClassVar[str] = "120"
F10: ClassVar[str] = "121"
F11: ClassVar[str] = "122"
F12: ClassVar[str] = "123"
BACKSPACE: ClassVar[str] = "8"
TAB: ClassVar[str] = "9"
ENTER: ClassVar[str] = "13"
SHIFT: ClassVar[str] = "16"
CONTROL: ClassVar[str] = "17"
ALT: ClassVar[str] = "18"
CAPS_LOCK: ClassVar[str] = "20"
ESCAPE: ClassVar[str] = "27"
SPACE: ClassVar[str] = "32"
PAGE_UP: ClassVar[str] = "33"
PAGE_DOWN: ClassVar[str] = "34"
END: ClassVar[str] = "35"
HOME: ClassVar[str] = "36"
LEFT: ClassVar[str] = "37"
UP: ClassVar[str] = "38"
RIGHT: ClassVar[str] = "39"
DOWN: ClassVar[str] = "40"
INSERT: ClassVar[str] = "45"
DELETE: ClassVar[str] = "46"
LEFT_COMMAND: ClassVar[str] = "91"
RIGHT_COMMAND: ClassVar[str] = "93"
WINDOWS: ClassVar[str] = "91"
MENU: ClassVar[str] = "93"
NUM_LOCK: ClassVar[str] = "144"
SCROLL_LOCK: ClassVar[str] = "145"
MODIFIER_KEYS: ClassVar[frozenset[str]] = frozenset(
{SHIFT, CONTROL, ALT, LEFT_COMMAND, RIGHT_COMMAND, CAPS_LOCK, NUM_LOCK, SCROLL_LOCK}
)
@classmethod
def is_modifier(cls, key_code: str) -> bool:
return key_code in cls.MODIFIER_KEYS
@classmethod
def to_key(cls, key_code: str | int) -> str | None:
return _LEGACY_KEYCODE_TO_KEY.get(str(key_code))
@classmethod
def to_code(cls, key_code: str | int) -> str | None:
key = cls.to_key(key_code)
return _KEY_TO_CODE.get(key) if key is not None else None
@classmethod
def from_key(cls, key: str) -> int | None:
return _KEY_TO_LEGACY_KEYCODE.get(normalize_key(key))
def normalize_key(key: str | None) -> str:
"""Normalize browser-ish key strings to the modern KeyboardEvent.key form."""
if key in (None, ""):
return ""
aliases = {
"Esc": Key.ESCAPE,
"Spacebar": Key.SPACE,
"Left": Key.ARROW_LEFT,
"Right": Key.ARROW_RIGHT,
"Up": Key.ARROW_UP,
"Down": Key.ARROW_DOWN,
"Del": Key.DELETE,
"OS": Key.META,
}
normalized = aliases.get(key, key)
if len(normalized) == 1 and normalized.isalpha():
return normalized.lower()
return normalized
def normalize_code(code: str | None) -> str:
"""Normalize shorthand physical key names to KeyboardEvent.code values."""
if code in (None, ""):
return ""
if len(code) == 1 and code.isalpha():
return f"Key{code.upper()}"
if len(code) == 1 and code.isdigit():
return f"Digit{code}"
return code
__all__ = ["Code", "Key", "KeyCode", "KeyLocation", "normalize_code", "normalize_key"]