From e93f97fd2e735f3d48bcba08150f8bb5b9bb8fd2 Mon Sep 17 00:00:00 2001 From: ferhatb Date: Sat, 3 Oct 2020 10:19:19 -0700 Subject: [PATCH 1/3] Fix metaState for Key events for lock modifiers --- lib/web_ui/lib/src/engine/keyboard.dart | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index c003dcfce6045..8c1874d505838 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -116,6 +116,17 @@ class Keyboard { } _lastMetaState = _getMetaState(event); + if (event.type == 'keydown') { + // For lock modifiers _getMetaState won't report a metaState at keydown. + // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState. + if (event.key == 'CapsLock') { + _lastMetaState |= modifierCapsLock; + } else if (event.key == 'NumLock') { + _lastMetaState |= modifierNumLock; + } else if (event.key == 'ScrollLock') { + _lastMetaState |= modifierScrollLock; + } + } final Map eventData = { 'type': event.type, 'keymap': 'web', @@ -132,7 +143,6 @@ class Keyboard { switch (event.key) { case 'Tab': return true; - default: return false; } @@ -157,6 +167,9 @@ const int _modifierShift = 0x01; const int _modifierAlt = 0x02; const int _modifierControl = 0x04; const int _modifierMeta = 0x08; +const int modifierNumLock = 0x10; +const int modifierCapsLock = 0x20; +const int modifierScrollLock = 0x40; /// Creates a bitmask representing the meta state of the [event]. int _getMetaState(html.KeyboardEvent event) { @@ -173,8 +186,17 @@ int _getMetaState(html.KeyboardEvent event) { if (event.getModifierState('Meta')) { metaState |= _modifierMeta; } - // TODO: Re-enable lock key modifiers once there is support on Flutter - // Framework. https://github.com/flutter/flutter/issues/46718 + // See https://github.com/flutter/flutter/issues/66601 for why we don't + // set the ones below based on persistent state. + // if (event.getModifierState("CapsLock")) { + // metaState |= modifierCapsLock; + // } + // if (event.getModifierState("NumLock")) { + // metaState |= modifierNumLock; + // } + // if (event.getModifierState("ScrollLock")) { + // metaState |= modifierScrollLock; + // } return metaState; } From 2eea3f418a8fd0b192d0af8793c10ef5c6b06f5f Mon Sep 17 00:00:00 2001 From: ferhatb Date: Sat, 3 Oct 2020 10:28:13 -0700 Subject: [PATCH 2/3] Update Numlock detection --- lib/web_ui/lib/src/engine/keyboard.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 8c1874d505838..558e53f6f927a 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -121,7 +121,7 @@ class Keyboard { // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState. if (event.key == 'CapsLock') { _lastMetaState |= modifierCapsLock; - } else if (event.key == 'NumLock') { + } else if (event.code == 'NumLock') { _lastMetaState |= modifierNumLock; } else if (event.key == 'ScrollLock') { _lastMetaState |= modifierScrollLock; From 1a9e775591758724b254aa5196b5cbc1e0e39cdc Mon Sep 17 00:00:00 2001 From: ferhatb Date: Mon, 5 Oct 2020 12:07:54 -0700 Subject: [PATCH 3/3] Add integration test using Tab and CapsLock --- .../test_driver/text_editing_integration.dart | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart b/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart index 67263f054ca97..af1b8d6b00a0f 100644 --- a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart +++ b/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart @@ -143,6 +143,57 @@ void main() { expect(input2.value, 'Text2'); }); + testWidgets('Jump between TextFormFields with tab key after CapsLock is' + 'activated', + (WidgetTester tester) async { + app.main(); + await tester.pumpAndSettle(); + + // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 + SystemChannels.textInput.setMockMethodCallHandler(null); + + // Focus on a TextFormField. + final Finder finder = find.byKey(const Key('input')); + expect(finder, findsOneWidget); + await tester.tap(find.byKey(const Key('input'))); + + // A native input element will be appended to the DOM. + final List nodeList = document.getElementsByTagName('input'); + expect(nodeList.length, equals(1)); + final InputElement input = + document.getElementsByTagName('input')[0] as InputElement; + + // Press and release CapsLock. + dispatchKeyboardEvent(input, 'keydown', { + 'key': 'CapsLock', + 'code': 'CapsLock', + 'bubbles': true, + 'cancelable': true, + }); + dispatchKeyboardEvent(input, 'keyup', { + 'key': 'CapsLock', + 'code': 'CapsLock', + 'bubbles': true, + 'cancelable': true, + }); + + // Press Tab. The focus should move to the next TextFormField. + dispatchKeyboardEvent(input, 'keydown', { + 'key': 'Tab', + 'code': 'Tab', + 'bubbles': true, + 'cancelable': true, + }); + + await tester.pumpAndSettle(); + + // A native input element for the next TextField should be attached to the + // DOM. + final InputElement input2 = + document.getElementsByTagName('input')[0] as InputElement; + expect(input2.value, 'Text2'); + }); + testWidgets('Read-only fields work', (WidgetTester tester) async { const String text = 'Lorem ipsum dolor sit amet'; app.main();