Skip to content

Commit

Permalink
Impl and test (flutter#30488)
Browse files Browse the repository at this point in the history
  • Loading branch information
dkwingsmt authored Jan 4, 2022
1 parent ba23c6c commit e749ba3
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 16 deletions.
41 changes: 30 additions & 11 deletions lib/web_ui/lib/src/engine/keyboard_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,7 @@ class KeyboardConverter {
// followed by an immediate cancel event.
(_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock);

final int? lastLogicalRecord = _pressingRecords[physicalKey];

ui.KeyEventType type;
final ui.KeyEventType type;

if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) {
// Case 1: Handle CapsLock on macOS
Expand All @@ -399,28 +397,45 @@ class KeyboardConverter {

} else if (isPhysicalDown) {
// Case 2: Handle key down of normal keys
type = ui.KeyEventType.down;
if (lastLogicalRecord != null) {
if (_pressingRecords[physicalKey] != null) {
// This physical key is being pressed according to the record.
if (event.repeat ?? false) {
// A normal repeated key.
type = ui.KeyEventType.repeat;
} else {
// A non-repeated key has been pressed that has the exact physical key as
// a currently pressed one, usually indicating multiple keyboards are
// pressing keys with the same physical key, or the up event was lost
// during a loss of focus. The down event is ignored.
event.preventDefault();
return;
// a currently pressed one. This can mean one of the following cases:
//
// * Multiple keyboards are pressing keys with the same physical key.
// * The up event was lost during a loss of focus.
// * The previous down event was a system shortcut and its release
// was skipped (see `_startGuardingKey`,) such as holding Ctrl and
// pressing V then V, within the "guard window".
//
// The three cases can't be distinguished, and in the 3rd case, the
// latter event must be dispatched as down events for the framework to
// correctly recognize and choose to not to handle. Therefore, an up
// event is synthesized before it.
_dispatchKeyData!(ui.KeyData(
timeStamp: timeStamp,
type: ui.KeyEventType.up,
physical: physicalKey,
logical: logicalKey,
character: null,
synthesized: true,
));
_pressingRecords.remove(physicalKey);
type = ui.KeyEventType.down;
}
} else {
// This physical key is not being pressed according to the record. It's a
// normal down event, whether the system event is a repeat or not.
type = ui.KeyEventType.down;
}

} else { // isPhysicalDown is false and not CapsLock
// Case 2: Handle key up of normal keys
if (lastLogicalRecord == null) {
if (_pressingRecords[physicalKey] == null) {
// The physical key has been released before. It indicates multiple
// keyboards pressed keys with the same physical key. Ignore the up event.
event.preventDefault();
Expand All @@ -430,6 +445,10 @@ class KeyboardConverter {
type = ui.KeyEventType.up;
}

// The _pressingRecords[physicalKey] might have been changed during the last
// `if` clause.
final int? lastLogicalRecord = _pressingRecords[physicalKey];

final int? nextLogicalRecord;
switch (type) {
case ui.KeyEventType.down:
Expand Down
21 changes: 16 additions & 5 deletions lib/web_ui/test/keyboard_converter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ void testMain() {
converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft));
});

test('Duplicate down is ignored', () {
test('Duplicate down is preceded with synthesized up', () {
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) {
keyDataList.add(key);
Expand All @@ -392,15 +392,26 @@ void testMain() {
);
expect(preventedDefault, isTrue);
preventedDefault = false;
// A KeyUp of ShiftLeft is missed due to loss of focus.
// A KeyUp of ShiftLeft is missed.

keyDataList.clear();
converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)
..onPreventDefault = onPreventDefault
);
expect(keyDataList, hasLength(1));
expect(keyDataList[0].physical, 0);
expect(keyDataList[0].logical, 0);
expect(keyDataList, hasLength(2));
expectKeyData(keyDataList.first,
type: ui.KeyEventType.up,
physical: kPhysicalShiftLeft,
logical: kLogicalShiftLeft,
character: null,
synthesized: true,
);
expectKeyData(keyDataList.last,
type: ui.KeyEventType.down,
physical: kPhysicalShiftLeft,
logical: kLogicalShiftLeft,
character: null,
);
expect(preventedDefault, isTrue);

keyDataList.clear();
Expand Down

0 comments on commit e749ba3

Please sign in to comment.