[macOS, Keyboard] Duplicate down events are no longer ignored, but kept and preceded by up events (flutter/engine#31800)

This commit is contained in:
Tong Mu
2022-03-03 21:31:12 -08:00
committed by GitHub
parent 643b1cd598
commit 2ed741ac83
2 changed files with 33 additions and 31 deletions

View File

@@ -680,12 +680,23 @@ const char* getEventString(NSString* characters) {
bool isARepeat = event.isARepeat;
NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
if (pressedLogicalKey != nil && !isARepeat) {
// Normally the key up events won't be missed since macOS always sends the
// key up event to the window where the corresponding key down occurred.
// However this might happen in add-to-app scenarios if the focus is changed
// This might happen in add-to-app scenarios if the focus is changed
// from the native view to the Flutter view amid the key tap.
[callback resolveTo:TRUE];
return;
//
// This might also happen when a key event is forged (such as by an
// IME) using the same keyCode as an unreleased key. See
// https://github.com/flutter/flutter/issues/82673#issuecomment-988661079
FlutterKeyEvent flutterEvent = {
.struct_size = sizeof(FlutterKeyEvent),
.timestamp = GetFlutterTimestampFrom(event.timestamp),
.type = kFlutterKeyEventTypeUp,
.physical = physicalKey,
.logical = [pressedLogicalKey unsignedLongLongValue],
.character = nil,
.synthesized = true,
};
[self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
pressedLogicalKey = nil;
}
if (pressedLogicalKey == nil) {

View File

@@ -306,7 +306,7 @@ TEST(FlutterEmbedderKeyResponderUnittests, MultipleCharacters) {
[events removeAllObjects];
}
TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateDownEvent) {
TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeForDuplicateDownEvent) {
__block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
__block BOOL last_handled = TRUE;
FlutterKeyEvent* event;
@@ -319,7 +319,7 @@ TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateDownEvent) {
userData:user_data]];
}];
last_handled = FALSE;
last_handled = TRUE;
[responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
callback:^(BOOL handled) {
last_handled = handled;
@@ -332,44 +332,35 @@ TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateDownEvent) {
EXPECT_EQ(event->logical, kLogicalKeyA);
EXPECT_STREQ(event->character, "a");
EXPECT_EQ(event->synthesized, false);
EXPECT_EQ(last_handled, TRUE);
[[events lastObject] respond:FALSE];
EXPECT_EQ(last_handled, FALSE);
[[events lastObject] respond:TRUE];
EXPECT_EQ(last_handled, TRUE);
[events removeAllObjects];
last_handled = FALSE;
[responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
last_handled = TRUE;
[responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"à", @"à", FALSE, kKeyCodeKeyA)
callback:^(BOOL handled) {
last_handled = handled;
}];
EXPECT_EQ([events count], 1u);
EXPECT_EQ(last_handled, TRUE);
event = [events lastObject].data;
EXPECT_EQ(event->physical, 0ull);
EXPECT_EQ(event->logical, 0ull);
EXPECT_FALSE([[events lastObject] hasCallback]);
EXPECT_EQ(last_handled, TRUE);
EXPECT_EQ([events count], 2u);
[events removeAllObjects];
last_handled = FALSE;
[responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
callback:^(BOOL handled) {
last_handled = handled;
}];
EXPECT_EQ([events count], 1u);
event = [events lastObject].data;
event = [events firstObject].data;
EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
EXPECT_EQ(event->physical, kPhysicalKeyA);
EXPECT_EQ(event->logical, kLogicalKeyA);
EXPECT_STREQ(event->character, nullptr);
EXPECT_STREQ(event->character, NULL);
EXPECT_EQ(event->synthesized, true);
event = [events lastObject].data;
EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
EXPECT_EQ(event->physical, kPhysicalKeyA);
EXPECT_EQ(event->logical, 0xE0ull /* à */);
EXPECT_STREQ(event->character, "à");
EXPECT_EQ(event->synthesized, false);
[[events lastObject] respond:FALSE];
EXPECT_EQ(last_handled, FALSE);
[[events lastObject] respond:TRUE];
EXPECT_EQ(last_handled, TRUE);
[events removeAllObjects];
}