From 833b4f1ab74580ba26cf35f9f5b5c398729cc9cb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 11 Aug 2021 13:55:35 -0700 Subject: [PATCH] ui.KeyData.toString (#28007) Enhances ui.KeyData.toString by: - Fixing the character interpretation; - Adding hex representation to characters; - Applying the code to the web engine which is currently missing. --- lib/ui/key.dart | 55 ++++++++++++++------ lib/web_ui/lib/src/ui/key.dart | 55 +++++++++++++++++++- lib/web_ui/test/keyboard_converter_test.dart | 29 +++++++++++ 3 files changed, 122 insertions(+), 17 deletions(-) diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 50e07703fb97..f3ced0774ac2 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -112,21 +112,36 @@ class KeyData { } String _logicalToString() { - String result = '0x${logical.toRadixString(16)}'; - final int nonValueBits = _nonValueBits(logical); - if (nonValueBits & 0x0FF == 0x000) { - result += '(Unicode)'; - } - if (nonValueBits & 0x0FF == 0x001) { - result += '(HID)'; - } - if (nonValueBits & 0x100 != 0x000) { - result += '(auto)'; - } - if (nonValueBits & 0x200 != 0x000) { - result += '(synonym)'; - } - return result; + final String result = '0x${logical.toRadixString(16)}'; + final int planeNum = _nonValueBits(logical) & 0x0FF; + final String planeDescription = (() { + switch (planeNum) { + case 0x000: + return ' (Unicode)'; + case 0x001: + return ' (Unprintable)'; + case 0x002: + return ' (Flutter)'; + case 0x011: + return ' (Android)'; + case 0x012: + return ' (Fuchsia)'; + case 0x013: + return ' (iOS)'; + case 0x014: + return ' (macOS)'; + case 0x015: + return ' (GTK)'; + case 0x016: + return ' (Windows)'; + case 0x017: + return ' (Web)'; + case 0x018: + return ' (GLFW)'; + } + return ''; + })(); + return '$result$planeDescription'; } String? _escapeCharacter() { @@ -149,9 +164,17 @@ class KeyData { } } + String? _quotedCharCode() { + if (character == null) + return ''; + final Iterable hexChars = character!.codeUnits + .map((int code) => code.toRadixString(16).padLeft(2, '0')); + return ' (0x${hexChars.join(' ')})'; + } + @override String toString() => 'KeyData(key ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' - 'logical: ${_logicalToString()}, character: ${_escapeCharacter()})'; + 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${_quotedCharCode()}${synthesized ? ', synthesized' : ''})'; /// Returns a complete textual description of the information in this object. String toStringFull() { diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart index 1484ccb548e3..b1665526298b 100644 --- a/lib/web_ui/lib/src/ui/key.dart +++ b/lib/web_ui/lib/src/ui/key.dart @@ -78,9 +78,62 @@ class KeyData { /// [KeyRepeatEvent] is never synthesized. final bool synthesized; + String _logicalToString() { + final String result = '0x${logical.toRadixString(16)}'; + // Find the bits that are not included in `valueMask`, shifted to the right. + // For example, if [logical] 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 operations and needs to use + // division instead. + final int planeNum = (logical / 0x100000000).floor(); + final String planeDescription = (() { + switch (planeNum) { + case 0x000: + return ' (Unicode)'; + case 0x001: + return ' (Unprintable)'; + case 0x002: + return ' (Flutter)'; + case 0x017: + return ' (Web)'; + } + return ''; + })(); + return '$result$planeDescription'; + } + + String? _escapeCharacter() { + if (character == null) { + return character ?? ''; + } + switch (character!) { + case '\n': + return r'"\n"'; + case '\t': + return r'"\t"'; + case '\r': + return r'"\r"'; + case '\b': + return r'"\b"'; + case '\f': + return r'"\f"'; + default: + return '"$character"'; + } + } + + String? _quotedCharCode() { + if (character == null) + return ''; + final Iterable hexChars = character!.codeUnits + .map((int code) => code.toRadixString(16).padLeft(2, '0')); + return ' (0x${hexChars.join(' ')})'; + } + @override String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' - 'logical: 0x${logical.toRadixString(16)}, character: $character)'; + 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${_quotedCharCode()}${synthesized ? ', synthesized' : ''})'; /// Returns a complete textual description of the information in this object. String toStringFull() { diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart index b788bfc09bba..6183097f65a0 100644 --- a/lib/web_ui/test/keyboard_converter_test.dart +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -43,6 +43,35 @@ void main() { } void testMain() { + test('KeyData.toString', () { + expect(const ui.KeyData( + type: ui.KeyEventType.down, + physical: 0x700e5, + logical: 0x61, + character: 'A', + timeStamp: Duration.zero, + synthesized: false, + ).toString(), 'KeyData(type: down, physical: 0x700e5, logical: 0x61 (Unicode), character: "A" (0x41))'); + + expect(const ui.KeyData( + type: ui.KeyEventType.up, + physical: 0x700e6, + logical: 0x100000061, + character: '\n', + timeStamp: Duration.zero, + synthesized: true, + ).toString(), r'KeyData(type: up, physical: 0x700e6, logical: 0x100000061 (Unprintable), character: "\n" (0x0a), synthesized)'); + + expect(const ui.KeyData( + type: ui.KeyEventType.repeat, + physical: 0x700e7, + logical: 0x9900000071, + character: null, + timeStamp: Duration.zero, + synthesized: false, + ).toString(), 'KeyData(type: repeat, physical: 0x700e7, logical: 0x9900000071, character: )'); + }); + test('Single key press, repeat, and release', () { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) {