diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 0d7aec59c57cc..c003dcfce6045 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -91,8 +91,7 @@ class Keyboard { final String timerKey = keyboardEvent.code!; - // Don't synthesize a keyup event for modifier keys because the browser always - // sends a keyup event for those. + // Don't handle synthesizing a keyup event for modifier keys if (!_isModifierKey(event)) { // When the user enters a browser/system shortcut (e.g. `cmd+alt+i`) the // browser doesn't send a keyup for it. This puts the framework in a @@ -103,7 +102,10 @@ class Keyboard { // event within a specific duration ([_keydownCancelDuration]) we assume // the user has released the key and we synthesize a keyup event. _keydownTimers[timerKey]?.cancel(); - if (event.type == 'keydown') { + + // Only keys affected by modifiers, require synthesizing + // because the browser always sends a keyup event otherwise + if (event.type == 'keydown' && _isAffectedByModifiers(event)) { _keydownTimers[timerKey] = Timer(_keydownCancelDuration, () { _keydownTimers.remove(timerKey); _synthesizeKeyup(event); @@ -185,4 +187,11 @@ bool _isModifierKey(html.KeyboardEvent event) { return key == 'Meta' || key == 'Shift' || key == 'Alt' || key == 'Control'; } +/// Returns true if the [event] is been affects by any of the modifiers key +/// +/// This is a strong indication that this key is been used for a shortcut +bool _isAffectedByModifiers(html.KeyboardEvent event) { + return event.ctrlKey || event.shiftKey || event.altKey || event.metaKey; +} + void _noopCallback(ByteData? data) {} diff --git a/lib/web_ui/test/keyboard_test.dart b/lib/web_ui/test/keyboard_test.dart index 4b68cec6e2e13..c16fe89467b03 100644 --- a/lib/web_ui/test/keyboard_test.dart +++ b/lib/web_ui/test/keyboard_test.dart @@ -473,18 +473,37 @@ void testMain() { } messages.clear(); - // When repeat events stop for a long-enough period of time, a keyup - // should be synthesized. + Keyboard.instance.dispose(); + }, + ); + + testFakeAsync( + 'do not synthesize keyup when keys are not affected by meta modifiers', + (FakeAsync async) { + Keyboard.initialize(); + + List> messages = >[]; + ui.window.onPlatformMessage = (String channel, ByteData data, + ui.PlatformMessageResponseCallback callback) { + messages.add(const JSONMessageCodec().decodeMessage(data)); + }; + + dispatchKeyboardEvent( + 'keydown', + key: 'i', + code: 'KeyI', + ); + dispatchKeyboardEvent( + 'keydown', + key: 'o', + code: 'KeyO', + ); + messages.clear(); + + // Wait for a long-enough period of time and no events + // should be synthesized async.elapse(Duration(seconds: 3)); - expect(messages, >[ - { - 'type': 'keyup', - 'keymap': 'web', - 'key': 'i', - 'code': 'KeyI', - 'metaState': 0x0, - } - ]); + expect(messages, hasLength(0)); Keyboard.instance.dispose(); },