mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[Keyboard, Web] Map from "Esc" to the Escape key (#106133)
* Impl * Fix build * Add test
This commit is contained in:
parent
df55dbb9d9
commit
0ac05f1c5c
@ -1552,7 +1552,8 @@
|
|||||||
"value": 4294967323,
|
"value": 4294967323,
|
||||||
"names": {
|
"names": {
|
||||||
"web": [
|
"web": [
|
||||||
"Escape"
|
"Escape",
|
||||||
|
"Esc"
|
||||||
],
|
],
|
||||||
"macos": [
|
"macos": [
|
||||||
"Escape"
|
"Escape"
|
||||||
|
@ -1192,6 +1192,9 @@
|
|||||||
"name": "Escape",
|
"name": "Escape",
|
||||||
"chromium": "Escape"
|
"chromium": "Escape"
|
||||||
},
|
},
|
||||||
|
"otherWebCodes": [
|
||||||
|
"Esc"
|
||||||
|
],
|
||||||
"scanCodes": {
|
"scanCodes": {
|
||||||
"android": [
|
"android": [
|
||||||
1
|
1
|
||||||
|
@ -50,6 +50,11 @@
|
|||||||
DOM_CODE(0x05ff1e, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonY", BUTTON_Y),
|
DOM_CODE(0x05ff1e, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonY", BUTTON_Y),
|
||||||
DOM_CODE(0x05ff1f, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonZ", BUTTON_Z),
|
DOM_CODE(0x05ff1f, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonZ", BUTTON_Z),
|
||||||
|
|
||||||
|
// Sometimes the Escape key produces "Esc" instead of "Escape". This includes
|
||||||
|
// older IE and Firefox browsers, and the current Cobalt browser.
|
||||||
|
// See: https://github.com/flutter/flutter/issues/106062
|
||||||
|
DOM_CODE(0x070029, 0x0000, 0x0000, 0x0000, 0xffff, "Esc", ESCAPE),
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Fn key for Mac
|
// Fn key for Mac
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@ -58,4 +63,4 @@
|
|||||||
// defined on other platforms. Chromium does define an "Fn" row, but doesn't
|
// defined on other platforms. Chromium does define an "Fn" row, but doesn't
|
||||||
// give it a Mac keycode. This overrides their definition.
|
// give it a Mac keycode. This overrides their definition.
|
||||||
// USB HID evdev XKB Win Mac DOMKey Code
|
// USB HID evdev XKB Win Mac DOMKey Code
|
||||||
DOM_CODE(0x000012, 0x0000, 0x0000, 0x0000, 0x003f, "Fn", FN),
|
DOM_CODE(0x000012, 0x0000, 0x0000, 0x0000, 0x003f, "Fn", FN),
|
||||||
|
@ -76,6 +76,16 @@
|
|||||||
DOM_KEY_UNI("Tilde", TILDE, '~'),
|
DOM_KEY_UNI("Tilde", TILDE, '~'),
|
||||||
DOM_KEY_UNI("Bar", BAR, '|'),
|
DOM_KEY_UNI("Bar", BAR, '|'),
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Unprintable keys (Unicode plane)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
// Key Enum Value
|
||||||
|
// Sometimes the Escape key produces "Esc" instead of "Escape". This includes
|
||||||
|
// older IE and Firefox browsers, and the current Cobalt browser.
|
||||||
|
// See: https://github.com/flutter/flutter/issues/106062
|
||||||
|
DOM_KEY_MAP("Esc", ESC, 0x1B),
|
||||||
|
|
||||||
// The following keys reside in the Flutter plane (0x0100000000).
|
// The following keys reside in the Flutter plane (0x0100000000).
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
@ -78,7 +78,7 @@ $otherComments static const PhysicalKeyboardKey ${entry.constantName} = Physica
|
|||||||
|
|
||||||
/// Gets the generated definitions of LogicalKeyboardKeys.
|
/// Gets the generated definitions of LogicalKeyboardKeys.
|
||||||
String get _logicalDefinitions {
|
String get _logicalDefinitions {
|
||||||
final OutputLines<int> lines = OutputLines<int>('Logical debug names');
|
final OutputLines<int> lines = OutputLines<int>('Logical debug names', behavior: DeduplicateBehavior.kSkip);
|
||||||
void printKey(int flutterId, String constantName, String commentName, {String? otherComments}) {
|
void printKey(int flutterId, String constantName, String commentName, {String? otherComments}) {
|
||||||
final String firstComment = _wrapString('Represents the logical "$commentName" key on the keyboard.');
|
final String firstComment = _wrapString('Represents the logical "$commentName" key on the keyboard.');
|
||||||
otherComments ??= _wrapString('See the function [RawKeyEvent.logicalKey] for more information.');
|
otherComments ??= _wrapString('See the function [RawKeyEvent.logicalKey] for more information.');
|
||||||
@ -122,7 +122,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get _logicalKeyLabels {
|
String get _logicalKeyLabels {
|
||||||
final OutputLines<int> lines = OutputLines<int>('Logical key labels');
|
final OutputLines<int> lines = OutputLines<int>('Logical key labels', behavior: DeduplicateBehavior.kSkip);
|
||||||
for (final LogicalKeyEntry entry in logicalData.entries) {
|
for (final LogicalKeyEntry entry in logicalData.entries) {
|
||||||
lines.add(entry.value, '''
|
lines.add(entry.value, '''
|
||||||
${toHex(entry.value, digits: 11)}: '${entry.commentName}',''');
|
${toHex(entry.value, digits: 11)}: '${entry.commentName}',''');
|
||||||
@ -141,7 +141,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
|||||||
|
|
||||||
/// This generates the map of Flutter key codes to logical keys.
|
/// This generates the map of Flutter key codes to logical keys.
|
||||||
String get _predefinedKeyCodeMap {
|
String get _predefinedKeyCodeMap {
|
||||||
final OutputLines<int> lines = OutputLines<int>('Logical key map');
|
final OutputLines<int> lines = OutputLines<int>('Logical key map', behavior: DeduplicateBehavior.kSkip);
|
||||||
for (final LogicalKeyEntry entry in logicalData.entries) {
|
for (final LogicalKeyEntry entry in logicalData.entries) {
|
||||||
lines.add(entry.value, ' ${toHex(entry.value, digits: 11)}: ${entry.constantName},');
|
lines.add(entry.value, ' ${toHex(entry.value, digits: 11)}: ${entry.constantName},');
|
||||||
}
|
}
|
||||||
@ -149,7 +149,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get _maskConstantVariables {
|
String get _maskConstantVariables {
|
||||||
final OutputLines<int> lines = OutputLines<int>('Mask constants', checkDuplicate: false);
|
final OutputLines<int> lines = OutputLines<int>('Mask constants', behavior: DeduplicateBehavior.kKeep);
|
||||||
for (final MaskConstant constant in _maskConstants) {
|
for (final MaskConstant constant in _maskConstants) {
|
||||||
lines.add(constant.value, '''
|
lines.add(constant.value, '''
|
||||||
${_wrapString(constant.description)} ///
|
${_wrapString(constant.description)} ///
|
||||||
|
@ -303,7 +303,7 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
|
|||||||
|
|
||||||
/// This generates the map of Web KeyboardEvent codes to physical keys.
|
/// This generates the map of Web KeyboardEvent codes to physical keys.
|
||||||
String get _webPhysicalKeyMap {
|
String get _webPhysicalKeyMap {
|
||||||
final OutputLines<String> lines = OutputLines<String>('Web physical key map');
|
final OutputLines<String> lines = OutputLines<String>('Web physical key map', behavior: DeduplicateBehavior.kKeep);
|
||||||
for (final PhysicalKeyEntry entry in keyData.entries) {
|
for (final PhysicalKeyEntry entry in keyData.entries) {
|
||||||
for (final String webCodes in entry.webCodes()) {
|
for (final String webCodes in entry.webCodes()) {
|
||||||
lines.add(entry.name, " '$webCodes': PhysicalKeyboardKey.${entry.constantName},");
|
lines.add(entry.name, " '$webCodes': PhysicalKeyboardKey.${entry.constantName},");
|
||||||
|
@ -53,8 +53,7 @@ class LogicalKeyData {
|
|||||||
String glfwNameMap,
|
String glfwNameMap,
|
||||||
PhysicalKeyData physicalKeyData,
|
PhysicalKeyData physicalKeyData,
|
||||||
) {
|
) {
|
||||||
final Map<String, LogicalKeyEntry> data = <String, LogicalKeyEntry>{};
|
final Map<String, LogicalKeyEntry> data = _readKeyEntries(chromiumKeys);
|
||||||
_readKeyEntries(data, chromiumKeys);
|
|
||||||
_readWindowsKeyCodes(data, windowsKeyCodeHeader, parseMapOfListOfString(windowsNameMap));
|
_readWindowsKeyCodes(data, windowsKeyCodeHeader, parseMapOfListOfString(windowsNameMap));
|
||||||
_readGtkKeyCodes(data, gtkKeyCodeHeader, parseMapOfListOfString(gtkNameMap));
|
_readGtkKeyCodes(data, gtkKeyCodeHeader, parseMapOfListOfString(gtkNameMap));
|
||||||
_readAndroidKeyCodes(data, androidKeyCodeHeader, parseMapOfListOfString(androidNameMap));
|
_readAndroidKeyCodes(data, androidKeyCodeHeader, parseMapOfListOfString(androidNameMap));
|
||||||
@ -130,7 +129,8 @@ class LogicalKeyData {
|
|||||||
/// The following format should be mapped to the Flutter plane.
|
/// The following format should be mapped to the Flutter plane.
|
||||||
/// Key Enum Character
|
/// Key Enum Character
|
||||||
/// FLUTTER_KEY_MAP("Lang4", LANG4, 0x00013),
|
/// FLUTTER_KEY_MAP("Lang4", LANG4, 0x00013),
|
||||||
static void _readKeyEntries(Map<String, LogicalKeyEntry> data, String input) {
|
static Map<String, LogicalKeyEntry> _readKeyEntries(String input) {
|
||||||
|
final Map<int, LogicalKeyEntry> dataByValue = <int, LogicalKeyEntry>{};
|
||||||
final RegExp domKeyRegExp = RegExp(
|
final RegExp domKeyRegExp = RegExp(
|
||||||
r'(?<source>DOM|FLUTTER)_KEY_(?<kind>UNI|MAP)\s*\(\s*'
|
r'(?<source>DOM|FLUTTER)_KEY_(?<kind>UNI|MAP)\s*\(\s*'
|
||||||
r'"(?<name>[^\s]+?)",\s*'
|
r'"(?<name>[^\s]+?)",\s*'
|
||||||
@ -162,17 +162,23 @@ class LogicalKeyData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final bool isPrintable = keyLabel != null;
|
final bool isPrintable = keyLabel != null;
|
||||||
data.putIfAbsent(name, () {
|
final int entryValue = toPlane(value, _sourceToPlane(source, isPrintable));
|
||||||
final LogicalKeyEntry entry = LogicalKeyEntry.fromName(
|
final LogicalKeyEntry entry = dataByValue.putIfAbsent(entryValue, () =>
|
||||||
value: toPlane(value, _sourceToPlane(source, isPrintable)),
|
LogicalKeyEntry.fromName(
|
||||||
|
value: entryValue,
|
||||||
name: name,
|
name: name,
|
||||||
keyLabel: keyLabel,
|
keyLabel: keyLabel,
|
||||||
);
|
),
|
||||||
if (source == 'DOM' && !isPrintable)
|
);
|
||||||
entry.webNames.add(webName);
|
if (source == 'DOM' && !isPrintable) {
|
||||||
return entry;
|
entry.webNames.add(webName);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
return Map<String, LogicalKeyEntry>.fromEntries(
|
||||||
|
dataByValue.values.map((LogicalKeyEntry entry) =>
|
||||||
|
MapEntry<String, LogicalKeyEntry>(entry.name, entry),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _readMacOsKeyCodes(
|
static void _readMacOsKeyCodes(
|
||||||
|
@ -171,6 +171,22 @@ class PhysicalKeyData {
|
|||||||
// Skip key that is not actually generated by any keyboard.
|
// Skip key that is not actually generated by any keyboard.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
final PhysicalKeyEntry? existing = entries[usbHidCode];
|
||||||
|
// Allow duplicate entries for Fn, which overwrites.
|
||||||
|
if (existing != null && existing.name != 'Fn') {
|
||||||
|
// If it's an existing entry, the only thing we currently support is
|
||||||
|
// to insert an extra DOMKey. The other entries must be empty.
|
||||||
|
assert(evdevCode == 0
|
||||||
|
&& xKbScanCode == 0
|
||||||
|
&& windowsScanCode == 0
|
||||||
|
&& macScanCode == 0xffff
|
||||||
|
&& chromiumCode != null
|
||||||
|
&& chromiumCode.isNotEmpty,
|
||||||
|
'Duplicate usbHidCode ${existing.usbHidCode} of key ${existing.name} '
|
||||||
|
'conflicts with existing ${entries[existing.usbHidCode]!.name}.');
|
||||||
|
existing.otherWebCodes.add(chromiumCode!);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final PhysicalKeyEntry newEntry = PhysicalKeyEntry(
|
final PhysicalKeyEntry newEntry = PhysicalKeyEntry(
|
||||||
usbHidCode: usbHidCode,
|
usbHidCode: usbHidCode,
|
||||||
androidScanCodes: nameToAndroidScanCodes[name] ?? <int>[],
|
androidScanCodes: nameToAndroidScanCodes[name] ?? <int>[],
|
||||||
@ -182,15 +198,6 @@ class PhysicalKeyData {
|
|||||||
name: name,
|
name: name,
|
||||||
chromiumCode: chromiumCode,
|
chromiumCode: chromiumCode,
|
||||||
);
|
);
|
||||||
// Remove duplicates: last one wins, so that supplemental codes
|
|
||||||
// override.
|
|
||||||
if (entries.containsKey(newEntry.usbHidCode)) {
|
|
||||||
// This is expected for Fn. Warn for other keys.
|
|
||||||
if (newEntry.name != 'Fn') {
|
|
||||||
print('Duplicate usbHidCode ${newEntry.usbHidCode} of key ${newEntry.name} '
|
|
||||||
'conflicts with existing ${entries[newEntry.usbHidCode]!.name}. Keeping the new one.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entries[newEntry.usbHidCode] = newEntry;
|
entries[newEntry.usbHidCode] = newEntry;
|
||||||
}
|
}
|
||||||
return entries.map((int code, PhysicalKeyEntry entry) =>
|
return entries.map((int code, PhysicalKeyEntry entry) =>
|
||||||
@ -216,7 +223,8 @@ class PhysicalKeyEntry {
|
|||||||
required this.macOSScanCode,
|
required this.macOSScanCode,
|
||||||
required this.iOSScanCode,
|
required this.iOSScanCode,
|
||||||
required this.chromiumCode,
|
required this.chromiumCode,
|
||||||
});
|
List<String>? otherWebCodes,
|
||||||
|
}) : otherWebCodes = otherWebCodes ?? <String>[];
|
||||||
|
|
||||||
/// Populates the key from a JSON map.
|
/// Populates the key from a JSON map.
|
||||||
factory PhysicalKeyEntry.fromJsonMapEntry(Map<String, dynamic> map) {
|
factory PhysicalKeyEntry.fromJsonMapEntry(Map<String, dynamic> map) {
|
||||||
@ -232,6 +240,7 @@ class PhysicalKeyEntry {
|
|||||||
windowsScanCode: scanCodes['windows'] as int?,
|
windowsScanCode: scanCodes['windows'] as int?,
|
||||||
macOSScanCode: scanCodes['macos'] as int?,
|
macOSScanCode: scanCodes['macos'] as int?,
|
||||||
iOSScanCode: scanCodes['ios'] as int?,
|
iOSScanCode: scanCodes['ios'] as int?,
|
||||||
|
otherWebCodes: (map['otherWebCodes'] as List<dynamic>?)?.cast<String>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,11 +267,14 @@ class PhysicalKeyEntry {
|
|||||||
final String name;
|
final String name;
|
||||||
/// The Chromium event code for the key.
|
/// The Chromium event code for the key.
|
||||||
final String? chromiumCode;
|
final String? chromiumCode;
|
||||||
|
/// Other codes used by Web besides chromiumCode.
|
||||||
|
final List<String> otherWebCodes;
|
||||||
|
|
||||||
Iterable<String> webCodes() sync* {
|
Iterable<String> webCodes() sync* {
|
||||||
if (chromiumCode != null) {
|
if (chromiumCode != null) {
|
||||||
yield chromiumCode!;
|
yield chromiumCode!;
|
||||||
}
|
}
|
||||||
|
yield* otherWebCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a JSON map from the key data.
|
/// Creates a JSON map from the key data.
|
||||||
@ -272,6 +284,7 @@ class PhysicalKeyEntry {
|
|||||||
'name': name,
|
'name': name,
|
||||||
'chromium': chromiumCode,
|
'chromium': chromiumCode,
|
||||||
},
|
},
|
||||||
|
'otherWebCodes': otherWebCodes,
|
||||||
'scanCodes': <String, dynamic>{
|
'scanCodes': <String, dynamic>{
|
||||||
'android': androidScanCodes,
|
'android': androidScanCodes,
|
||||||
'usb': usbHidCode,
|
'usb': usbHidCode,
|
||||||
@ -323,11 +336,14 @@ class PhysicalKeyEntry {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
final String otherWebStr = otherWebCodes.isEmpty
|
||||||
|
? ''
|
||||||
|
: ', otherWebCodes: [${otherWebCodes.join(', ')}]';
|
||||||
return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """
|
return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """
|
||||||
'linuxScanCode: ${toHex(evdevCode)}, xKbScanCode: ${toHex(xKbScanCode)}, '
|
'linuxScanCode: ${toHex(evdevCode)}, xKbScanCode: ${toHex(xKbScanCode)}, '
|
||||||
'windowsKeyCode: ${toHex(windowsScanCode)}, macOSScanCode: ${toHex(macOSScanCode)}, '
|
'windowsKeyCode: ${toHex(windowsScanCode)}, macOSScanCode: ${toHex(macOSScanCode)}, '
|
||||||
'windowsScanCode: ${toHex(windowsScanCode)}, chromiumSymbolName: $chromiumCode '
|
'windowsScanCode: ${toHex(windowsScanCode)}, chromiumSymbolName: $chromiumCode '
|
||||||
'iOSScanCode: ${toHex(iOSScanCode)})';
|
'iOSScanCode: ${toHex(iOSScanCode)})$otherWebStr';
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compareByUsbHidCode(PhysicalKeyEntry a, PhysicalKeyEntry b) =>
|
static int compareByUsbHidCode(PhysicalKeyEntry a, PhysicalKeyEntry b) =>
|
||||||
|
@ -30,7 +30,7 @@ constexpr uint64_t kPhysical${_toUpperCammel(entry.constantName)} = ${toHex(entr
|
|||||||
|
|
||||||
/// Gets the generated definitions of PhysicalKeyboardKeys.
|
/// Gets the generated definitions of PhysicalKeyboardKeys.
|
||||||
String get _logicalDefinitions {
|
String get _logicalDefinitions {
|
||||||
final OutputLines<int> lines = OutputLines<int>('Logical Key list');
|
final OutputLines<int> lines = OutputLines<int>('Logical Key list', behavior: DeduplicateBehavior.kSkip);
|
||||||
for (final LogicalKeyEntry entry in logicalData.entries) {
|
for (final LogicalKeyEntry entry in logicalData.entries) {
|
||||||
lines.add(entry.value, '''
|
lines.add(entry.value, '''
|
||||||
constexpr uint64_t kLogical${_toUpperCammel(entry.constantName)} = ${toHex(entry.value, digits: 11)};''');
|
constexpr uint64_t kLogical${_toUpperCammel(entry.constantName)} = ${toHex(entry.value, digits: 11)};''');
|
||||||
|
@ -42,7 +42,7 @@ class KeyCodesJavaGenerator extends BaseCodeGenerator {
|
|||||||
|
|
||||||
/// Gets the generated definitions of PhysicalKeyboardKeys.
|
/// Gets the generated definitions of PhysicalKeyboardKeys.
|
||||||
String get _logicalDefinitions {
|
String get _logicalDefinitions {
|
||||||
final OutputLines<int> lines = OutputLines<int>('Logical Key list');
|
final OutputLines<int> lines = OutputLines<int>('Logical Key list', behavior: DeduplicateBehavior.kSkip);
|
||||||
for (final LogicalKeyEntry entry in logicalData.entries) {
|
for (final LogicalKeyEntry entry in logicalData.entries) {
|
||||||
lines.add(entry.value, '''
|
lines.add(entry.value, '''
|
||||||
public static final long LOGICAL_${_toUpperSnake(entry.constantName)} = ${toHex(entry.value, digits: 11)}L;''');
|
public static final long LOGICAL_${_toUpperSnake(entry.constantName)} = ${toHex(entry.value, digits: 11)}L;''');
|
||||||
|
@ -233,12 +233,24 @@ void addNameValue(List<String> names, List<int> values, String name, int value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum DeduplicateBehavior {
|
||||||
|
// Warn the duplicate entry.
|
||||||
|
kWarn,
|
||||||
|
|
||||||
|
// Skip the latter duplicate entry.
|
||||||
|
kSkip,
|
||||||
|
|
||||||
|
// Keep all duplicate entries.
|
||||||
|
kKeep,
|
||||||
|
}
|
||||||
|
|
||||||
/// The information for a line used by [OutputLines].
|
/// The information for a line used by [OutputLines].
|
||||||
class OutputLine<T extends Comparable<Object>> {
|
class OutputLine<T extends Comparable<Object>> {
|
||||||
const OutputLine(this.key, this.value);
|
OutputLine(this.key, String value)
|
||||||
|
: values = <String>[value];
|
||||||
|
|
||||||
final T key;
|
final T key;
|
||||||
final String value;
|
final List<String> values;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A utility class to build join a number of lines in a sorted order.
|
/// A utility class to build join a number of lines in a sorted order.
|
||||||
@ -247,41 +259,43 @@ class OutputLine<T extends Comparable<Object>> {
|
|||||||
/// get the joined string of these lines joined sorting them in the order of the
|
/// get the joined string of these lines joined sorting them in the order of the
|
||||||
/// index.
|
/// index.
|
||||||
class OutputLines<T extends Comparable<Object>> {
|
class OutputLines<T extends Comparable<Object>> {
|
||||||
OutputLines(this.mapName, {this.checkDuplicate = true});
|
OutputLines(this.mapName, {this.behavior = DeduplicateBehavior.kWarn});
|
||||||
|
|
||||||
/// If true, then lines with duplicate keys will be warned and discarded.
|
/// What to do if there are entries with the same key.
|
||||||
///
|
final DeduplicateBehavior behavior;
|
||||||
/// Default to true.
|
|
||||||
final bool checkDuplicate;
|
|
||||||
|
|
||||||
/// The name for this map.
|
/// The name for this map.
|
||||||
///
|
///
|
||||||
/// Used in warning messages.
|
/// Used in warning messages.
|
||||||
final String mapName;
|
final String mapName;
|
||||||
|
|
||||||
final Set<T> keys = <T>{};
|
final Map<T, OutputLine<T>> lines = <T, OutputLine<T>>{};
|
||||||
final List<OutputLine<T>> lines = <OutputLine<T>>[];
|
|
||||||
|
|
||||||
void add(T code, String line) {
|
void add(T key, String line) {
|
||||||
if (checkDuplicate) {
|
final OutputLine<T>? existing = lines[key];
|
||||||
if (keys.contains(code)) {
|
if (existing != null) {
|
||||||
final OutputLine<T> existing = lines.firstWhere((OutputLine<T> line) => line.key == code);
|
switch (behavior) {
|
||||||
print('Warn: $mapName is requested to add line $code as:\n $line\n but it already exists as:\n ${existing.value}');
|
case DeduplicateBehavior.kWarn:
|
||||||
return;
|
print('Warn: Request to add $key to map "$mapName" as:\n $line\n but it already exists as:\n ${existing.values[0]}');
|
||||||
|
return;
|
||||||
|
case DeduplicateBehavior.kSkip:
|
||||||
|
return;
|
||||||
|
case DeduplicateBehavior.kKeep:
|
||||||
|
existing.values.add(line);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
keys.add(code);
|
|
||||||
}
|
}
|
||||||
lines.add(OutputLine<T>(code, line));
|
lines[key] = OutputLine<T>(key, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
String join() {
|
String join() {
|
||||||
return lines.map((OutputLine<T> line) => line.value).join('\n');
|
return lines.values.map((OutputLine<T> line) => line.values.join('\n')).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
String sortedJoin() {
|
String sortedJoin() {
|
||||||
return (lines.sublist(0)
|
return (lines.values.toList()
|
||||||
..sort((OutputLine<T> a, OutputLine<T> b) => a.key.compareTo(b.key)))
|
..sort((OutputLine<T> a, OutputLine<T> b) => a.key.compareTo(b.key)))
|
||||||
.map((OutputLine<T> line) => line.value)
|
.map((OutputLine<T> line) => line.values.join('\n'))
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2211,6 +2211,7 @@ const Map<String, LogicalKeyboardKey> kWebToLogicalKey = <String, LogicalKeyboar
|
|||||||
'EndCall': LogicalKeyboardKey.endCall,
|
'EndCall': LogicalKeyboardKey.endCall,
|
||||||
'Enter': LogicalKeyboardKey.enter,
|
'Enter': LogicalKeyboardKey.enter,
|
||||||
'EraseEof': LogicalKeyboardKey.eraseEof,
|
'EraseEof': LogicalKeyboardKey.eraseEof,
|
||||||
|
'Esc': LogicalKeyboardKey.escape,
|
||||||
'Escape': LogicalKeyboardKey.escape,
|
'Escape': LogicalKeyboardKey.escape,
|
||||||
'ExSel': LogicalKeyboardKey.exSel,
|
'ExSel': LogicalKeyboardKey.exSel,
|
||||||
'Execute': LogicalKeyboardKey.execute,
|
'Execute': LogicalKeyboardKey.execute,
|
||||||
@ -2495,6 +2496,7 @@ const Map<String, PhysicalKeyboardKey> kWebToPhysicalKey = <String, PhysicalKeyb
|
|||||||
'Enter': PhysicalKeyboardKey.enter,
|
'Enter': PhysicalKeyboardKey.enter,
|
||||||
'Equal': PhysicalKeyboardKey.equal,
|
'Equal': PhysicalKeyboardKey.equal,
|
||||||
'Escape': PhysicalKeyboardKey.escape,
|
'Escape': PhysicalKeyboardKey.escape,
|
||||||
|
'Esc': PhysicalKeyboardKey.escape,
|
||||||
'F1': PhysicalKeyboardKey.f1,
|
'F1': PhysicalKeyboardKey.f1,
|
||||||
'F10': PhysicalKeyboardKey.f10,
|
'F10': PhysicalKeyboardKey.f10,
|
||||||
'F11': PhysicalKeyboardKey.f11,
|
'F11': PhysicalKeyboardKey.f11,
|
||||||
|
@ -2680,6 +2680,23 @@ void main() {
|
|||||||
expect(data.keyCode, equals(0x10));
|
expect(data.keyCode, equals(0x10));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Esc keys generated by older browsers are correctly translated', () {
|
||||||
|
final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const <String, Object?>{
|
||||||
|
'type': 'keydown',
|
||||||
|
'keymap': 'web',
|
||||||
|
'code': 'Esc',
|
||||||
|
'key': 'Esc',
|
||||||
|
'location': 0,
|
||||||
|
'metaState': 0x0,
|
||||||
|
'keyCode': 0x1B,
|
||||||
|
});
|
||||||
|
final RawKeyEventDataWeb data = escapeKeyEvent.data as RawKeyEventDataWeb;
|
||||||
|
expect(data.physicalKey, equals(PhysicalKeyboardKey.escape));
|
||||||
|
expect(data.logicalKey, equals(LogicalKeyboardKey.escape));
|
||||||
|
expect(data.keyLabel, isEmpty);
|
||||||
|
expect(data.keyCode, equals(0x1B));
|
||||||
|
});
|
||||||
|
|
||||||
test('Arrow keys from a keyboard give correct physical key mappings', () {
|
test('Arrow keys from a keyboard give correct physical key mappings', () {
|
||||||
final RawKeyEvent arrowKeyDown = RawKeyEvent.fromMessage(const <String, Object?>{
|
final RawKeyEvent arrowKeyDown = RawKeyEvent.fromMessage(const <String, Object?>{
|
||||||
'type': 'keydown',
|
'type': 'keydown',
|
||||||
|
Loading…
Reference in New Issue
Block a user