From 59644674a5cdd47610dde7b69edf9686a20e4a09 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 13 Jan 2021 11:43:51 -0800 Subject: [PATCH 1/2] Implement handling of framework-handled key combinations --- lib/web_ui/lib/src/engine/keyboard.dart | 27 +++++++-------- lib/web_ui/test/keyboard_test.dart | 46 ++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 8779e095d552d..6cb52acad39ea 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -80,11 +80,6 @@ class Keyboard { } final html.KeyboardEvent keyboardEvent = event; - - if (_shouldPreventDefault(event)) { - event.preventDefault(); - } - final String timerKey = keyboardEvent.code!; // Don't handle synthesizing a keyup event for modifier keys @@ -132,16 +127,18 @@ class Keyboard { }; EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', - _messageCodec.encodeMessage(eventData), _noopCallback); - } - - bool _shouldPreventDefault(html.KeyboardEvent event) { - switch (event.key) { - case 'Tab': - return true; - default: - return false; - } + _messageCodec.encodeMessage(eventData), (ByteData? data) { + if (data == null) { + return; + } + final String response = utf8.decode(data.buffer.asUint8List()); + final Map jsonResponse = json.decode(response); + if (jsonResponse['handled'] as bool) { + // If the framework handled it, then don't propagate it any further. + event.preventDefault(); + } + }, + ); } void _synthesizeKeyup(html.KeyboardEvent event) { diff --git a/lib/web_ui/test/keyboard_test.dart b/lib/web_ui/test/keyboard_test.dart index c16fe89467b03..c91be7610cfd5 100644 --- a/lib/web_ui/test/keyboard_test.dart +++ b/lib/web_ui/test/keyboard_test.dart @@ -6,6 +6,7 @@ import 'dart:html' as html; import 'dart:js_util' as js_util; import 'dart:typed_data'; +import 'dart:convert' hide Codec; import 'package:quiver/testing/async.dart'; import 'package:test/bootstrap/browser.dart'; @@ -17,6 +18,14 @@ void main() { internalBootstrapBrowserTest(() => testMain); } +ByteData _toByteData(List bytes) { + final ByteData byteData = ByteData(bytes.length); + for (int i = 0; i < bytes.length; i++) { + byteData.setUint8(i, bytes[i]); + } + return byteData; +} + void testMain() { group('Keyboard', () { /// Used to save and restore [ui.window.onPlatformMessage] after each test. @@ -226,13 +235,23 @@ void testMain() { expect(count, 2); }); - test('prevents default when "Tab" is pressed', () { + test('prevents default when key is handled by the framework', () { + ByteData _toByteData(List bytes) { + final ByteData byteData = ByteData(bytes.length); + for (int i = 0; i < bytes.length; i++) { + byteData.setUint8(i, bytes[i]); + } + return byteData; + } + Keyboard.initialize(); int count = 0; ui.window.onPlatformMessage = (String channel, ByteData data, ui.PlatformMessageResponseCallback callback) { count += 1; + ByteData response = _toByteData(utf8.encode(json.encode({'handled': true}))); + callback(response); }; final html.KeyboardEvent event = dispatchKeyboardEvent( @@ -247,6 +266,29 @@ void testMain() { Keyboard.instance.dispose(); }); + test("Doesn't prevent default when key is not handled by the framework", () { + Keyboard.initialize(); + + int count = 0; + ui.window.onPlatformMessage = (String channel, ByteData data, + ui.PlatformMessageResponseCallback callback) { + count += 1; + ByteData response = _toByteData(utf8.encode(json.encode({'handled': false}))); + callback(response); + }; + + final html.KeyboardEvent event = dispatchKeyboardEvent( + 'keydown', + key: 'Tab', + code: 'Tab', + ); + + expect(event.defaultPrevented, isFalse); + expect(count, 1); + + Keyboard.instance.dispose(); + }); + test('keyboard events should be triggered on text fields', () { Keyboard.initialize(); @@ -278,6 +320,8 @@ void testMain() { ui.window.onPlatformMessage = (String channel, ByteData data, ui.PlatformMessageResponseCallback callback) { count += 1; + ByteData response = _toByteData(utf8.encode(json.encode({'handled': true}))); + callback(response); }; useTextEditingElement((html.Element element) { From 9686d66df381e9ffb0d0d5fc736c1a232cf6626e Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Thu, 14 Jan 2021 15:21:03 -0800 Subject: [PATCH 2/2] Fix silly use of json.encode --- lib/web_ui/lib/src/engine/keyboard.dart | 3 +-- lib/web_ui/test/keyboard_test.dart | 23 +++-------------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 6cb52acad39ea..8307bbac369b6 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -131,8 +131,7 @@ class Keyboard { if (data == null) { return; } - final String response = utf8.decode(data.buffer.asUint8List()); - final Map jsonResponse = json.decode(response); + final Map jsonResponse = _messageCodec.decodeMessage(data); if (jsonResponse['handled'] as bool) { // If the framework handled it, then don't propagate it any further. event.preventDefault(); diff --git a/lib/web_ui/test/keyboard_test.dart b/lib/web_ui/test/keyboard_test.dart index c91be7610cfd5..2b042b9fc1f87 100644 --- a/lib/web_ui/test/keyboard_test.dart +++ b/lib/web_ui/test/keyboard_test.dart @@ -6,7 +6,6 @@ import 'dart:html' as html; import 'dart:js_util' as js_util; import 'dart:typed_data'; -import 'dart:convert' hide Codec; import 'package:quiver/testing/async.dart'; import 'package:test/bootstrap/browser.dart'; @@ -18,14 +17,6 @@ void main() { internalBootstrapBrowserTest(() => testMain); } -ByteData _toByteData(List bytes) { - final ByteData byteData = ByteData(bytes.length); - for (int i = 0; i < bytes.length; i++) { - byteData.setUint8(i, bytes[i]); - } - return byteData; -} - void testMain() { group('Keyboard', () { /// Used to save and restore [ui.window.onPlatformMessage] after each test. @@ -236,21 +227,13 @@ void testMain() { }); test('prevents default when key is handled by the framework', () { - ByteData _toByteData(List bytes) { - final ByteData byteData = ByteData(bytes.length); - for (int i = 0; i < bytes.length; i++) { - byteData.setUint8(i, bytes[i]); - } - return byteData; - } - Keyboard.initialize(); int count = 0; ui.window.onPlatformMessage = (String channel, ByteData data, ui.PlatformMessageResponseCallback callback) { count += 1; - ByteData response = _toByteData(utf8.encode(json.encode({'handled': true}))); + ByteData response = const JSONMessageCodec().encodeMessage({'handled': true}); callback(response); }; @@ -273,7 +256,7 @@ void testMain() { ui.window.onPlatformMessage = (String channel, ByteData data, ui.PlatformMessageResponseCallback callback) { count += 1; - ByteData response = _toByteData(utf8.encode(json.encode({'handled': false}))); + ByteData response = const JSONMessageCodec().encodeMessage({'handled': false}); callback(response); }; @@ -320,7 +303,7 @@ void testMain() { ui.window.onPlatformMessage = (String channel, ByteData data, ui.PlatformMessageResponseCallback callback) { count += 1; - ByteData response = _toByteData(utf8.encode(json.encode({'handled': true}))); + ByteData response = const JSONMessageCodec().encodeMessage({'handled': true}); callback(response); };