From e045ef29691ea9f2ef3eafe3176f8d1da8a3a5fa Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Tue, 1 Oct 2019 14:10:53 -0700 Subject: [PATCH] Keymap for Web (#41397) --- .../gen_keycodes/data/keyboard_maps.tmpl | 17 + dev/tools/gen_keycodes/lib/code_gen.dart | 57 +- packages/flutter/lib/services.dart | 1 + .../lib/src/services/keyboard_maps.dart | 489 ++++++++++++++++++ .../lib/src/services/raw_keyboard.dart | 8 + .../lib/src/services/raw_keyboard_web.dart | 198 +++++++ .../test/services/raw_keyboard_test.dart | 132 ++++- 7 files changed, 885 insertions(+), 17 deletions(-) create mode 100644 packages/flutter/lib/src/services/raw_keyboard_web.dart diff --git a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl index 3dd9ac2afb3..45d90a897e5 100644 --- a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl +++ b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl @@ -70,3 +70,20 @@ const Map kLinuxToPhysicalKey = kWebToLogicalKey = { +@@@WEB_LOGICAL_KEY_MAP@@@ +}; + +/// Maps Web KeyboardEvent codes to the matching [PhysicalKeyboardKey]. +const Map kWebToPhysicalKey = { +@@@WEB_PHYSICAL_KEY_MAP@@@ +}; + +/// A map of Web KeyboardEvent codes which have printable representations, but appear +/// on the number pad. Used to provide different key objects for keys like +/// KEY_EQUALS and NUMPAD_EQUALS. +const Map kWebNumPadMap = { +@@@WEB_NUMPAD_MAP@@@ +}; + diff --git a/dev/tools/gen_keycodes/lib/code_gen.dart b/dev/tools/gen_keycodes/lib/code_gen.dart index 8a1107467f2..1afe2f78099 100644 --- a/dev/tools/gen_keycodes/lib/code_gen.dart +++ b/dev/tools/gen_keycodes/lib/code_gen.dart @@ -108,6 +108,12 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK return synonyms.toString(); } + List get numpadKeyData { + return keyData.data.where((Key entry) { + return entry.constantName.startsWith('numpad') && entry.keyLabel != null; + }).toList(); + } + /// This generates the map of USB HID codes to physical keys. String get predefinedHidCodeMap { final StringBuffer scanCodeMap = StringBuffer(); @@ -139,10 +145,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK /// This generates the map of GLFW number pad key codes to logical keys. String get glfwNumpadMap { final StringBuffer glfwNumpadMap = StringBuffer(); - final List onlyNumpads = keyData.data.where((Key entry) { - return entry.constantName.startsWith('numpad') && entry.keyLabel != null; - }).toList(); - for (Key entry in onlyNumpads) { + for (Key entry in numpadKeyData) { if (entry.glfwKeyCodes != null) { for (int code in entry.glfwKeyCodes.cast()) { glfwNumpadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},'); @@ -192,10 +195,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK /// This generates the map of Android number pad key codes to logical keys. String get androidNumpadMap { final StringBuffer androidKeyCodeMap = StringBuffer(); - final List onlyNumpads = keyData.data.where((Key entry) { - return entry.constantName.startsWith('numpad') && entry.keyLabel != null; - }).toList(); - for (Key entry in onlyNumpads) { + for (Key entry in numpadKeyData) { if (entry.androidKeyCodes != null) { for (int code in entry.androidKeyCodes.cast()) { androidKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},'); @@ -232,10 +232,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK /// This generates the map of macOS number pad key codes to logical keys. String get macOsNumpadMap { final StringBuffer macOsNumPadMap = StringBuffer(); - final List onlyNumpads = keyData.data.where((Key entry) { - return entry.constantName.startsWith('numpad') && entry.keyLabel != null; - }).toList(); - for (Key entry in onlyNumpads) { + for (Key entry in numpadKeyData) { if (entry.macOsScanCode != null) { macOsNumPadMap.writeln(' ${toHex(entry.macOsScanCode)}: LogicalKeyboardKey.${entry.constantName},'); } @@ -265,6 +262,39 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK return fuchsiaScanCodeMap.toString().trimRight(); } + /// This generates the map of Web KeyboardEvent codes to logical keys. + String get webLogicalKeyMap { + final StringBuffer result = StringBuffer(); + for (Key entry in keyData.data) { + if (entry.name != null) { + result.writeln(" '${entry.name}': LogicalKeyboardKey.${entry.constantName},"); + } + } + return result.toString().trimRight(); + } + + /// This generates the map of Web KeyboardEvent codes to physical keys. + String get webPhysicalKeyMap { + final StringBuffer result = StringBuffer(); + for (Key entry in keyData.data) { + if (entry.name != null) { + result.writeln(" '${entry.name}': PhysicalKeyboardKey.${entry.constantName},"); + } + } + return result.toString().trimRight(); + } + + /// This generates the map of Web number pad codes to logical keys. + String get webNumpadMap { + final StringBuffer result = StringBuffer(); + for (Key entry in numpadKeyData) { + if (entry.name != null) { + result.writeln(" '${entry.name}': LogicalKeyboardKey.${entry.constantName},"); + } + } + return result.toString().trimRight(); + } + /// Substitutes the various maps and definitions into the template file for /// keyboard_key.dart. String generateKeyboardKeys() { @@ -297,6 +327,9 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK 'GLFW_KEY_CODE_MAP': glfwKeyCodeMap, 'GLFW_NUMPAD_MAP': glfwNumpadMap, 'XKB_SCAN_CODE_MAP': xkbScanCodeMap, + 'WEB_LOGICAL_KEY_MAP': webLogicalKeyMap, + 'WEB_PHYSICAL_KEY_MAP': webPhysicalKeyMap, + 'WEB_NUMPAD_MAP': webNumpadMap, }; final String template = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_maps.tmpl')).readAsStringSync(); diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index 9ba05e07175..3ada7e86e5e 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -28,6 +28,7 @@ export 'src/services/raw_keyboard_android.dart'; export 'src/services/raw_keyboard_fuchsia.dart'; export 'src/services/raw_keyboard_linux.dart'; export 'src/services/raw_keyboard_macos.dart'; +export 'src/services/raw_keyboard_web.dart'; export 'src/services/system_channels.dart'; export 'src/services/system_chrome.dart'; export 'src/services/system_navigator.dart'; diff --git a/packages/flutter/lib/src/services/keyboard_maps.dart b/packages/flutter/lib/src/services/keyboard_maps.dart index dd815542b53..f5030d959dd 100644 --- a/packages/flutter/lib/src/services/keyboard_maps.dart +++ b/packages/flutter/lib/src/services/keyboard_maps.dart @@ -1493,3 +1493,492 @@ const Map kLinuxToPhysicalKey = kWebToLogicalKey = { + 'None': LogicalKeyboardKey.none, + 'Hyper': LogicalKeyboardKey.hyper, + 'Super': LogicalKeyboardKey.superKey, + 'Fn': LogicalKeyboardKey.fn, + 'FnLock': LogicalKeyboardKey.fnLock, + 'Suspend': LogicalKeyboardKey.suspend, + 'Resume': LogicalKeyboardKey.resume, + 'Turbo': LogicalKeyboardKey.turbo, + 'Sleep': LogicalKeyboardKey.sleep, + 'WakeUp': LogicalKeyboardKey.wakeUp, + 'DisplayToggleIntExt': LogicalKeyboardKey.displayToggleIntExt, + 'KeyA': LogicalKeyboardKey.keyA, + 'KeyB': LogicalKeyboardKey.keyB, + 'KeyC': LogicalKeyboardKey.keyC, + 'KeyD': LogicalKeyboardKey.keyD, + 'KeyE': LogicalKeyboardKey.keyE, + 'KeyF': LogicalKeyboardKey.keyF, + 'KeyG': LogicalKeyboardKey.keyG, + 'KeyH': LogicalKeyboardKey.keyH, + 'KeyI': LogicalKeyboardKey.keyI, + 'KeyJ': LogicalKeyboardKey.keyJ, + 'KeyK': LogicalKeyboardKey.keyK, + 'KeyL': LogicalKeyboardKey.keyL, + 'KeyM': LogicalKeyboardKey.keyM, + 'KeyN': LogicalKeyboardKey.keyN, + 'KeyO': LogicalKeyboardKey.keyO, + 'KeyP': LogicalKeyboardKey.keyP, + 'KeyQ': LogicalKeyboardKey.keyQ, + 'KeyR': LogicalKeyboardKey.keyR, + 'KeyS': LogicalKeyboardKey.keyS, + 'KeyT': LogicalKeyboardKey.keyT, + 'KeyU': LogicalKeyboardKey.keyU, + 'KeyV': LogicalKeyboardKey.keyV, + 'KeyW': LogicalKeyboardKey.keyW, + 'KeyX': LogicalKeyboardKey.keyX, + 'KeyY': LogicalKeyboardKey.keyY, + 'KeyZ': LogicalKeyboardKey.keyZ, + 'Digit1': LogicalKeyboardKey.digit1, + 'Digit2': LogicalKeyboardKey.digit2, + 'Digit3': LogicalKeyboardKey.digit3, + 'Digit4': LogicalKeyboardKey.digit4, + 'Digit5': LogicalKeyboardKey.digit5, + 'Digit6': LogicalKeyboardKey.digit6, + 'Digit7': LogicalKeyboardKey.digit7, + 'Digit8': LogicalKeyboardKey.digit8, + 'Digit9': LogicalKeyboardKey.digit9, + 'Digit0': LogicalKeyboardKey.digit0, + 'Enter': LogicalKeyboardKey.enter, + 'Escape': LogicalKeyboardKey.escape, + 'Backspace': LogicalKeyboardKey.backspace, + 'Tab': LogicalKeyboardKey.tab, + 'Space': LogicalKeyboardKey.space, + 'Minus': LogicalKeyboardKey.minus, + 'Equal': LogicalKeyboardKey.equal, + 'BracketLeft': LogicalKeyboardKey.bracketLeft, + 'BracketRight': LogicalKeyboardKey.bracketRight, + 'Backslash': LogicalKeyboardKey.backslash, + 'Semicolon': LogicalKeyboardKey.semicolon, + 'Quote': LogicalKeyboardKey.quote, + 'Backquote': LogicalKeyboardKey.backquote, + 'Comma': LogicalKeyboardKey.comma, + 'Period': LogicalKeyboardKey.period, + 'Slash': LogicalKeyboardKey.slash, + 'CapsLock': LogicalKeyboardKey.capsLock, + 'F1': LogicalKeyboardKey.f1, + 'F2': LogicalKeyboardKey.f2, + 'F3': LogicalKeyboardKey.f3, + 'F4': LogicalKeyboardKey.f4, + 'F5': LogicalKeyboardKey.f5, + 'F6': LogicalKeyboardKey.f6, + 'F7': LogicalKeyboardKey.f7, + 'F8': LogicalKeyboardKey.f8, + 'F9': LogicalKeyboardKey.f9, + 'F10': LogicalKeyboardKey.f10, + 'F11': LogicalKeyboardKey.f11, + 'F12': LogicalKeyboardKey.f12, + 'PrintScreen': LogicalKeyboardKey.printScreen, + 'ScrollLock': LogicalKeyboardKey.scrollLock, + 'Pause': LogicalKeyboardKey.pause, + 'Insert': LogicalKeyboardKey.insert, + 'Home': LogicalKeyboardKey.home, + 'PageUp': LogicalKeyboardKey.pageUp, + 'Delete': LogicalKeyboardKey.delete, + 'End': LogicalKeyboardKey.end, + 'PageDown': LogicalKeyboardKey.pageDown, + 'ArrowRight': LogicalKeyboardKey.arrowRight, + 'ArrowLeft': LogicalKeyboardKey.arrowLeft, + 'ArrowDown': LogicalKeyboardKey.arrowDown, + 'ArrowUp': LogicalKeyboardKey.arrowUp, + 'NumLock': LogicalKeyboardKey.numLock, + 'NumpadDivide': LogicalKeyboardKey.numpadDivide, + 'NumpadMultiply': LogicalKeyboardKey.numpadMultiply, + 'NumpadSubtract': LogicalKeyboardKey.numpadSubtract, + 'NumpadAdd': LogicalKeyboardKey.numpadAdd, + 'NumpadEnter': LogicalKeyboardKey.numpadEnter, + 'Numpad1': LogicalKeyboardKey.numpad1, + 'Numpad2': LogicalKeyboardKey.numpad2, + 'Numpad3': LogicalKeyboardKey.numpad3, + 'Numpad4': LogicalKeyboardKey.numpad4, + 'Numpad5': LogicalKeyboardKey.numpad5, + 'Numpad6': LogicalKeyboardKey.numpad6, + 'Numpad7': LogicalKeyboardKey.numpad7, + 'Numpad8': LogicalKeyboardKey.numpad8, + 'Numpad9': LogicalKeyboardKey.numpad9, + 'Numpad0': LogicalKeyboardKey.numpad0, + 'NumpadDecimal': LogicalKeyboardKey.numpadDecimal, + 'IntlBackslash': LogicalKeyboardKey.intlBackslash, + 'ContextMenu': LogicalKeyboardKey.contextMenu, + 'Power': LogicalKeyboardKey.power, + 'NumpadEqual': LogicalKeyboardKey.numpadEqual, + 'F13': LogicalKeyboardKey.f13, + 'F14': LogicalKeyboardKey.f14, + 'F15': LogicalKeyboardKey.f15, + 'F16': LogicalKeyboardKey.f16, + 'F17': LogicalKeyboardKey.f17, + 'F18': LogicalKeyboardKey.f18, + 'F19': LogicalKeyboardKey.f19, + 'F20': LogicalKeyboardKey.f20, + 'F21': LogicalKeyboardKey.f21, + 'F22': LogicalKeyboardKey.f22, + 'F23': LogicalKeyboardKey.f23, + 'F24': LogicalKeyboardKey.f24, + 'Open': LogicalKeyboardKey.open, + 'Help': LogicalKeyboardKey.help, + 'Select': LogicalKeyboardKey.select, + 'Again': LogicalKeyboardKey.again, + 'Undo': LogicalKeyboardKey.undo, + 'Cut': LogicalKeyboardKey.cut, + 'Copy': LogicalKeyboardKey.copy, + 'Paste': LogicalKeyboardKey.paste, + 'Find': LogicalKeyboardKey.find, + 'AudioVolumeMute': LogicalKeyboardKey.audioVolumeMute, + 'AudioVolumeUp': LogicalKeyboardKey.audioVolumeUp, + 'AudioVolumeDown': LogicalKeyboardKey.audioVolumeDown, + 'NumpadComma': LogicalKeyboardKey.numpadComma, + 'IntlRo': LogicalKeyboardKey.intlRo, + 'KanaMode': LogicalKeyboardKey.kanaMode, + 'IntlYen': LogicalKeyboardKey.intlYen, + 'Convert': LogicalKeyboardKey.convert, + 'NonConvert': LogicalKeyboardKey.nonConvert, + 'Lang1': LogicalKeyboardKey.lang1, + 'Lang2': LogicalKeyboardKey.lang2, + 'Lang3': LogicalKeyboardKey.lang3, + 'Lang4': LogicalKeyboardKey.lang4, + 'Lang5': LogicalKeyboardKey.lang5, + 'Abort': LogicalKeyboardKey.abort, + 'Props': LogicalKeyboardKey.props, + 'NumpadParenLeft': LogicalKeyboardKey.numpadParenLeft, + 'NumpadParenRight': LogicalKeyboardKey.numpadParenRight, + 'NumpadBackspace': LogicalKeyboardKey.numpadBackspace, + 'NumpadMemoryStore': LogicalKeyboardKey.numpadMemoryStore, + 'NumpadMemoryRecall': LogicalKeyboardKey.numpadMemoryRecall, + 'NumpadMemoryClear': LogicalKeyboardKey.numpadMemoryClear, + 'NumpadMemoryAdd': LogicalKeyboardKey.numpadMemoryAdd, + 'NumpadMemorySubtract': LogicalKeyboardKey.numpadMemorySubtract, + 'NumpadClear': LogicalKeyboardKey.numpadClear, + 'NumpadClearEntry': LogicalKeyboardKey.numpadClearEntry, + 'ControlLeft': LogicalKeyboardKey.controlLeft, + 'ShiftLeft': LogicalKeyboardKey.shiftLeft, + 'AltLeft': LogicalKeyboardKey.altLeft, + 'MetaLeft': LogicalKeyboardKey.metaLeft, + 'ControlRight': LogicalKeyboardKey.controlRight, + 'ShiftRight': LogicalKeyboardKey.shiftRight, + 'AltRight': LogicalKeyboardKey.altRight, + 'MetaRight': LogicalKeyboardKey.metaRight, + 'BrightnessUp': LogicalKeyboardKey.brightnessUp, + 'BrightnessDown': LogicalKeyboardKey.brightnessDown, + 'MediaPlay': LogicalKeyboardKey.mediaPlay, + 'MediaRecord': LogicalKeyboardKey.mediaRecord, + 'MediaFastForward': LogicalKeyboardKey.mediaFastForward, + 'MediaRewind': LogicalKeyboardKey.mediaRewind, + 'MediaTrackNext': LogicalKeyboardKey.mediaTrackNext, + 'MediaTrackPrevious': LogicalKeyboardKey.mediaTrackPrevious, + 'MediaStop': LogicalKeyboardKey.mediaStop, + 'Eject': LogicalKeyboardKey.eject, + 'MediaPlayPause': LogicalKeyboardKey.mediaPlayPause, + 'MediaSelect': LogicalKeyboardKey.mediaSelect, + 'LaunchMail': LogicalKeyboardKey.launchMail, + 'LaunchApp2': LogicalKeyboardKey.launchApp2, + 'LaunchApp1': LogicalKeyboardKey.launchApp1, + 'LaunchControlPanel': LogicalKeyboardKey.launchControlPanel, + 'SelectTask': LogicalKeyboardKey.selectTask, + 'LaunchScreenSaver': LogicalKeyboardKey.launchScreenSaver, + 'LaunchAssistant': LogicalKeyboardKey.launchAssistant, + 'BrowserSearch': LogicalKeyboardKey.browserSearch, + 'BrowserHome': LogicalKeyboardKey.browserHome, + 'BrowserBack': LogicalKeyboardKey.browserBack, + 'BrowserForward': LogicalKeyboardKey.browserForward, + 'BrowserStop': LogicalKeyboardKey.browserStop, + 'BrowserRefresh': LogicalKeyboardKey.browserRefresh, + 'BrowserFavorites': LogicalKeyboardKey.browserFavorites, + 'ZoomToggle': LogicalKeyboardKey.zoomToggle, + 'MailReply': LogicalKeyboardKey.mailReply, + 'MailForward': LogicalKeyboardKey.mailForward, + 'MailSend': LogicalKeyboardKey.mailSend, + 'KeyboardLayoutSelect': LogicalKeyboardKey.keyboardLayoutSelect, + 'ShowAllWindows': LogicalKeyboardKey.showAllWindows, + 'GameButton1': LogicalKeyboardKey.gameButton1, + 'GameButton2': LogicalKeyboardKey.gameButton2, + 'GameButton3': LogicalKeyboardKey.gameButton3, + 'GameButton4': LogicalKeyboardKey.gameButton4, + 'GameButton5': LogicalKeyboardKey.gameButton5, + 'GameButton6': LogicalKeyboardKey.gameButton6, + 'GameButton7': LogicalKeyboardKey.gameButton7, + 'GameButton8': LogicalKeyboardKey.gameButton8, + 'GameButton9': LogicalKeyboardKey.gameButton9, + 'GameButton10': LogicalKeyboardKey.gameButton10, + 'GameButton11': LogicalKeyboardKey.gameButton11, + 'GameButton12': LogicalKeyboardKey.gameButton12, + 'GameButton13': LogicalKeyboardKey.gameButton13, + 'GameButton14': LogicalKeyboardKey.gameButton14, + 'GameButton15': LogicalKeyboardKey.gameButton15, + 'GameButton16': LogicalKeyboardKey.gameButton16, + 'GameButtonA': LogicalKeyboardKey.gameButtonA, + 'GameButtonB': LogicalKeyboardKey.gameButtonB, + 'GameButtonC': LogicalKeyboardKey.gameButtonC, + 'GameButtonLeft1': LogicalKeyboardKey.gameButtonLeft1, + 'GameButtonLeft2': LogicalKeyboardKey.gameButtonLeft2, + 'GameButtonMode': LogicalKeyboardKey.gameButtonMode, + 'GameButtonRight1': LogicalKeyboardKey.gameButtonRight1, + 'GameButtonRight2': LogicalKeyboardKey.gameButtonRight2, + 'GameButtonSelect': LogicalKeyboardKey.gameButtonSelect, + 'GameButtonStart': LogicalKeyboardKey.gameButtonStart, + 'GameButtonThumbLeft': LogicalKeyboardKey.gameButtonThumbLeft, + 'GameButtonThumbRight': LogicalKeyboardKey.gameButtonThumbRight, + 'GameButtonX': LogicalKeyboardKey.gameButtonX, + 'GameButtonY': LogicalKeyboardKey.gameButtonY, + 'GameButtonZ': LogicalKeyboardKey.gameButtonZ, +}; + +/// Maps Web KeyboardEvent codes to the matching [PhysicalKeyboardKey]. +const Map kWebToPhysicalKey = { + 'None': PhysicalKeyboardKey.none, + 'Hyper': PhysicalKeyboardKey.hyper, + 'Super': PhysicalKeyboardKey.superKey, + 'Fn': PhysicalKeyboardKey.fn, + 'FnLock': PhysicalKeyboardKey.fnLock, + 'Suspend': PhysicalKeyboardKey.suspend, + 'Resume': PhysicalKeyboardKey.resume, + 'Turbo': PhysicalKeyboardKey.turbo, + 'Sleep': PhysicalKeyboardKey.sleep, + 'WakeUp': PhysicalKeyboardKey.wakeUp, + 'DisplayToggleIntExt': PhysicalKeyboardKey.displayToggleIntExt, + 'KeyA': PhysicalKeyboardKey.keyA, + 'KeyB': PhysicalKeyboardKey.keyB, + 'KeyC': PhysicalKeyboardKey.keyC, + 'KeyD': PhysicalKeyboardKey.keyD, + 'KeyE': PhysicalKeyboardKey.keyE, + 'KeyF': PhysicalKeyboardKey.keyF, + 'KeyG': PhysicalKeyboardKey.keyG, + 'KeyH': PhysicalKeyboardKey.keyH, + 'KeyI': PhysicalKeyboardKey.keyI, + 'KeyJ': PhysicalKeyboardKey.keyJ, + 'KeyK': PhysicalKeyboardKey.keyK, + 'KeyL': PhysicalKeyboardKey.keyL, + 'KeyM': PhysicalKeyboardKey.keyM, + 'KeyN': PhysicalKeyboardKey.keyN, + 'KeyO': PhysicalKeyboardKey.keyO, + 'KeyP': PhysicalKeyboardKey.keyP, + 'KeyQ': PhysicalKeyboardKey.keyQ, + 'KeyR': PhysicalKeyboardKey.keyR, + 'KeyS': PhysicalKeyboardKey.keyS, + 'KeyT': PhysicalKeyboardKey.keyT, + 'KeyU': PhysicalKeyboardKey.keyU, + 'KeyV': PhysicalKeyboardKey.keyV, + 'KeyW': PhysicalKeyboardKey.keyW, + 'KeyX': PhysicalKeyboardKey.keyX, + 'KeyY': PhysicalKeyboardKey.keyY, + 'KeyZ': PhysicalKeyboardKey.keyZ, + 'Digit1': PhysicalKeyboardKey.digit1, + 'Digit2': PhysicalKeyboardKey.digit2, + 'Digit3': PhysicalKeyboardKey.digit3, + 'Digit4': PhysicalKeyboardKey.digit4, + 'Digit5': PhysicalKeyboardKey.digit5, + 'Digit6': PhysicalKeyboardKey.digit6, + 'Digit7': PhysicalKeyboardKey.digit7, + 'Digit8': PhysicalKeyboardKey.digit8, + 'Digit9': PhysicalKeyboardKey.digit9, + 'Digit0': PhysicalKeyboardKey.digit0, + 'Enter': PhysicalKeyboardKey.enter, + 'Escape': PhysicalKeyboardKey.escape, + 'Backspace': PhysicalKeyboardKey.backspace, + 'Tab': PhysicalKeyboardKey.tab, + 'Space': PhysicalKeyboardKey.space, + 'Minus': PhysicalKeyboardKey.minus, + 'Equal': PhysicalKeyboardKey.equal, + 'BracketLeft': PhysicalKeyboardKey.bracketLeft, + 'BracketRight': PhysicalKeyboardKey.bracketRight, + 'Backslash': PhysicalKeyboardKey.backslash, + 'Semicolon': PhysicalKeyboardKey.semicolon, + 'Quote': PhysicalKeyboardKey.quote, + 'Backquote': PhysicalKeyboardKey.backquote, + 'Comma': PhysicalKeyboardKey.comma, + 'Period': PhysicalKeyboardKey.period, + 'Slash': PhysicalKeyboardKey.slash, + 'CapsLock': PhysicalKeyboardKey.capsLock, + 'F1': PhysicalKeyboardKey.f1, + 'F2': PhysicalKeyboardKey.f2, + 'F3': PhysicalKeyboardKey.f3, + 'F4': PhysicalKeyboardKey.f4, + 'F5': PhysicalKeyboardKey.f5, + 'F6': PhysicalKeyboardKey.f6, + 'F7': PhysicalKeyboardKey.f7, + 'F8': PhysicalKeyboardKey.f8, + 'F9': PhysicalKeyboardKey.f9, + 'F10': PhysicalKeyboardKey.f10, + 'F11': PhysicalKeyboardKey.f11, + 'F12': PhysicalKeyboardKey.f12, + 'PrintScreen': PhysicalKeyboardKey.printScreen, + 'ScrollLock': PhysicalKeyboardKey.scrollLock, + 'Pause': PhysicalKeyboardKey.pause, + 'Insert': PhysicalKeyboardKey.insert, + 'Home': PhysicalKeyboardKey.home, + 'PageUp': PhysicalKeyboardKey.pageUp, + 'Delete': PhysicalKeyboardKey.delete, + 'End': PhysicalKeyboardKey.end, + 'PageDown': PhysicalKeyboardKey.pageDown, + 'ArrowRight': PhysicalKeyboardKey.arrowRight, + 'ArrowLeft': PhysicalKeyboardKey.arrowLeft, + 'ArrowDown': PhysicalKeyboardKey.arrowDown, + 'ArrowUp': PhysicalKeyboardKey.arrowUp, + 'NumLock': PhysicalKeyboardKey.numLock, + 'NumpadDivide': PhysicalKeyboardKey.numpadDivide, + 'NumpadMultiply': PhysicalKeyboardKey.numpadMultiply, + 'NumpadSubtract': PhysicalKeyboardKey.numpadSubtract, + 'NumpadAdd': PhysicalKeyboardKey.numpadAdd, + 'NumpadEnter': PhysicalKeyboardKey.numpadEnter, + 'Numpad1': PhysicalKeyboardKey.numpad1, + 'Numpad2': PhysicalKeyboardKey.numpad2, + 'Numpad3': PhysicalKeyboardKey.numpad3, + 'Numpad4': PhysicalKeyboardKey.numpad4, + 'Numpad5': PhysicalKeyboardKey.numpad5, + 'Numpad6': PhysicalKeyboardKey.numpad6, + 'Numpad7': PhysicalKeyboardKey.numpad7, + 'Numpad8': PhysicalKeyboardKey.numpad8, + 'Numpad9': PhysicalKeyboardKey.numpad9, + 'Numpad0': PhysicalKeyboardKey.numpad0, + 'NumpadDecimal': PhysicalKeyboardKey.numpadDecimal, + 'IntlBackslash': PhysicalKeyboardKey.intlBackslash, + 'ContextMenu': PhysicalKeyboardKey.contextMenu, + 'Power': PhysicalKeyboardKey.power, + 'NumpadEqual': PhysicalKeyboardKey.numpadEqual, + 'F13': PhysicalKeyboardKey.f13, + 'F14': PhysicalKeyboardKey.f14, + 'F15': PhysicalKeyboardKey.f15, + 'F16': PhysicalKeyboardKey.f16, + 'F17': PhysicalKeyboardKey.f17, + 'F18': PhysicalKeyboardKey.f18, + 'F19': PhysicalKeyboardKey.f19, + 'F20': PhysicalKeyboardKey.f20, + 'F21': PhysicalKeyboardKey.f21, + 'F22': PhysicalKeyboardKey.f22, + 'F23': PhysicalKeyboardKey.f23, + 'F24': PhysicalKeyboardKey.f24, + 'Open': PhysicalKeyboardKey.open, + 'Help': PhysicalKeyboardKey.help, + 'Select': PhysicalKeyboardKey.select, + 'Again': PhysicalKeyboardKey.again, + 'Undo': PhysicalKeyboardKey.undo, + 'Cut': PhysicalKeyboardKey.cut, + 'Copy': PhysicalKeyboardKey.copy, + 'Paste': PhysicalKeyboardKey.paste, + 'Find': PhysicalKeyboardKey.find, + 'AudioVolumeMute': PhysicalKeyboardKey.audioVolumeMute, + 'AudioVolumeUp': PhysicalKeyboardKey.audioVolumeUp, + 'AudioVolumeDown': PhysicalKeyboardKey.audioVolumeDown, + 'NumpadComma': PhysicalKeyboardKey.numpadComma, + 'IntlRo': PhysicalKeyboardKey.intlRo, + 'KanaMode': PhysicalKeyboardKey.kanaMode, + 'IntlYen': PhysicalKeyboardKey.intlYen, + 'Convert': PhysicalKeyboardKey.convert, + 'NonConvert': PhysicalKeyboardKey.nonConvert, + 'Lang1': PhysicalKeyboardKey.lang1, + 'Lang2': PhysicalKeyboardKey.lang2, + 'Lang3': PhysicalKeyboardKey.lang3, + 'Lang4': PhysicalKeyboardKey.lang4, + 'Lang5': PhysicalKeyboardKey.lang5, + 'Abort': PhysicalKeyboardKey.abort, + 'Props': PhysicalKeyboardKey.props, + 'NumpadParenLeft': PhysicalKeyboardKey.numpadParenLeft, + 'NumpadParenRight': PhysicalKeyboardKey.numpadParenRight, + 'NumpadBackspace': PhysicalKeyboardKey.numpadBackspace, + 'NumpadMemoryStore': PhysicalKeyboardKey.numpadMemoryStore, + 'NumpadMemoryRecall': PhysicalKeyboardKey.numpadMemoryRecall, + 'NumpadMemoryClear': PhysicalKeyboardKey.numpadMemoryClear, + 'NumpadMemoryAdd': PhysicalKeyboardKey.numpadMemoryAdd, + 'NumpadMemorySubtract': PhysicalKeyboardKey.numpadMemorySubtract, + 'NumpadClear': PhysicalKeyboardKey.numpadClear, + 'NumpadClearEntry': PhysicalKeyboardKey.numpadClearEntry, + 'ControlLeft': PhysicalKeyboardKey.controlLeft, + 'ShiftLeft': PhysicalKeyboardKey.shiftLeft, + 'AltLeft': PhysicalKeyboardKey.altLeft, + 'MetaLeft': PhysicalKeyboardKey.metaLeft, + 'ControlRight': PhysicalKeyboardKey.controlRight, + 'ShiftRight': PhysicalKeyboardKey.shiftRight, + 'AltRight': PhysicalKeyboardKey.altRight, + 'MetaRight': PhysicalKeyboardKey.metaRight, + 'BrightnessUp': PhysicalKeyboardKey.brightnessUp, + 'BrightnessDown': PhysicalKeyboardKey.brightnessDown, + 'MediaPlay': PhysicalKeyboardKey.mediaPlay, + 'MediaRecord': PhysicalKeyboardKey.mediaRecord, + 'MediaFastForward': PhysicalKeyboardKey.mediaFastForward, + 'MediaRewind': PhysicalKeyboardKey.mediaRewind, + 'MediaTrackNext': PhysicalKeyboardKey.mediaTrackNext, + 'MediaTrackPrevious': PhysicalKeyboardKey.mediaTrackPrevious, + 'MediaStop': PhysicalKeyboardKey.mediaStop, + 'Eject': PhysicalKeyboardKey.eject, + 'MediaPlayPause': PhysicalKeyboardKey.mediaPlayPause, + 'MediaSelect': PhysicalKeyboardKey.mediaSelect, + 'LaunchMail': PhysicalKeyboardKey.launchMail, + 'LaunchApp2': PhysicalKeyboardKey.launchApp2, + 'LaunchApp1': PhysicalKeyboardKey.launchApp1, + 'LaunchControlPanel': PhysicalKeyboardKey.launchControlPanel, + 'SelectTask': PhysicalKeyboardKey.selectTask, + 'LaunchScreenSaver': PhysicalKeyboardKey.launchScreenSaver, + 'LaunchAssistant': PhysicalKeyboardKey.launchAssistant, + 'BrowserSearch': PhysicalKeyboardKey.browserSearch, + 'BrowserHome': PhysicalKeyboardKey.browserHome, + 'BrowserBack': PhysicalKeyboardKey.browserBack, + 'BrowserForward': PhysicalKeyboardKey.browserForward, + 'BrowserStop': PhysicalKeyboardKey.browserStop, + 'BrowserRefresh': PhysicalKeyboardKey.browserRefresh, + 'BrowserFavorites': PhysicalKeyboardKey.browserFavorites, + 'ZoomToggle': PhysicalKeyboardKey.zoomToggle, + 'MailReply': PhysicalKeyboardKey.mailReply, + 'MailForward': PhysicalKeyboardKey.mailForward, + 'MailSend': PhysicalKeyboardKey.mailSend, + 'KeyboardLayoutSelect': PhysicalKeyboardKey.keyboardLayoutSelect, + 'ShowAllWindows': PhysicalKeyboardKey.showAllWindows, + 'GameButton1': PhysicalKeyboardKey.gameButton1, + 'GameButton2': PhysicalKeyboardKey.gameButton2, + 'GameButton3': PhysicalKeyboardKey.gameButton3, + 'GameButton4': PhysicalKeyboardKey.gameButton4, + 'GameButton5': PhysicalKeyboardKey.gameButton5, + 'GameButton6': PhysicalKeyboardKey.gameButton6, + 'GameButton7': PhysicalKeyboardKey.gameButton7, + 'GameButton8': PhysicalKeyboardKey.gameButton8, + 'GameButton9': PhysicalKeyboardKey.gameButton9, + 'GameButton10': PhysicalKeyboardKey.gameButton10, + 'GameButton11': PhysicalKeyboardKey.gameButton11, + 'GameButton12': PhysicalKeyboardKey.gameButton12, + 'GameButton13': PhysicalKeyboardKey.gameButton13, + 'GameButton14': PhysicalKeyboardKey.gameButton14, + 'GameButton15': PhysicalKeyboardKey.gameButton15, + 'GameButton16': PhysicalKeyboardKey.gameButton16, + 'GameButtonA': PhysicalKeyboardKey.gameButtonA, + 'GameButtonB': PhysicalKeyboardKey.gameButtonB, + 'GameButtonC': PhysicalKeyboardKey.gameButtonC, + 'GameButtonLeft1': PhysicalKeyboardKey.gameButtonLeft1, + 'GameButtonLeft2': PhysicalKeyboardKey.gameButtonLeft2, + 'GameButtonMode': PhysicalKeyboardKey.gameButtonMode, + 'GameButtonRight1': PhysicalKeyboardKey.gameButtonRight1, + 'GameButtonRight2': PhysicalKeyboardKey.gameButtonRight2, + 'GameButtonSelect': PhysicalKeyboardKey.gameButtonSelect, + 'GameButtonStart': PhysicalKeyboardKey.gameButtonStart, + 'GameButtonThumbLeft': PhysicalKeyboardKey.gameButtonThumbLeft, + 'GameButtonThumbRight': PhysicalKeyboardKey.gameButtonThumbRight, + 'GameButtonX': PhysicalKeyboardKey.gameButtonX, + 'GameButtonY': PhysicalKeyboardKey.gameButtonY, + 'GameButtonZ': PhysicalKeyboardKey.gameButtonZ, +}; + +/// A map of Web KeyboardEvent codes which have printable representations, but appear +/// on the number pad. Used to provide different key objects for keys like +/// KEY_EQUALS and NUMPAD_EQUALS. +const Map kWebNumPadMap = { + 'NumpadDivide': LogicalKeyboardKey.numpadDivide, + 'NumpadMultiply': LogicalKeyboardKey.numpadMultiply, + 'NumpadSubtract': LogicalKeyboardKey.numpadSubtract, + 'NumpadAdd': LogicalKeyboardKey.numpadAdd, + 'Numpad1': LogicalKeyboardKey.numpad1, + 'Numpad2': LogicalKeyboardKey.numpad2, + 'Numpad3': LogicalKeyboardKey.numpad3, + 'Numpad4': LogicalKeyboardKey.numpad4, + 'Numpad5': LogicalKeyboardKey.numpad5, + 'Numpad6': LogicalKeyboardKey.numpad6, + 'Numpad7': LogicalKeyboardKey.numpad7, + 'Numpad8': LogicalKeyboardKey.numpad8, + 'Numpad9': LogicalKeyboardKey.numpad9, + 'Numpad0': LogicalKeyboardKey.numpad0, + 'NumpadDecimal': LogicalKeyboardKey.numpadDecimal, + 'NumpadEqual': LogicalKeyboardKey.numpadEqual, + 'NumpadComma': LogicalKeyboardKey.numpadComma, + 'NumpadParenLeft': LogicalKeyboardKey.numpadParenLeft, + 'NumpadParenRight': LogicalKeyboardKey.numpadParenRight, +}; + diff --git a/packages/flutter/lib/src/services/raw_keyboard.dart b/packages/flutter/lib/src/services/raw_keyboard.dart index b04712ff874..6b53aaa8140 100644 --- a/packages/flutter/lib/src/services/raw_keyboard.dart +++ b/packages/flutter/lib/src/services/raw_keyboard.dart @@ -11,6 +11,7 @@ import 'raw_keyboard_android.dart'; import 'raw_keyboard_fuchsia.dart'; import 'raw_keyboard_linux.dart'; import 'raw_keyboard_macos.dart'; +import 'raw_keyboard_web.dart'; import 'system_channels.dart'; /// An enum describing the side of the keyboard that a key is on, to allow @@ -284,6 +285,13 @@ abstract class RawKeyEvent extends Diagnosticable { scanCode: message['scanCode'] ?? 0, modifiers: message['modifiers'] ?? 0); break; + case 'web': + data = RawKeyEventDataWeb( + code: message['code'], + key: message['key'], + metaState: message['metaState'], + ); + break; default: // We don't yet implement raw key events on iOS or other platforms, but // we don't hit this exception because the engine never sends us these diff --git a/packages/flutter/lib/src/services/raw_keyboard_web.dart b/packages/flutter/lib/src/services/raw_keyboard_web.dart new file mode 100644 index 00000000000..a0dc66374b0 --- /dev/null +++ b/packages/flutter/lib/src/services/raw_keyboard_web.dart @@ -0,0 +1,198 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import 'keyboard_key.dart'; +import 'keyboard_maps.dart'; +import 'raw_keyboard.dart'; + +/// Platform-specific key event data for Web. +/// +/// See also: +/// +/// * [RawKeyboard], which uses this interface to expose key data. +@immutable +class RawKeyEventDataWeb extends RawKeyEventData { + /// Creates a key event data structure specific for Web. + /// + /// The [keyCode] and [metaState] arguments must not be null. + const RawKeyEventDataWeb({ + @required this.code, + @required this.key, + this.metaState = modifierNone, + }) : assert(code != null), + assert(metaState != null); + + /// The `KeyboardEvent.code` corresponding to this event. + /// + /// See + /// for more information. + final String code; + + /// The `KeyboardEvent.key` corresponding to this event. + /// + /// See + /// for more information. + final String key; + + /// The modifiers that were present when the key event occurred. + /// + /// See `lib/src/engine/keyboard.dart` in the web engine for the numerical + /// values of the `metaState`. These constants are also replicated as static + /// constants in this class. + /// + /// See also: + /// + /// * [modifiersPressed], which returns a Map of currently pressed modifiers + /// and their keyboard side. + /// * [isModifierPressed], to see if a specific modifier is pressed. + /// * [isControlPressed], to see if a CTRL key is pressed. + /// * [isShiftPressed], to see if a SHIFT key is pressed. + /// * [isAltPressed], to see if an ALT key is pressed. + /// * [isMetaPressed], to see if a META key is pressed. + final int metaState; + + @override + String get keyLabel => key; + + @override + PhysicalKeyboardKey get physicalKey { + return kWebToPhysicalKey[code] ?? PhysicalKeyboardKey.none; + } + + @override + LogicalKeyboardKey get logicalKey { + // Look to see if the keyCode is a printable number pad key, so that a + // difference between regular keys (e.g. ".") and the number pad version + // (e.g. the "." on the number pad) can be determined. + final LogicalKeyboardKey numPadKey = kWebNumPadMap[code]; + if (numPadKey != null) { + return numPadKey; + } + + // Look to see if the [code] is one we know about and have a mapping for. + final LogicalKeyboardKey newKey = kWebToLogicalKey[code]; + if (newKey != null) { + return newKey; + } + + // This is a non-printable key that we don't know about, so we mint a new + // code with the autogenerated bit set. + const int webKeyIdPlane = 0x00800000000; + return LogicalKeyboardKey( + webKeyIdPlane | code.hashCode | LogicalKeyboardKey.autogeneratedMask, + debugName: kReleaseMode ? null : 'Unknown Web code "$code"', + ); + } + + @override + bool isModifierPressed( + ModifierKey key, { + KeyboardSide side = KeyboardSide.any, + }) { + switch (key) { + case ModifierKey.controlModifier: + return metaState & modifierControl != 0; + case ModifierKey.shiftModifier: + return metaState & modifierShift != 0; + case ModifierKey.altModifier: + return metaState & modifierAlt != 0; + case ModifierKey.metaModifier: + return metaState & modifierMeta != 0; + case ModifierKey.numLockModifier: + return metaState & modifierNumLock != 0; + case ModifierKey.capsLockModifier: + return metaState & modifierCapsLock != 0; + case ModifierKey.scrollLockModifier: + return metaState & modifierScrollLock != 0; + case ModifierKey.functionModifier: + case ModifierKey.symbolModifier: + default: + // On Web, the browser doesn't report the state of the FN and SYM modifiers. + return false; + } + } + + @override + KeyboardSide getModifierSide(ModifierKey key) { + // On Web, we don't distinguish the sides of modifier keys. Both left shift + // and right shift, for example, are reported as the "Shift" modifier. + // + // See + // for more information. + return KeyboardSide.all; + } + + // Modifier key masks. + + /// No modifier keys are pressed in the [metaState] field. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierNone = 0; + + /// This mask is used to check the [metaState] field to test whether one of + /// the SHIFT modifier keys is pressed. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierShift = 0x01; + + /// This mask is used to check the [metaState] field to test whether one of + /// the ALT modifier keys is pressed. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierAlt = 0x02; + + /// This mask is used to check the [metaState] field to test whether one of + /// the CTRL modifier keys is pressed. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierControl = 0x04; + + /// This mask is used to check the [metaState] field to test whether one of + /// the META modifier keys is pressed. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierMeta = 0x08; + + /// This mask is used to check the [metaState] field to test whether the NUM + /// LOCK modifier key is on. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierNumLock = 0x10; + + /// This mask is used to check the [metaState] field to test whether the CAPS + /// LOCK modifier key is on. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierCapsLock = 0x20; + + /// This mask is used to check the [metaState] field to test whether the + /// SCROLL LOCK modifier key is on. + /// + /// Use this value if you need to decode the [metaState] field yourself, but + /// it's much easier to use [isModifierPressed] if you just want to know if + /// a modifier is pressed. + static const int modifierScrollLock = 0x40; + + @override + String toString() { + return '$runtimeType(keyLabel: $keyLabel, code: $code, ' + 'metaState: $metaState, modifiers down: $modifiersPressed)'; + } +} diff --git a/packages/flutter/test/services/raw_keyboard_test.dart b/packages/flutter/test/services/raw_keyboard_test.dart index c5438f5e5d7..7a78b110986 100644 --- a/packages/flutter/test/services/raw_keyboard_test.dart +++ b/packages/flutter/test/services/raw_keyboard_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@TestOn('!chrome') // web does not have keyboard support yet. - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -321,7 +319,7 @@ void main() { expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft)); expect(data.keyLabel, isNull); }); - }); + }, skip: isBrowser); group('RawKeyEventDataMacOs', () { const Map modifierTests = { RawKeyEventDataMacOs.modifierOption | RawKeyEventDataMacOs.modifierLeftOption: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.left), @@ -465,8 +463,7 @@ void main() { expect(data.logicalKey, equals(LogicalKeyboardKey.arrowLeft)); expect(data.logicalKey.keyLabel, isNull); }); - - }); + }, skip: isBrowser); group('RawKeyEventDataLinux-GFLW', () { const Map modifierTests = { GLFWKeyHelper.modifierAlt: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.any), @@ -622,5 +619,130 @@ void main() { expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft)); expect(data.keyLabel, isNull); }); + }, skip: isBrowser); + + group('RawKeyEventDataWeb', () { + const Map modifierTests = { + RawKeyEventDataWeb.modifierAlt: ModifierKey.altModifier, + RawKeyEventDataWeb.modifierShift: ModifierKey.shiftModifier, + RawKeyEventDataWeb.modifierControl: ModifierKey.controlModifier, + RawKeyEventDataWeb.modifierMeta: ModifierKey.metaModifier, + RawKeyEventDataWeb.modifierCapsLock: ModifierKey.capsLockModifier, + RawKeyEventDataWeb.modifierNumLock: ModifierKey.numLockModifier, + RawKeyEventDataWeb.modifierScrollLock: ModifierKey.scrollLockModifier, + }; + + test('modifier keys are recognized individually', () { + for (int modifier in modifierTests.keys) { + final RawKeyEvent event = RawKeyEvent.fromMessage({ + 'type': 'keydown', + 'keymap': 'web', + 'code': 'RandomCode', + 'key': null, + 'metaState': modifier, + }); + final RawKeyEventDataWeb data = event.data; + for (ModifierKey key in ModifierKey.values) { + if (modifierTests[modifier] == key) { + expect( + data.isModifierPressed(key), + isTrue, + reason: "$key should be pressed with metaState $modifier, but isn't.", + ); + } else { + expect( + data.isModifierPressed(key), + isFalse, + reason: '$key should not be pressed with metaState $modifier.', + ); + } + } + } + }); + test('modifier keys are recognized when combined', () { + for (int modifier in modifierTests.keys) { + if (modifier == RawKeyEventDataWeb.modifierMeta) { + // No need to combine meta key with itself. + continue; + } + final RawKeyEvent event = RawKeyEvent.fromMessage({ + 'type': 'keydown', + 'keymap': 'web', + 'code': 'RandomCode', + 'key': null, + 'metaState': modifier | RawKeyEventDataWeb.modifierMeta, + }); + final RawKeyEventDataWeb data = event.data; + for (ModifierKey key in ModifierKey.values) { + if (modifierTests[modifier] == key || key == ModifierKey.metaModifier) { + expect( + data.isModifierPressed(key), + isTrue, + reason: '$key should be pressed with metaState $modifier ' + "and additional key ${RawKeyEventDataWeb.modifierMeta}, but isn't.", + ); + } else { + expect( + data.isModifierPressed(key), + isFalse, + reason: '$key should not be pressed with metaState $modifier ' + 'and additional key ${RawKeyEventDataWeb.modifierMeta}.', + ); + } + } + } + }); + test('Printable keyboard keys are correctly translated', () { + final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'web', + 'code': 'KeyA', + 'key': 'a', + 'metaState': 0x0, + }); + final RawKeyEventDataWeb data = keyAEvent.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA)); + expect(data.logicalKey, equals(LogicalKeyboardKey.keyA)); + expect(data.keyLabel, equals('a')); + }); + test('Control keyboard keys are correctly translated', () { + final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'web', + 'code': 'Escape', + 'key': null, + 'metaState': 0x0, + }); + final RawKeyEventDataWeb data = escapeKeyEvent.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.escape)); + expect(data.logicalKey, equals(LogicalKeyboardKey.escape)); + expect(data.keyLabel, isNull); + }); + test('Modifier keyboard keys are correctly translated', () { + final RawKeyEvent shiftKeyEvent = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'web', + 'code': 'ShiftLeft', + 'keyLabel': null, + 'metaState': RawKeyEventDataWeb.modifierShift, + }); + final RawKeyEventDataWeb data = shiftKeyEvent.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft)); + expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft)); + expect(data.keyLabel, isNull); + }); + test('Arrow keys from a keyboard give correct physical key mappings', () { + final RawKeyEvent arrowKeyDown = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'web', + 'code': 'ArrowDown', + 'key': null, + 'metaState': 0x0, + }); + final RawKeyEventDataWeb data = arrowKeyDown.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowDown)); + expect(data.logicalKey, equals(LogicalKeyboardKey.arrowDown)); + expect(data.keyLabel, isNull); + }); }); }