Unverified Commit dbcac7f1 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Reland Make LogicalKeyboardKey.keyLabel a getter and better #79100 (#79302)

parent 1cdb6c80
......@@ -76,9 +76,9 @@ abstract class KeyboardKey with Diagnosticable {
/// _message = 'Pressed the "Q" key!';
/// } else {
/// if (kReleaseMode) {
/// _message = 'Not a Q: Key label is "${event.logicalKey.keyLabel}"';
/// _message = 'Not a Q: Pressed 0x${event.logicalKey.keyId.toRadixString(16)}';
/// } else {
/// // This will only print useful information in debug mode.
/// // The debugName will only print useful information in debug mode.
/// _message = 'Not a Q: Pressed ${event.logicalKey.debugName}';
/// }
/// }
......@@ -124,21 +124,8 @@ abstract class KeyboardKey with Diagnosticable {
/// keyboard events.
@immutable
class LogicalKeyboardKey extends KeyboardKey {
/// Creates a LogicalKeyboardKey object with an optional key label and debug
/// name.
///
/// [keyId] must not be null.
///
/// {@tool snippet}
/// To save executable size, it is recommended that the [debugName] be null in
/// release mode. You can do this by using the [kReleaseMode] constant.
///
/// ```dart
/// const LogicalKeyboardKey(0x0010000000a, debugName: kReleaseMode ? null : 'Special Key')
/// ```
/// {@end-tool}
const LogicalKeyboardKey(this.keyId, {this.debugName, this.keyLabel = ''})
: assert(keyId != null);
/// Creates a new LogicalKeyboardKey object for a key ID.
const LogicalKeyboardKey(this.keyId);
/// A unique code representing this key.
///
......@@ -146,35 +133,106 @@ class LogicalKeyboardKey extends KeyboardKey {
/// from it, as the representation of the code could change at any time.
final int keyId;
/// The debug string to print for this keyboard key, which will be null in
/// release mode.
final String? debugName;
// Returns the bits that are not included in [valueMask], shifted to the
// right.
//
// For example, if the input is 0x12abcdabcd, then the result is 0x12.
//
// This is mostly equivalent to a right shift, resolving the problem that
// JavaScript only support 32-bit bitwise operation and needs to use division
// instead.
static int _nonValueBits(int n) {
// `n >> valueMaskWidth` is equivalent to `n / divisorForValueMask`.
const int divisorForValueMask = valueMask + 1;
const int valueMaskWidth = 32;
// Equivalent to assert(divisorForValueMask == (1 << valueMaskWidth)).
const int _firstDivisorWidth = 28;
assert(divisorForValueMask ==
(1 << _firstDivisorWidth) * (1 << (valueMaskWidth - _firstDivisorWidth)));
/// The Unicode string representing the character produced by a [RawKeyEvent].
// JS only supports up to 2^53 - 1, therefore non-value bits can only
// contain (maxSafeIntegerWidth - valueMaskWidth) bits.
const int maxSafeIntegerWidth = 52;
const int nonValueMask = (1 << (maxSafeIntegerWidth - valueMaskWidth)) - 1;
if (kIsWeb) {
return (n / divisorForValueMask).floor() & nonValueMask;
} else {
return (n >> valueMaskWidth) & nonValueMask;
}
}
static String? _unicodeKeyLabel(int keyId) {
if (_nonValueBits(keyId) == 0) {
return String.fromCharCode(keyId).toUpperCase();
}
return null;
}
/// A description representing the character produced by a [RawKeyEvent].
///
/// This value is useful for providing readable strings for keys or keyboard
/// shortcuts. Do not use this value to compare equality of keys; compare
/// [keyId] instead.
///
/// This value is useful for describing or matching mnemonic keyboard
/// shortcuts.
/// For printable keys, this is usually the printable character in upper case
/// ignoring modifiers or combining keys, such as 'A', '1', or '/'. This
/// might also return accented letters (such as 'Ù') for keys labeled as so,
/// but not if such character is a result from preceding combining keys ('`̀'
/// followed by key U).
///
/// This value is an empty string if there's no key label data for a key.
/// For other keys, [keyLabel] looks up the full key name from a predefined
/// map, such as 'F1', 'Shift Left', or 'Media Down'. This value is an empty
/// string if there's no key label data for a key.
///
/// On most platforms this is a single code point, but it could contain any
/// Unicode string. The `keyLabel` differs from [RawKeyEvent.character]
/// because `keyLabel` only takes into account the key being pressed, not any
/// combining keys pressed before it, so, for example, an “o” that follows a
/// combining dieresis (“¨”, COMBINING DIAERESIS (U+0308)) would just return
/// “o” for [keyLabel], but would return “ö” for [RawKeyEvent.character].
/// For the printable representation that takes into consideration the
/// modifiers and combining keys, see [RawKeyEvent.character].
///
/// {@macro flutter.services.RawKeyEventData.keyLabel}
final String keyLabel;
String get keyLabel {
return _unicodeKeyLabel(keyId)
?? _keyLabels[keyId]
?? '';
}
/// The debug string to print for this keyboard key, which will be null in
/// release mode.
///
/// For printable keys, this is usually a more descriptive name related to
/// [keyLabel], such as 'Key A', 'Digit 1', 'Backslash'. This might
/// also return accented letters (such as 'Key Ù') for keys labeled as so.
///
/// For other keys, this looks up the full key name from a predefined map (the
/// same value as [keyLabel]), such as 'F1', 'Shift Left', or 'Media Down'. If
/// there's no key label data for a key, this returns a name that explains the
/// ID (such as 'Key with ID 0x00100012345').
String? get debugName {
String? result;
assert(() {
result = _keyLabels[keyId];
if (result == null) {
final String? unicodeKeyLabel = _unicodeKeyLabel(keyId);
if (unicodeKeyLabel != null) {
result = 'Key $unicodeKeyLabel';
} else {
result = 'Key with ID 0x${keyId.toRadixString(16).padLeft(11, '0')}';
}
}
return true;
}());
return result;
}
@override
int get hashCode => keyId.hashCode;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
}
return other is LogicalKeyboardKey
&& other.keyId == keyId;
}
......@@ -313,6 +371,9 @@ class LogicalKeyboardKey extends KeyboardKey {
// A map of keys to the pseudo-key synonym for that key. Used by getSynonyms.
static final Map<LogicalKeyboardKey, LogicalKeyboardKey> _synonyms = <LogicalKeyboardKey, LogicalKeyboardKey>{
@@@LOGICAL_KEY_SYNONYMS@@@ };
static const Map<int, String> _keyLabels = <int, String>{
@@@LOGICAL_KEY_KEY_LABELS@@@ };
}
/// A class with static values that describe the keys that are returned from
......@@ -387,7 +448,7 @@ class LogicalKeyboardKey extends KeyboardKey {
/// onTap: () {
/// FocusScope.of(context).requestFocus(_focusNode);
/// },
/// child: Text('Tap to focus'),
/// child: const Text('Tap to focus'),
/// );
/// }
/// return Text(_message ?? 'Press a key');
......@@ -408,20 +469,8 @@ class LogicalKeyboardKey extends KeyboardKey {
/// keyboard events.
@immutable
class PhysicalKeyboardKey extends KeyboardKey {
/// Creates a PhysicalKeyboardKey object with an optional debug name.
///
/// The [usbHidUsage] must not be null.
///
/// {@tool snippet}
/// To save executable size, it is recommended that the [debugName] be null in
/// release mode. You can do this using the [kReleaseMode] constant.
///
/// ```dart
/// const PhysicalKeyboardKey(0x0000ffff, debugName: kReleaseMode ? null : 'Special Key')
/// ```
/// {@end-tool}
const PhysicalKeyboardKey(this.usbHidUsage, {this.debugName})
: assert(usbHidUsage != null);
/// Creates a new PhysicalKeyboardKey object for a USB HID usage.
const PhysicalKeyboardKey(this.usbHidUsage);
/// The unique USB HID usage ID of this physical key on the keyboard.
///
......@@ -435,24 +484,33 @@ class PhysicalKeyboardKey extends KeyboardKey {
/// The debug string to print for this keyboard key, which will be null in
/// release mode.
final String? debugName;
/// Finds a known [PhysicalKeyboardKey] that matches the given USB HID usage
/// code.
static PhysicalKeyboardKey? findKeyByCode(int usageCode) => _knownPhysicalKeys[usageCode];
String? get debugName {
String? result;
assert(() {
result = _debugNames[usbHidUsage] ??
'Key with ID 0x${usbHidUsage.toRadixString(16).padLeft(8, '0')}';
return true;
}());
return result;
}
@override
int get hashCode => usbHidUsage.hashCode;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
}
return other is PhysicalKeyboardKey
&& other.usbHidUsage == usbHidUsage;
}
/// Finds a known [PhysicalKeyboardKey] that matches the given USB HID usage
/// code.
static PhysicalKeyboardKey? findKeyByCode(int usageCode) => _knownPhysicalKeys[usageCode];
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
......@@ -468,4 +526,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
static const Map<int, PhysicalKeyboardKey> _knownPhysicalKeys = <int, PhysicalKeyboardKey>{
@@@PHYSICAL_KEY_MAP@@@
};
static const Map<int, String> _debugNames = kReleaseMode ?
<int, String>{} :
<int, String>{
@@@PHYSICAL_KEY_DEBUG_NAMES@@@ };
}
......@@ -45,38 +45,38 @@ class KeyboardKeysCodeGenerator extends BaseCodeGenerator {
definitions.write('''
$firstComment ///
$otherComments static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeyboardKey(${toHex(entry.usbHidCode, digits: 8)}, debugName: kReleaseMode ? null : '${entry.commentName}');
$otherComments static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeyboardKey(${toHex(entry.usbHidCode, digits: 8)});
''');
}
return definitions.toString();
}
String get _physicalDebugNames {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
result.write('''
${toHex(entry.usbHidCode, digits: 8)}: '${entry.commentName}',
''');
}
return result.toString();
}
/// Gets the generated definitions of LogicalKeyboardKeys.
String get _logicalDefinitions {
String escapeLabel(String label) => label.contains("'") ? 'r"$label"' : "r'$label'";
final StringBuffer definitions = StringBuffer();
void printKey(int flutterId, String keyLabel, 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.');
otherComments ??= _wrapString('See the function [RawKeyEvent.logicalKey] for more information.');
if (keyLabel == null) {
definitions.write('''
$firstComment ///
$otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardKey(${toHex(flutterId, digits: 11)}, debugName: kReleaseMode ? null : '$commentName');
$otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardKey(${toHex(flutterId, digits: 11)});
''');
} else {
definitions.write('''
$firstComment ///
$otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardKey(${toHex(flutterId, digits: 11)}, keyLabel: ${escapeLabel(keyLabel)}, debugName: kReleaseMode ? null : '$commentName');
''');
}
}
for (final Key entry in keyData.data) {
printKey(
entry.flutterId,
entry.keyLabel,
entry.constantName,
entry.commentName,
);
......@@ -89,7 +89,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
final Set<String> unionNames = Key.synonyms[name].map<String>((dynamic name) {
return upperCamelToLowerCamel(name as String);
}).toSet();
printKey(Key.synonymPlane | entry.flutterId, entry.keyLabel, name, Key.getCommentName(name),
printKey(Key.synonymPlane | entry.flutterId, name, Key.getCommentName(name),
otherComments: _wrapString('This key represents the union of the keys '
'$unionNames when comparing keys. This key will never be generated '
'directly, its main use is in defining key maps.'));
......@@ -108,6 +108,25 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
return synonyms.toString();
}
String get _logicalKeyLabels {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
result.write('''
${toHex(entry.flutterId, digits: 11)}: '${entry.commentName}',
''');
}
for (final String name in Key.synonyms.keys) {
// Use the first item in the synonyms as a template for the ID to use.
// It won't end up being the same value because it'll be in the pseudo-key
// plane.
final Key entry = keyData.data.firstWhere((Key item) => item.name == Key.synonyms[name][0]);
result.write('''
${toHex(Key.synonymPlane | entry.flutterId, digits: 11)}: '${Key.getCommentName(name)}',
''');
}
return result.toString();
}
/// This generates the map of USB HID codes to physical keys.
String get _predefinedHidCodeMap {
final StringBuffer scanCodeMap = StringBuffer();
......@@ -146,7 +165,9 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
'LOGICAL_KEY_MAP': _predefinedKeyCodeMap,
'LOGICAL_KEY_DEFINITIONS': _logicalDefinitions,
'LOGICAL_KEY_SYNONYMS': _logicalSynonyms,
'LOGICAL_KEY_KEY_LABELS': _logicalKeyLabels,
'PHYSICAL_KEY_DEFINITIONS': _physicalDefinitions,
'PHYSICAL_KEY_DEBUG_NAMES': _physicalDebugNames,
};
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -194,11 +194,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
if (keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) {
final int combinedCodePoint = plainCodePoint & _kCombiningCharacterMask;
final int keyId = LogicalKeyboardKey.unicodePlane | (combinedCodePoint & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Look to see if the keyCode is one we know about and have a mapping for.
......@@ -210,10 +206,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
// 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 androidKeyIdPlane = 0x00200000000;
newKey ??= LogicalKeyboardKey(
androidKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown Android key code $keyCode',
);
newKey ??= LogicalKeyboardKey(androidKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
return newKey;
}
......
......@@ -68,11 +68,8 @@ class RawKeyEventDataFuchsia extends RawKeyEventData {
// If the key has a printable representation, then make a logical key based
// on that.
if (codePoint != 0) {
return LogicalKeyboardKey(
LogicalKeyboardKey.unicodePlane | codePoint & LogicalKeyboardKey.valueMask,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key $keyLabel',
);
final int flutterId = LogicalKeyboardKey.unicodePlane | codePoint & LogicalKeyboardKey.valueMask;
return kFuchsiaToLogicalKey[flutterId] ?? LogicalKeyboardKey(LogicalKeyboardKey.unicodePlane | codePoint & LogicalKeyboardKey.valueMask);
}
// Look to see if the hidUsage is one we know about and have a mapping for.
......@@ -84,10 +81,7 @@ class RawKeyEventDataFuchsia extends RawKeyEventData {
// 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 fuchsiaKeyIdPlane = 0x00300000000;
newKey ??= LogicalKeyboardKey(
fuchsiaKeyIdPlane | hidUsage | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Ephemeral Fuchsia key code $hidUsage',
);
newKey ??= LogicalKeyboardKey(fuchsiaKeyIdPlane | hidUsage | LogicalKeyboardKey.autogeneratedMask);
return newKey;
}
......
......@@ -92,7 +92,6 @@ class RawKeyEventDataIos extends RawKeyEventData {
@override
PhysicalKeyboardKey get physicalKey => kIosToPhysicalKey[keyCode] ?? PhysicalKeyboardKey.none;
@override
LogicalKeyboardKey get logicalKey {
// Look to see if the keyCode is a printable number pad key, so that a
......@@ -126,11 +125,7 @@ class RawKeyEventDataIos extends RawKeyEventData {
}
final int keyId = LogicalKeyboardKey.unicodePlane | (codeUnit & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Control keys like "backspace" and movement keys like arrow keys don't
......@@ -141,21 +136,14 @@ class RawKeyEventDataIos extends RawKeyEventData {
// physical key map.
if (physicalKey != PhysicalKeyboardKey.none) {
final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane;
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: physicalKey.debugName ?? '',
debugName: physicalKey.debugName,
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// This is a non-printable key that is unrecognized, so a new code is minted
// with the autogenerated bit set.
const int iosKeyIdPlane = 0x00400000000;
return LogicalKeyboardKey(
iosKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown iOS key code $keyCode',
);
return LogicalKeyboardKey(iosKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
......
......@@ -92,11 +92,7 @@ class RawKeyEventDataLinux extends RawKeyEventData {
if (keyLabel.isNotEmpty &&
!LogicalKeyboardKey.isControlCharacter(keyLabel)) {
final int keyId = LogicalKeyboardKey.unicodePlane | (unicodeScalarValues & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Look to see if the keyCode is one we know about and have a mapping for.
......@@ -109,10 +105,7 @@ class RawKeyEventDataLinux extends RawKeyEventData {
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
newKey ??= LogicalKeyboardKey(
linuxKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown key code $keyCode',
);
newKey ??= LogicalKeyboardKey(linuxKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
return newKey;
}
......
......@@ -95,11 +95,7 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
}
final int keyId = LogicalKeyboardKey.unicodePlane | (codeUnit & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Control keys like "backspace" and movement keys like arrow keys don't
......@@ -110,21 +106,14 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
// physical key map.
if (physicalKey != PhysicalKeyboardKey.none) {
final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane;
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: physicalKey.debugName ?? '',
debugName: physicalKey.debugName,
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// This is a non-printable key that is unrecognized, so a new code is minted
// with the autogenerated bit set.
const int macOsKeyIdPlane = 0x00500000000;
return LogicalKeyboardKey(
macOsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown macOS key code $keyCode',
);
return LogicalKeyboardKey(macOsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
......
......@@ -82,10 +82,7 @@ class RawKeyEventDataWeb extends RawKeyEventData {
// 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"',
);
return LogicalKeyboardKey(webKeyIdPlane | code.hashCode | LogicalKeyboardKey.autogeneratedMask);
}
@override
......
......@@ -3,8 +3,6 @@
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'keyboard_maps.dart';
import 'raw_keyboard.dart';
......@@ -80,11 +78,7 @@ class RawKeyEventDataWindows extends RawKeyEventData {
// plane.
if (keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) {
final int keyId = LogicalKeyboardKey.unicodePlane | (characterCodePoint & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Look to see if the keyCode is one we know about and have a mapping for.
LogicalKeyboardKey? newKey = kWindowsToLogicalKey[keyCode];
......@@ -95,10 +89,7 @@ class RawKeyEventDataWindows extends RawKeyEventData {
// 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 windowsKeyIdPlane = 0x00700000000;
newKey ??= LogicalKeyboardKey(
windowsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown Windows key code $keyCode',
);
newKey ??= LogicalKeyboardKey(windowsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
return newKey;
}
......
......@@ -15,11 +15,15 @@ void main() {
// Check a modifier key
expect(PhysicalKeyboardKey.findKeyByCode(0x000700e1), equals(PhysicalKeyboardKey.shiftLeft));
});
test('Equality is only based on HID code.', () async {
const PhysicalKeyboardKey key1 = PhysicalKeyboardKey(0x01, debugName: 'key1');
const PhysicalKeyboardKey key2 = PhysicalKeyboardKey(0x01, debugName: 'key2');
expect(key1, equals(key1));
expect(key1, equals(key2));
test('Values are equal', () async {
expect(PhysicalKeyboardKey.keyA == PhysicalKeyboardKey(PhysicalKeyboardKey.keyA.usbHidUsage), true);
// ignore: prefer_const_constructors, intentionally test if a const key is equal to a non-const key
expect(const PhysicalKeyboardKey(0x12345) == PhysicalKeyboardKey(0x12345), true);
});
test('debugNames', () async {
expect(PhysicalKeyboardKey.keyA.debugName, 'Key A');
expect(PhysicalKeyboardKey.backslash.debugName, 'Backslash');
expect(const PhysicalKeyboardKey(0x12345).debugName, 'Key with ID 0x00012345');
});
});
group(LogicalKeyboardKey, () {
......@@ -44,12 +48,6 @@ void main() {
expect(LogicalKeyboardKey.isControlCharacter('~'), isFalse);
expect(LogicalKeyboardKey.isControlCharacter('\xa0'), isFalse); // NO-BREAK SPACE
});
test('Equality is only based on ID.', () async {
const LogicalKeyboardKey key1 = LogicalKeyboardKey(0x01, keyLabel: 'label1', debugName: 'key1');
const LogicalKeyboardKey key2 = LogicalKeyboardKey(0x01, keyLabel: 'label2', debugName: 'key2');
expect(key1, equals(key1));
expect(key1, equals(key2));
});
test('Basic synonyms can be looked up.', () async {
expect(LogicalKeyboardKey.shiftLeft.synonyms.first, equals(LogicalKeyboardKey.shift));
expect(LogicalKeyboardKey.controlLeft.synonyms.first, equals(LogicalKeyboardKey.control));
......@@ -106,5 +104,30 @@ void main() {
LogicalKeyboardKey.meta,
}));
});
test('Values are equal', () async {
expect(LogicalKeyboardKey.keyA == LogicalKeyboardKey(LogicalKeyboardKey.keyA.keyId), true);
// ignore: prefer_const_constructors, intentionally test if a const key is equal to a non-const key
expect(const PhysicalKeyboardKey(0x12345) == PhysicalKeyboardKey(0x12345), true);
});
test('keyLabel', () async {
expect(LogicalKeyboardKey.keyA.keyLabel, 'A');
expect(LogicalKeyboardKey.backslash.keyLabel, r'\');
expect(const LogicalKeyboardKey(0xD9).keyLabel, 'Ù');
expect(const LogicalKeyboardKey(0xF9).keyLabel, 'Ù');
expect(LogicalKeyboardKey.shiftLeft.keyLabel, 'Shift Left');
expect(LogicalKeyboardKey.numpadDecimal.keyLabel, 'Numpad Decimal');
expect(LogicalKeyboardKey.numpad1.keyLabel, 'Numpad 1');
expect(LogicalKeyboardKey.delete.keyLabel, 'Delete');
expect(LogicalKeyboardKey.f12.keyLabel, 'F12');
expect(LogicalKeyboardKey.mediaPlay.keyLabel, 'Media Play');
expect(const LogicalKeyboardKey(0x100012345).keyLabel, '');
});
test('debugName', () async {
expect(LogicalKeyboardKey.keyA.debugName, 'Key A');
expect(LogicalKeyboardKey.backslash.debugName, 'Backslash');
expect(const LogicalKeyboardKey(0xD9).debugName, 'Key Ù');
expect(LogicalKeyboardKey.mediaPlay.debugName, 'Media Play');
expect(const LogicalKeyboardKey(0x100012345).debugName, 'Key with ID 0x00100012345');
});
});
}
......@@ -1178,7 +1178,6 @@ void main() {
final RawKeyEventDataMacOs data = leftArrowKey.data as RawKeyEventDataMacOs;
expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.arrowLeft));
expect(data.logicalKey.keyLabel, isEmpty);
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/35347
}, skip: isBrowser); // This is a macOS-specific group.
......@@ -1324,7 +1323,6 @@ void main() {
final RawKeyEventDataIos data = leftArrowKey.data as RawKeyEventDataIos;
expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.arrowLeft));
expect(data.logicalKey.keyLabel, isEmpty);
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/35347
}, skip: isBrowser); // This is an iOS-specific group.
......@@ -1469,7 +1467,6 @@ void main() {
final RawKeyEventDataWindows data = leftArrowKey.data as RawKeyEventDataWindows;
expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.arrowLeft));
expect(data.logicalKey.keyLabel, isEmpty);
});
testWidgets('Win32 VK_PROCESSKEY events are skipped', (WidgetTester tester) async {
const String platform = 'windows';
......
......@@ -13,6 +13,13 @@ import 'test_async_utils.dart';
// https://github.com/flutter/flutter/issues/33521
// This code can only simulate keys which appear in the key maps.
String _keyLabel(LogicalKeyboardKey key) {
final String keyLabel = key.keyLabel;
if (keyLabel.length == 1)
return keyLabel.toLowerCase();
return '';
}
/// A class that serves as a namespace for a bunch of keyboard-key generation
/// utilities.
class KeyEventSimulator {
......@@ -206,7 +213,7 @@ class KeyEventSimulator {
if (kIsWeb) {
result['code'] = _getWebKeyCode(key);
result['key'] = key.keyLabel;
result['key'] = _keyLabel(key);
result['metaState'] = _getWebModifierFlags(key, isDown);
return result;
}
......@@ -214,17 +221,17 @@ class KeyEventSimulator {
switch (platform) {
case 'android':
result['keyCode'] = keyCode;
if (key.keyLabel.isNotEmpty) {
result['codePoint'] = key.keyLabel.codeUnitAt(0);
result['character'] = key.keyLabel;
if (_keyLabel(key).isNotEmpty) {
result['codePoint'] = _keyLabel(key).codeUnitAt(0);
result['character'] = _keyLabel(key);
}
result['scanCode'] = scanCode;
result['metaState'] = _getAndroidModifierFlags(key, isDown);
break;
case 'fuchsia':
result['hidUsage'] = physicalKey.usbHidUsage;
if (key.keyLabel.isNotEmpty) {
result['codePoint'] = key.keyLabel.codeUnitAt(0);
if (_keyLabel(key).isNotEmpty) {
result['codePoint'] = _keyLabel(key).codeUnitAt(0);
}
result['modifiers'] = _getFuchsiaModifierFlags(key, isDown);
break;
......@@ -233,32 +240,32 @@ class KeyEventSimulator {
result['keyCode'] = keyCode;
result['scanCode'] = scanCode;
result['modifiers'] = _getGlfwModifierFlags(key, isDown);
result['unicodeScalarValues'] = key.keyLabel.isNotEmpty ? key.keyLabel.codeUnitAt(0) : 0;
result['unicodeScalarValues'] = _keyLabel(key).isNotEmpty ? _keyLabel(key).codeUnitAt(0) : 0;
break;
case 'macos':
result['keyCode'] = scanCode;
if (key.keyLabel.isNotEmpty) {
result['characters'] = key.keyLabel;
result['charactersIgnoringModifiers'] = key.keyLabel;
if (_keyLabel(key).isNotEmpty) {
result['characters'] = _keyLabel(key);
result['charactersIgnoringModifiers'] = _keyLabel(key);
}
result['modifiers'] = _getMacOsModifierFlags(key, isDown);
break;
case 'ios':
result['keyCode'] = scanCode;
result['characters'] = key.keyLabel;
result['charactersIgnoringModifiers'] = key.keyLabel;
result['characters'] = _keyLabel(key);
result['charactersIgnoringModifiers'] = _keyLabel(key);
result['modifiers'] = _getIOSModifierFlags(key, isDown);
break;
case 'web':
result['code'] = _getWebKeyCode(key);
result['key'] = key.keyLabel;
result['key'] = _keyLabel(key);
result['metaState'] = _getWebModifierFlags(key, isDown);
break;
case 'windows':
result['keyCode'] = keyCode;
result['scanCode'] = scanCode;
if (key.keyLabel.isNotEmpty) {
result['characterCodePoint'] = key.keyLabel.codeUnitAt(0);
if (_keyLabel(key).isNotEmpty) {
result['characterCodePoint'] = _keyLabel(key).codeUnitAt(0);
}
result['modifiers'] = _getWindowsModifierFlags(key, isDown);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment