[gen_keycodes] Remove nonexistent Web keys and improve their emulation (#87098)
This commit is contained in:
parent
c451b6b0d7
commit
a7899c1961
File diff suppressed because it is too large
Load Diff
@ -11,11 +11,7 @@ import 'package:path/path.dart' as path;
|
||||
import 'constants.dart';
|
||||
import 'physical_key_data.dart';
|
||||
|
||||
bool _isControlCharacter(String label) {
|
||||
if (label.length != 1) {
|
||||
return false;
|
||||
}
|
||||
final int codeUnit = label.codeUnitAt(0);
|
||||
bool _isControlCharacter(int codeUnit) {
|
||||
return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f);
|
||||
}
|
||||
|
||||
@ -27,8 +23,11 @@ class _ModifierPair {
|
||||
final String right;
|
||||
}
|
||||
|
||||
List<T> _toNonEmptyArray<T>(dynamic source) {
|
||||
final List<dynamic>? dynamicNullableList = source as List<dynamic>?;
|
||||
// Return map[key1][key2] as a non-nullable List<T>, where both map[key1] or
|
||||
// map[key1][key2] might be null.
|
||||
List<T> _getGrandchildList<T>(Map<String, dynamic> map, String key1, String key2) {
|
||||
final dynamic value = (map[key1] as Map<String, dynamic>?)?[key2];
|
||||
final List<dynamic>? dynamicNullableList = value as List<dynamic>?;
|
||||
final List<dynamic> dynamicList = dynamicNullableList ?? <dynamic>[];
|
||||
return dynamicList.cast<T>();
|
||||
}
|
||||
@ -155,20 +154,23 @@ class LogicalKeyData {
|
||||
final int value = match.namedGroup('unicode') != null ?
|
||||
getHex(match.namedGroup('unicode')!) :
|
||||
match.namedGroup('char')!.codeUnitAt(0);
|
||||
final String? keyLabel = match.namedGroup('kind')! == 'UNI' ? String.fromCharCode(value) : null;
|
||||
final String? keyLabel = (match.namedGroup('kind')! == 'UNI' && !_isControlCharacter(value)) ?
|
||||
String.fromCharCode(value) : null;
|
||||
// Skip modifier keys from DOM. They will be added with supplemental data.
|
||||
if (_chromeModifiers.containsKey(name) && source == 'DOM') {
|
||||
continue;
|
||||
}
|
||||
|
||||
final bool isPrintable = (keyLabel != null && !_isControlCharacter(keyLabel))
|
||||
|| printable.containsKey(name);
|
||||
final bool isPrintable = keyLabel != null;
|
||||
data.putIfAbsent(name, () {
|
||||
return LogicalKeyEntry.fromName(
|
||||
final LogicalKeyEntry entry = LogicalKeyEntry.fromName(
|
||||
value: toPlane(value, _sourceToPlane(source, isPrintable)),
|
||||
name: name,
|
||||
keyLabel: keyLabel,
|
||||
)..webNames.add(webName);
|
||||
);
|
||||
if (source == 'DOM' && !isPrintable)
|
||||
entry.webNames.add(webName);
|
||||
return entry;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -418,9 +420,11 @@ class LogicalKeyData {
|
||||
})();
|
||||
|
||||
static int _sourceToPlane(String source, bool isPrintable) {
|
||||
if (isPrintable)
|
||||
return kUnicodePlane.value;
|
||||
switch (source) {
|
||||
case 'DOM':
|
||||
return isPrintable ? kUnicodePlane.value : kUnprintablePlane.value;
|
||||
return kUnprintablePlane.value;
|
||||
case 'FLUTTER':
|
||||
return kFlutterPlane.value;
|
||||
default:
|
||||
@ -470,20 +474,20 @@ class LogicalKeyEntry {
|
||||
LogicalKeyEntry.fromJsonMapEntry(Map<String, dynamic> map)
|
||||
: value = map['value'] as int,
|
||||
name = map['name'] as String,
|
||||
webNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['web']),
|
||||
macOSKeyCodeNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['macos']),
|
||||
macOSKeyCodeValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['macos']),
|
||||
iOSKeyCodeNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['ios']),
|
||||
iOSKeyCodeValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['ios']),
|
||||
gtkNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['gtk']),
|
||||
gtkValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['gtk']),
|
||||
windowsNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['windows']),
|
||||
windowsValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['windows']),
|
||||
androidNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['android']),
|
||||
androidValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['android']),
|
||||
fuchsiaValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['fuchsia']),
|
||||
glfwNames = _toNonEmptyArray<String>((map['names'] as Map<String, dynamic>)['glfw']),
|
||||
glfwValues = _toNonEmptyArray<int>((map['values'] as Map<String, dynamic>?)?['glfw']),
|
||||
webNames = _getGrandchildList<String>(map, 'names', 'web'),
|
||||
macOSKeyCodeNames = _getGrandchildList<String>(map, 'names', 'macos'),
|
||||
macOSKeyCodeValues = _getGrandchildList<int>(map, 'values', 'macos'),
|
||||
iOSKeyCodeNames = _getGrandchildList<String>(map, 'names', 'ios'),
|
||||
iOSKeyCodeValues = _getGrandchildList<int>(map, 'values', 'ios'),
|
||||
gtkNames = _getGrandchildList<String>(map, 'names', 'gtk'),
|
||||
gtkValues = _getGrandchildList<int>(map, 'values', 'gtk'),
|
||||
windowsNames = _getGrandchildList<String>(map, 'names', 'windows'),
|
||||
windowsValues = _getGrandchildList<int>(map, 'values', 'windows'),
|
||||
androidNames = _getGrandchildList<String>(map, 'names', 'android'),
|
||||
androidValues = _getGrandchildList<int>(map, 'values', 'android'),
|
||||
fuchsiaValues = _getGrandchildList<int>(map, 'values', 'fuchsia'),
|
||||
glfwNames = _getGrandchildList<String>(map, 'names', 'glfw'),
|
||||
glfwValues = _getGrandchildList<int>(map, 'values', 'glfw'),
|
||||
keyLabel = map['keyLabel'] as String?;
|
||||
|
||||
final int value;
|
||||
|
@ -22,8 +22,10 @@ String readDataFile(String fileName) {
|
||||
return File(path.join(dataRoot, fileName)).readAsStringSync();
|
||||
}
|
||||
|
||||
final String testPhysicalData = path.join(dataRoot, 'physical_key_data.json');
|
||||
final String testLogicalData = path.join(dataRoot,'logical_key_data.json');
|
||||
final PhysicalKeyData physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(readDataFile('physical_key_data.json')) as Map<String, dynamic>);
|
||||
final LogicalKeyData logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(readDataFile('logical_key_data.json')) as Map<String, dynamic>);
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
@ -45,14 +47,6 @@ void main() {
|
||||
}
|
||||
|
||||
test('Generate Keycodes for Android', () {
|
||||
PhysicalKeyData physicalData;
|
||||
LogicalKeyData logicalData;
|
||||
|
||||
physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(File(testPhysicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(File(testLogicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
|
||||
const String platform = 'android';
|
||||
final PlatformCodeGenerator codeGenerator = AndroidCodeGenerator(
|
||||
physicalData,
|
||||
@ -67,14 +61,6 @@ void main() {
|
||||
checkCommonOutput(output);
|
||||
});
|
||||
test('Generate Keycodes for macOS', () {
|
||||
PhysicalKeyData physicalData;
|
||||
LogicalKeyData logicalData;
|
||||
|
||||
physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(File(testPhysicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(File(testLogicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
|
||||
const String platform = 'macos';
|
||||
final PlatformCodeGenerator codeGenerator = MacOSCodeGenerator(
|
||||
physicalData,
|
||||
@ -93,14 +79,6 @@ void main() {
|
||||
checkCommonOutput(output);
|
||||
});
|
||||
test('Generate Keycodes for iOS', () {
|
||||
PhysicalKeyData physicalData;
|
||||
LogicalKeyData logicalData;
|
||||
|
||||
physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(File(testPhysicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(File(testLogicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
|
||||
const String platform = 'ios';
|
||||
final PlatformCodeGenerator codeGenerator = IOSCodeGenerator(
|
||||
physicalData,
|
||||
@ -120,14 +98,6 @@ void main() {
|
||||
checkCommonOutput(output);
|
||||
});
|
||||
test('Generate Keycodes for Windows', () {
|
||||
PhysicalKeyData physicalData;
|
||||
LogicalKeyData logicalData;
|
||||
|
||||
physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(File(testPhysicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(File(testLogicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
|
||||
const String platform = 'windows';
|
||||
final PlatformCodeGenerator codeGenerator = WindowsCodeGenerator(
|
||||
physicalData,
|
||||
@ -143,14 +113,6 @@ void main() {
|
||||
checkCommonOutput(output);
|
||||
});
|
||||
test('Generate Keycodes for Linux', () {
|
||||
PhysicalKeyData physicalData;
|
||||
LogicalKeyData logicalData;
|
||||
|
||||
physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(File(testPhysicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(File(testLogicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
|
||||
const String platform = 'gtk';
|
||||
final PlatformCodeGenerator codeGenerator = GtkCodeGenerator(
|
||||
physicalData,
|
||||
@ -166,14 +128,6 @@ void main() {
|
||||
checkCommonOutput(output);
|
||||
});
|
||||
test('Generate Keycodes for Web', () {
|
||||
PhysicalKeyData physicalData;
|
||||
LogicalKeyData logicalData;
|
||||
|
||||
physicalData = PhysicalKeyData.fromJson(
|
||||
json.decode(File(testPhysicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
logicalData = LogicalKeyData.fromJson(
|
||||
json.decode(File(testLogicalData).readAsStringSync()) as Map<String, dynamic>);
|
||||
|
||||
const String platform = 'web';
|
||||
final PlatformCodeGenerator codeGenerator = WebCodeGenerator(
|
||||
physicalData,
|
||||
@ -188,4 +142,25 @@ void main() {
|
||||
expect(output, contains('kWebLogicalLocationMap'));
|
||||
checkCommonOutput(output);
|
||||
});
|
||||
test('LogicalKeyData', () async {
|
||||
final List<LogicalKeyEntry> entries = logicalData.entries.toList();
|
||||
|
||||
// Regression tests for https://github.com/flutter/flutter/pull/87098
|
||||
|
||||
expect(
|
||||
entries.indexWhere((LogicalKeyEntry entry) => entry.name == 'ShiftLeft'),
|
||||
isNot(-1));
|
||||
expect(
|
||||
entries.indexWhere((LogicalKeyEntry entry) => entry.webNames.contains('ShiftLeft')),
|
||||
-1);
|
||||
// 'Shift' maps to both 'ShiftLeft' and 'ShiftRight', and should be resolved
|
||||
// by other ways.
|
||||
expect(
|
||||
entries.indexWhere((LogicalKeyEntry entry) => entry.webNames.contains('Shift')),
|
||||
-1);
|
||||
// Printable keys must not be added with Web key of their names.
|
||||
expect(
|
||||
entries.indexWhere((LogicalKeyEntry entry) => entry.webNames.contains('Slash')),
|
||||
-1);
|
||||
});
|
||||
}
|
||||
|
@ -2141,25 +2141,17 @@ const Map<int, PhysicalKeyboardKey> kLinuxToPhysicalKey = <int, PhysicalKeyboard
|
||||
const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboardKey>{
|
||||
'AVRInput': LogicalKeyboardKey.avrInput,
|
||||
'AVRPower': LogicalKeyboardKey.avrPower,
|
||||
'Abort': LogicalKeyboardKey.abort,
|
||||
'Accel': LogicalKeyboardKey.accel,
|
||||
'Accept': LogicalKeyboardKey.accept,
|
||||
'Add': LogicalKeyboardKey.add,
|
||||
'Again': LogicalKeyboardKey.again,
|
||||
'AllCandidates': LogicalKeyboardKey.allCandidates,
|
||||
'Alphanumeric': LogicalKeyboardKey.alphanumeric,
|
||||
'Alt': LogicalKeyboardKey.alt,
|
||||
'AltGraph': LogicalKeyboardKey.altGraph,
|
||||
'AltLeft': LogicalKeyboardKey.altLeft,
|
||||
'AltRight': LogicalKeyboardKey.altRight,
|
||||
'Ampersand': LogicalKeyboardKey.ampersand,
|
||||
'AppSwitch': LogicalKeyboardKey.appSwitch,
|
||||
'ArrowDown': LogicalKeyboardKey.arrowDown,
|
||||
'ArrowLeft': LogicalKeyboardKey.arrowLeft,
|
||||
'ArrowRight': LogicalKeyboardKey.arrowRight,
|
||||
'ArrowUp': LogicalKeyboardKey.arrowUp,
|
||||
'Asterisk': LogicalKeyboardKey.asterisk,
|
||||
'At': LogicalKeyboardKey.at,
|
||||
'Attn': LogicalKeyboardKey.attn,
|
||||
'AudioBalanceLeft': LogicalKeyboardKey.audioBalanceLeft,
|
||||
'AudioBalanceRight': LogicalKeyboardKey.audioBalanceRight,
|
||||
@ -2174,14 +2166,7 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'AudioVolumeDown': LogicalKeyboardKey.audioVolumeDown,
|
||||
'AudioVolumeMute': LogicalKeyboardKey.audioVolumeMute,
|
||||
'AudioVolumeUp': LogicalKeyboardKey.audioVolumeUp,
|
||||
'Backquote': LogicalKeyboardKey.backquote,
|
||||
'Backslash': LogicalKeyboardKey.backslash,
|
||||
'Backspace': LogicalKeyboardKey.backspace,
|
||||
'Bar': LogicalKeyboardKey.bar,
|
||||
'BraceLeft': LogicalKeyboardKey.braceLeft,
|
||||
'BraceRight': LogicalKeyboardKey.braceRight,
|
||||
'BracketLeft': LogicalKeyboardKey.bracketLeft,
|
||||
'BracketRight': LogicalKeyboardKey.bracketRight,
|
||||
'BrightnessDown': LogicalKeyboardKey.brightnessDown,
|
||||
'BrightnessUp': LogicalKeyboardKey.brightnessUp,
|
||||
'BrowserBack': LogicalKeyboardKey.browserBack,
|
||||
@ -2196,55 +2181,36 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'CameraFocus': LogicalKeyboardKey.cameraFocus,
|
||||
'Cancel': LogicalKeyboardKey.cancel,
|
||||
'CapsLock': LogicalKeyboardKey.capsLock,
|
||||
'Caret': LogicalKeyboardKey.caret,
|
||||
'ChannelDown': LogicalKeyboardKey.channelDown,
|
||||
'ChannelUp': LogicalKeyboardKey.channelUp,
|
||||
'Clear': LogicalKeyboardKey.clear,
|
||||
'Close': LogicalKeyboardKey.close,
|
||||
'ClosedCaptionToggle': LogicalKeyboardKey.closedCaptionToggle,
|
||||
'CodeInput': LogicalKeyboardKey.codeInput,
|
||||
'Colon': LogicalKeyboardKey.colon,
|
||||
'ColorF0Red': LogicalKeyboardKey.colorF0Red,
|
||||
'ColorF1Green': LogicalKeyboardKey.colorF1Green,
|
||||
'ColorF2Yellow': LogicalKeyboardKey.colorF2Yellow,
|
||||
'ColorF3Blue': LogicalKeyboardKey.colorF3Blue,
|
||||
'ColorF4Grey': LogicalKeyboardKey.colorF4Grey,
|
||||
'ColorF5Brown': LogicalKeyboardKey.colorF5Brown,
|
||||
'Comma': LogicalKeyboardKey.comma,
|
||||
'Compose': LogicalKeyboardKey.compose,
|
||||
'ContextMenu': LogicalKeyboardKey.contextMenu,
|
||||
'Control': LogicalKeyboardKey.control,
|
||||
'ControlLeft': LogicalKeyboardKey.controlLeft,
|
||||
'ControlRight': LogicalKeyboardKey.controlRight,
|
||||
'Convert': LogicalKeyboardKey.convert,
|
||||
'Copy': LogicalKeyboardKey.copy,
|
||||
'CrSel': LogicalKeyboardKey.crSel,
|
||||
'Cut': LogicalKeyboardKey.cut,
|
||||
'DVR': LogicalKeyboardKey.dvr,
|
||||
'Delete': LogicalKeyboardKey.delete,
|
||||
'Digit0': LogicalKeyboardKey.digit0,
|
||||
'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,
|
||||
'Dimmer': LogicalKeyboardKey.dimmer,
|
||||
'DisplaySwap': LogicalKeyboardKey.displaySwap,
|
||||
'Dollar': LogicalKeyboardKey.dollar,
|
||||
'Eisu': LogicalKeyboardKey.eisu,
|
||||
'Eject': LogicalKeyboardKey.eject,
|
||||
'End': LogicalKeyboardKey.end,
|
||||
'EndCall': LogicalKeyboardKey.endCall,
|
||||
'Enter': LogicalKeyboardKey.enter,
|
||||
'Equal': LogicalKeyboardKey.equal,
|
||||
'EraseEof': LogicalKeyboardKey.eraseEof,
|
||||
'Escape': LogicalKeyboardKey.escape,
|
||||
'ExSel': LogicalKeyboardKey.exSel,
|
||||
'Exclamation': LogicalKeyboardKey.exclamation,
|
||||
'Execute': LogicalKeyboardKey.execute,
|
||||
'Exit': LogicalKeyboardKey.exit,
|
||||
'F1': LogicalKeyboardKey.f1,
|
||||
@ -2287,40 +2253,8 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'Find': LogicalKeyboardKey.find,
|
||||
'Fn': LogicalKeyboardKey.fn,
|
||||
'FnLock': LogicalKeyboardKey.fnLock,
|
||||
'GameButton1': LogicalKeyboardKey.gameButton1,
|
||||
'GameButton10': LogicalKeyboardKey.gameButton10,
|
||||
'GameButton11': LogicalKeyboardKey.gameButton11,
|
||||
'GameButton12': LogicalKeyboardKey.gameButton12,
|
||||
'GameButton13': LogicalKeyboardKey.gameButton13,
|
||||
'GameButton14': LogicalKeyboardKey.gameButton14,
|
||||
'GameButton15': LogicalKeyboardKey.gameButton15,
|
||||
'GameButton16': LogicalKeyboardKey.gameButton16,
|
||||
'GameButton2': LogicalKeyboardKey.gameButton2,
|
||||
'GameButton3': LogicalKeyboardKey.gameButton3,
|
||||
'GameButton4': LogicalKeyboardKey.gameButton4,
|
||||
'GameButton5': LogicalKeyboardKey.gameButton5,
|
||||
'GameButton6': LogicalKeyboardKey.gameButton6,
|
||||
'GameButton7': LogicalKeyboardKey.gameButton7,
|
||||
'GameButton8': LogicalKeyboardKey.gameButton8,
|
||||
'GameButton9': LogicalKeyboardKey.gameButton9,
|
||||
'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,
|
||||
'GoBack': LogicalKeyboardKey.goBack,
|
||||
'GoHome': LogicalKeyboardKey.goHome,
|
||||
'Greater': LogicalKeyboardKey.greater,
|
||||
'GroupFirst': LogicalKeyboardKey.groupFirst,
|
||||
'GroupLast': LogicalKeyboardKey.groupLast,
|
||||
'GroupNext': LogicalKeyboardKey.groupNext,
|
||||
@ -2341,46 +2275,12 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'Info': LogicalKeyboardKey.info,
|
||||
'Insert': LogicalKeyboardKey.insert,
|
||||
'InstantReplay': LogicalKeyboardKey.instantReplay,
|
||||
'IntlBackslash': LogicalKeyboardKey.intlBackslash,
|
||||
'IntlRo': LogicalKeyboardKey.intlRo,
|
||||
'IntlYen': LogicalKeyboardKey.intlYen,
|
||||
'JunjaMode': LogicalKeyboardKey.junjaMode,
|
||||
'KanaMode': LogicalKeyboardKey.kanaMode,
|
||||
'KanjiMode': LogicalKeyboardKey.kanjiMode,
|
||||
'Katakana': LogicalKeyboardKey.katakana,
|
||||
'Key11': LogicalKeyboardKey.key11,
|
||||
'Key12': LogicalKeyboardKey.key12,
|
||||
'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,
|
||||
'Lang1': LogicalKeyboardKey.lang1,
|
||||
'Lang2': LogicalKeyboardKey.lang2,
|
||||
'Lang3': LogicalKeyboardKey.lang3,
|
||||
'Lang4': LogicalKeyboardKey.lang4,
|
||||
'Lang5': LogicalKeyboardKey.lang5,
|
||||
'LastNumberRedial': LogicalKeyboardKey.lastNumberRedial,
|
||||
'LaunchApplication1': LogicalKeyboardKey.launchApplication1,
|
||||
'LaunchApplication2': LogicalKeyboardKey.launchApplication2,
|
||||
@ -2397,7 +2297,6 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'LaunchWebBrowser': LogicalKeyboardKey.launchWebBrowser,
|
||||
'LaunchWebCam': LogicalKeyboardKey.launchWebCam,
|
||||
'LaunchWordProcessor': LogicalKeyboardKey.launchWordProcessor,
|
||||
'Less': LogicalKeyboardKey.less,
|
||||
'Link': LogicalKeyboardKey.link,
|
||||
'ListProgram': LogicalKeyboardKey.listProgram,
|
||||
'LiveContent': LogicalKeyboardKey.liveContent,
|
||||
@ -2426,14 +2325,10 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'MediaTopMenu': LogicalKeyboardKey.mediaTopMenu,
|
||||
'MediaTrackNext': LogicalKeyboardKey.mediaTrackNext,
|
||||
'MediaTrackPrevious': LogicalKeyboardKey.mediaTrackPrevious,
|
||||
'Meta': LogicalKeyboardKey.meta,
|
||||
'MetaLeft': LogicalKeyboardKey.metaLeft,
|
||||
'MetaRight': LogicalKeyboardKey.metaRight,
|
||||
'MicrophoneToggle': LogicalKeyboardKey.microphoneToggle,
|
||||
'MicrophoneVolumeDown': LogicalKeyboardKey.microphoneVolumeDown,
|
||||
'MicrophoneVolumeMute': LogicalKeyboardKey.microphoneVolumeMute,
|
||||
'MicrophoneVolumeUp': LogicalKeyboardKey.microphoneVolumeUp,
|
||||
'Minus': LogicalKeyboardKey.minus,
|
||||
'ModeChange': LogicalKeyboardKey.modeChange,
|
||||
'NavigateIn': LogicalKeyboardKey.navigateIn,
|
||||
'NavigateNext': LogicalKeyboardKey.navigateNext,
|
||||
@ -2446,38 +2341,13 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'NonConvert': LogicalKeyboardKey.nonConvert,
|
||||
'Notification': LogicalKeyboardKey.notification,
|
||||
'NumLock': LogicalKeyboardKey.numLock,
|
||||
'NumberSign': LogicalKeyboardKey.numberSign,
|
||||
'Numpad0': LogicalKeyboardKey.numpad0,
|
||||
'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,
|
||||
'NumpadAdd': LogicalKeyboardKey.numpadAdd,
|
||||
'NumpadComma': LogicalKeyboardKey.numpadComma,
|
||||
'NumpadDecimal': LogicalKeyboardKey.numpadDecimal,
|
||||
'NumpadDivide': LogicalKeyboardKey.numpadDivide,
|
||||
'NumpadEnter': LogicalKeyboardKey.numpadEnter,
|
||||
'NumpadEqual': LogicalKeyboardKey.numpadEqual,
|
||||
'NumpadMultiply': LogicalKeyboardKey.numpadMultiply,
|
||||
'NumpadParenLeft': LogicalKeyboardKey.numpadParenLeft,
|
||||
'NumpadParenRight': LogicalKeyboardKey.numpadParenRight,
|
||||
'NumpadSubtract': LogicalKeyboardKey.numpadSubtract,
|
||||
'OnDemand': LogicalKeyboardKey.onDemand,
|
||||
'Open': LogicalKeyboardKey.open,
|
||||
'PageDown': LogicalKeyboardKey.pageDown,
|
||||
'PageUp': LogicalKeyboardKey.pageUp,
|
||||
'Pairing': LogicalKeyboardKey.pairing,
|
||||
'ParenthesisLeft': LogicalKeyboardKey.parenthesisLeft,
|
||||
'ParenthesisRight': LogicalKeyboardKey.parenthesisRight,
|
||||
'Paste': LogicalKeyboardKey.paste,
|
||||
'Pause': LogicalKeyboardKey.pause,
|
||||
'Percent': LogicalKeyboardKey.percent,
|
||||
'Period': LogicalKeyboardKey.period,
|
||||
'PinPDown': LogicalKeyboardKey.pInPDown,
|
||||
'PinPMove': LogicalKeyboardKey.pInPMove,
|
||||
'PinPToggle': LogicalKeyboardKey.pInPToggle,
|
||||
@ -2493,14 +2363,10 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'PrintScreen': LogicalKeyboardKey.printScreen,
|
||||
'Process': LogicalKeyboardKey.process,
|
||||
'Props': LogicalKeyboardKey.props,
|
||||
'Question': LogicalKeyboardKey.question,
|
||||
'Quote': LogicalKeyboardKey.quote,
|
||||
'QuoteSingle': LogicalKeyboardKey.quoteSingle,
|
||||
'RandomToggle': LogicalKeyboardKey.randomToggle,
|
||||
'RcLowBattery': LogicalKeyboardKey.rcLowBattery,
|
||||
'RecordSpeedNext': LogicalKeyboardKey.recordSpeedNext,
|
||||
'Redo': LogicalKeyboardKey.redo,
|
||||
'Resume': LogicalKeyboardKey.resume,
|
||||
'RfBypass': LogicalKeyboardKey.rfBypass,
|
||||
'Romaji': LogicalKeyboardKey.romaji,
|
||||
'STBInput': LogicalKeyboardKey.stbInput,
|
||||
@ -2510,15 +2376,9 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'ScreenModeNext': LogicalKeyboardKey.screenModeNext,
|
||||
'ScrollLock': LogicalKeyboardKey.scrollLock,
|
||||
'Select': LogicalKeyboardKey.select,
|
||||
'Semicolon': LogicalKeyboardKey.semicolon,
|
||||
'Settings': LogicalKeyboardKey.settings,
|
||||
'Shift': LogicalKeyboardKey.shift,
|
||||
'ShiftLeft': LogicalKeyboardKey.shiftLeft,
|
||||
'ShiftLevel5': LogicalKeyboardKey.shiftLevel5,
|
||||
'ShiftRight': LogicalKeyboardKey.shiftRight,
|
||||
'SingleCandidate': LogicalKeyboardKey.singleCandidate,
|
||||
'Slash': LogicalKeyboardKey.slash,
|
||||
'Sleep': LogicalKeyboardKey.sleep,
|
||||
'Soft1': LogicalKeyboardKey.soft1,
|
||||
'Soft2': LogicalKeyboardKey.soft2,
|
||||
'Soft3': LogicalKeyboardKey.soft3,
|
||||
@ -2527,7 +2387,6 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'Soft6': LogicalKeyboardKey.soft6,
|
||||
'Soft7': LogicalKeyboardKey.soft7,
|
||||
'Soft8': LogicalKeyboardKey.soft8,
|
||||
'Space': LogicalKeyboardKey.space,
|
||||
'SpeechCorrectionList': LogicalKeyboardKey.speechCorrectionList,
|
||||
'SpeechInputToggle': LogicalKeyboardKey.speechInputToggle,
|
||||
'SpellCheck': LogicalKeyboardKey.spellCheck,
|
||||
@ -2535,7 +2394,6 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'Standby': LogicalKeyboardKey.standby,
|
||||
'Subtitle': LogicalKeyboardKey.subtitle,
|
||||
'Super': LogicalKeyboardKey.superKey,
|
||||
'Suspend': LogicalKeyboardKey.suspend,
|
||||
'Symbol': LogicalKeyboardKey.symbol,
|
||||
'SymbolLock': LogicalKeyboardKey.symbolLock,
|
||||
'TV': LogicalKeyboardKey.tv,
|
||||
@ -2570,8 +2428,6 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
||||
'TVTimer': LogicalKeyboardKey.tvTimer,
|
||||
'Tab': LogicalKeyboardKey.tab,
|
||||
'Teletext': LogicalKeyboardKey.teletext,
|
||||
'Tilde': LogicalKeyboardKey.tilde,
|
||||
'Underscore': LogicalKeyboardKey.underscore,
|
||||
'Undo': LogicalKeyboardKey.undo,
|
||||
'Unidentified': LogicalKeyboardKey.unidentified,
|
||||
'VideoModeNext': LogicalKeyboardKey.videoModeNext,
|
||||
|
@ -285,12 +285,10 @@ abstract class RawKeyEvent with Diagnosticable {
|
||||
/// Creates a concrete [RawKeyEvent] class from a message in the form received
|
||||
/// on the [SystemChannels.keyEvent] channel.
|
||||
factory RawKeyEvent.fromMessage(Map<String, dynamic> message) {
|
||||
final RawKeyEventData data;
|
||||
String? character;
|
||||
|
||||
RawKeyEventData _dataFromWeb() {
|
||||
final String? key = message['key'] as String?;
|
||||
if (key != null && key.isNotEmpty) {
|
||||
if (key != null && key.isNotEmpty && key.length == 1) {
|
||||
character = key;
|
||||
}
|
||||
return RawKeyEventDataWeb(
|
||||
@ -300,6 +298,8 @@ abstract class RawKeyEvent with Diagnosticable {
|
||||
metaState: message['metaState'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
final RawKeyEventData data;
|
||||
if (kIsWeb) {
|
||||
data = _dataFromWeb();
|
||||
} else {
|
||||
|
@ -10,6 +10,13 @@ import 'keyboard_key.dart';
|
||||
import 'keyboard_maps.dart';
|
||||
import 'raw_keyboard.dart';
|
||||
|
||||
String? _unicodeChar(String key) {
|
||||
if (key.length == 1) {
|
||||
return key.substring(0, 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Platform-specific key event data for Web.
|
||||
///
|
||||
/// See also:
|
||||
@ -74,7 +81,7 @@ class RawKeyEventDataWeb extends RawKeyEventData {
|
||||
final int metaState;
|
||||
|
||||
@override
|
||||
String get keyLabel => key == 'Unidentified' ? '' : key;
|
||||
String get keyLabel => key == 'Unidentified' ? '' : _unicodeChar(key) ?? '';
|
||||
|
||||
@override
|
||||
PhysicalKeyboardKey get physicalKey {
|
||||
@ -95,9 +102,14 @@ class RawKeyEventDataWeb extends RawKeyEventData {
|
||||
return newKey;
|
||||
}
|
||||
|
||||
final bool isPrintable = key.length == 1;
|
||||
if (isPrintable)
|
||||
return LogicalKeyboardKey(key.codeUnitAt(0));
|
||||
|
||||
// This is a non-printable key that we don't know about, so we mint a new
|
||||
// code.
|
||||
return LogicalKeyboardKey(code.hashCode | LogicalKeyboardKey.webPlane);
|
||||
// key from `code`. Don't mint with `key`, because the `key` will always be
|
||||
// "Unidentified" .
|
||||
return LogicalKeyboardKey(code.hashCode + LogicalKeyboardKey.webPlane);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2400,6 +2400,7 @@ void main() {
|
||||
'keymap': 'web',
|
||||
'code': 'KeyA',
|
||||
'key': 'a',
|
||||
'location': 0,
|
||||
'metaState': 0x0,
|
||||
});
|
||||
final RawKeyEventDataWeb data = keyAEvent.data as RawKeyEventDataWeb;
|
||||
@ -2413,6 +2414,8 @@ void main() {
|
||||
'type': 'keydown',
|
||||
'keymap': 'web',
|
||||
'code': 'Escape',
|
||||
'key': 'Escape',
|
||||
'location': 0,
|
||||
'metaState': 0x0,
|
||||
});
|
||||
final RawKeyEventDataWeb data = escapeKeyEvent.data as RawKeyEventDataWeb;
|
||||
@ -2426,6 +2429,8 @@ void main() {
|
||||
'type': 'keydown',
|
||||
'keymap': 'web',
|
||||
'code': 'ShiftLeft',
|
||||
'key': 'Shift',
|
||||
'location': 1,
|
||||
'metaState': RawKeyEventDataWeb.modifierShift,
|
||||
});
|
||||
final RawKeyEventDataWeb data = shiftKeyEvent.data as RawKeyEventDataWeb;
|
||||
@ -2439,6 +2444,8 @@ void main() {
|
||||
'type': 'keydown',
|
||||
'keymap': 'web',
|
||||
'code': 'ArrowDown',
|
||||
'key': 'ArrowDown',
|
||||
'location': 0,
|
||||
'metaState': 0x0,
|
||||
});
|
||||
final RawKeyEventDataWeb data = arrowKeyDown.data as RawKeyEventDataWeb;
|
||||
@ -2447,6 +2454,25 @@ void main() {
|
||||
expect(data.keyLabel, isEmpty);
|
||||
});
|
||||
|
||||
test('Unrecognized keys are mapped to Web plane', () {
|
||||
final RawKeyEvent arrowKeyDown = RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'web',
|
||||
'code': 'Unrecog1',
|
||||
'key': 'Unrecog2',
|
||||
'location': 0,
|
||||
'metaState': 0x0,
|
||||
});
|
||||
final RawKeyEventDataWeb data = arrowKeyDown.data as RawKeyEventDataWeb;
|
||||
// This might be easily broken on Web if the code fails to acknowledge
|
||||
// that JavaScript doesn't handle 64-bit bit-wise operation.
|
||||
expect(data.physicalKey.usbHidUsage, greaterThan(0x01700000000));
|
||||
expect(data.physicalKey.usbHidUsage, lessThan(0x01800000000));
|
||||
expect(data.logicalKey.keyId, greaterThan(0x01700000000));
|
||||
expect(data.logicalKey.keyId, lessThan(0x01800000000));
|
||||
expect(data.keyLabel, isEmpty);
|
||||
});
|
||||
|
||||
test('data.toString', () {
|
||||
expect(RawKeyEvent.fromMessage(const <String, dynamic>{
|
||||
'type': 'keydown',
|
||||
|
@ -14,6 +14,16 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'binding.dart';
|
||||
import 'test_async_utils.dart';
|
||||
|
||||
// A tuple of `key` and `location` from Web's `KeyboardEvent` class.
|
||||
//
|
||||
// See [RawKeyEventDataWeb]'s `key` and `location` fields for details.
|
||||
@immutable
|
||||
class _WebKeyLocationPair {
|
||||
const _WebKeyLocationPair(this.key, this.location);
|
||||
final String key;
|
||||
final int location;
|
||||
}
|
||||
|
||||
// TODO(gspencergoog): Replace this with more robust key simulation code once
|
||||
// the new key event code is in.
|
||||
// https://github.com/flutter/flutter/issues/33521
|
||||
@ -145,8 +155,32 @@ class KeyEventSimulator {
|
||||
}
|
||||
}
|
||||
|
||||
static String _getWebKeyCode(LogicalKeyboardKey key) {
|
||||
static PhysicalKeyboardKey _inferPhysicalKey(LogicalKeyboardKey key) {
|
||||
PhysicalKeyboardKey? result;
|
||||
for (final PhysicalKeyboardKey physicalKey in PhysicalKeyboardKey.knownPhysicalKeys) {
|
||||
if (physicalKey.debugName == key.debugName) {
|
||||
result = physicalKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(result != null, 'Unable to infer physical key for $key');
|
||||
return result!;
|
||||
}
|
||||
|
||||
static _WebKeyLocationPair _getWebKeyLocation(LogicalKeyboardKey key, String keyLabel) {
|
||||
String? result;
|
||||
for (final MapEntry<String, List<LogicalKeyboardKey?>> entry in kWebLocationMap.entries) {
|
||||
final int foundIndex = entry.value.indexOf(key);
|
||||
// If foundIndex is -1, then the key is not defined in kWebLocationMap.
|
||||
// If foundIndex is 0, then the key is in the standard part of the keyboard,
|
||||
// but we have to check `keyLabel` to see if it's remapped or modified.
|
||||
if (foundIndex != -1 && foundIndex != 0) {
|
||||
return _WebKeyLocationPair(entry.key, foundIndex);
|
||||
}
|
||||
}
|
||||
if (keyLabel.isNotEmpty) {
|
||||
return _WebKeyLocationPair(keyLabel, 0);
|
||||
}
|
||||
for (final String code in kWebToLogicalKey.keys) {
|
||||
if (key.keyId == kWebToLogicalKey[code]!.keyId) {
|
||||
result = code;
|
||||
@ -154,6 +188,18 @@ class KeyEventSimulator {
|
||||
}
|
||||
}
|
||||
assert(result != null, 'Key $key not found in web keyCode map');
|
||||
return _WebKeyLocationPair(result!, 0);
|
||||
}
|
||||
|
||||
static String _getWebCode(PhysicalKeyboardKey key) {
|
||||
String? result;
|
||||
for (final MapEntry<String, PhysicalKeyboardKey> entry in kWebToPhysicalKey.entries) {
|
||||
if (entry.value.usbHidUsage == key.usbHidUsage) {
|
||||
result = entry.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(result != null, 'Key $key not found in web code map');
|
||||
return result!;
|
||||
}
|
||||
|
||||
@ -215,8 +261,6 @@ class KeyEventSimulator {
|
||||
physicalKey ??= _findPhysicalKeyByPlatform(key, platform);
|
||||
|
||||
assert(key.debugName != null);
|
||||
final int keyCode = _getKeyCode(key, platform);
|
||||
final int scanCode = _getScanCode(physicalKey, platform);
|
||||
|
||||
final Map<String, dynamic> result = <String, dynamic>{
|
||||
'type': isDown ? 'keydown' : 'keyup',
|
||||
@ -225,14 +269,19 @@ class KeyEventSimulator {
|
||||
|
||||
final String resultCharacter = character ?? _keyLabel(key) ?? '';
|
||||
void assignWeb() {
|
||||
result['code'] = _getWebKeyCode(key);
|
||||
result['key'] = resultCharacter;
|
||||
final _WebKeyLocationPair keyLocation = _getWebKeyLocation(key, resultCharacter);
|
||||
final PhysicalKeyboardKey actualPhysicalKey = physicalKey ?? _inferPhysicalKey(key);
|
||||
result['code'] = _getWebCode(actualPhysicalKey);
|
||||
result['key'] = keyLocation.key;
|
||||
result['location'] = keyLocation.location;
|
||||
result['metaState'] = _getWebModifierFlags(key, isDown);
|
||||
}
|
||||
if (kIsWeb) {
|
||||
assignWeb();
|
||||
return result;
|
||||
}
|
||||
final int keyCode = _getKeyCode(key, platform);
|
||||
final int scanCode = _getScanCode(physicalKey, platform);
|
||||
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
|
Loading…
x
Reference in New Issue
Block a user