Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e749ba3

Browse files
authored
Impl and test (#30488)
1 parent ba23c6c commit e749ba3

File tree

2 files changed

+46
-16
lines changed

2 files changed

+46
-16
lines changed

lib/web_ui/lib/src/engine/keyboard_binding.dart

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,7 @@ class KeyboardConverter {
371371
// followed by an immediate cancel event.
372372
(_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock);
373373

374-
final int? lastLogicalRecord = _pressingRecords[physicalKey];
375-
376-
ui.KeyEventType type;
374+
final ui.KeyEventType type;
377375

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

400398
} else if (isPhysicalDown) {
401399
// Case 2: Handle key down of normal keys
402-
type = ui.KeyEventType.down;
403-
if (lastLogicalRecord != null) {
400+
if (_pressingRecords[physicalKey] != null) {
404401
// This physical key is being pressed according to the record.
405402
if (event.repeat ?? false) {
406403
// A normal repeated key.
407404
type = ui.KeyEventType.repeat;
408405
} else {
409406
// A non-repeated key has been pressed that has the exact physical key as
410-
// a currently pressed one, usually indicating multiple keyboards are
411-
// pressing keys with the same physical key, or the up event was lost
412-
// during a loss of focus. The down event is ignored.
413-
event.preventDefault();
414-
return;
407+
// a currently pressed one. This can mean one of the following cases:
408+
//
409+
// * Multiple keyboards are pressing keys with the same physical key.
410+
// * The up event was lost during a loss of focus.
411+
// * The previous down event was a system shortcut and its release
412+
// was skipped (see `_startGuardingKey`,) such as holding Ctrl and
413+
// pressing V then V, within the "guard window".
414+
//
415+
// The three cases can't be distinguished, and in the 3rd case, the
416+
// latter event must be dispatched as down events for the framework to
417+
// correctly recognize and choose to not to handle. Therefore, an up
418+
// event is synthesized before it.
419+
_dispatchKeyData!(ui.KeyData(
420+
timeStamp: timeStamp,
421+
type: ui.KeyEventType.up,
422+
physical: physicalKey,
423+
logical: logicalKey,
424+
character: null,
425+
synthesized: true,
426+
));
427+
_pressingRecords.remove(physicalKey);
428+
type = ui.KeyEventType.down;
415429
}
416430
} else {
417431
// This physical key is not being pressed according to the record. It's a
418432
// normal down event, whether the system event is a repeat or not.
433+
type = ui.KeyEventType.down;
419434
}
420435

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

448+
// The _pressingRecords[physicalKey] might have been changed during the last
449+
// `if` clause.
450+
final int? lastLogicalRecord = _pressingRecords[physicalKey];
451+
433452
final int? nextLogicalRecord;
434453
switch (type) {
435454
case ui.KeyEventType.down:

lib/web_ui/test/keyboard_converter_test.dart

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ void testMain() {
378378
converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft));
379379
});
380380

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

397397
keyDataList.clear();
398398
converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)
399399
..onPreventDefault = onPreventDefault
400400
);
401-
expect(keyDataList, hasLength(1));
402-
expect(keyDataList[0].physical, 0);
403-
expect(keyDataList[0].logical, 0);
401+
expect(keyDataList, hasLength(2));
402+
expectKeyData(keyDataList.first,
403+
type: ui.KeyEventType.up,
404+
physical: kPhysicalShiftLeft,
405+
logical: kLogicalShiftLeft,
406+
character: null,
407+
synthesized: true,
408+
);
409+
expectKeyData(keyDataList.last,
410+
type: ui.KeyEventType.down,
411+
physical: kPhysicalShiftLeft,
412+
logical: kLogicalShiftLeft,
413+
character: null,
414+
);
404415
expect(preventedDefault, isTrue);
405416

406417
keyDataList.clear();

0 commit comments

Comments
 (0)