Skip to content

Commit

Permalink
[Keyboard, Windows] Fix that IME events are still dispatched to Focus…
Browse files Browse the repository at this point in the history
…Node.onKey (#104244)
  • Loading branch information
dkwingsmt authored May 21, 2022
1 parent a79233c commit baac9a8
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 34 deletions.
48 changes: 36 additions & 12 deletions packages/flutter/lib/src/services/hardware_keyboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,11 @@ class KeyEventManager {
// dispatchable [RawKeyEvent] is available.
final List<KeyEvent> _keyEventsSinceLastMessage = <KeyEvent>[];

// When a RawKeyDownEvent is skipped ([RawKeyEventData.shouldDispatchEvent]
// is false), its physical key will be recorded here, so that its up event
// can also be properly skipped.
final Set<PhysicalKeyboardKey> _skippedRawKeysPressed = <PhysicalKeyboardKey>{};

/// Dispatch a key data to global and leaf listeners.
///
/// This method is the handler to the global `onKeyData` API.
Expand Down Expand Up @@ -843,21 +848,40 @@ class KeyEventManager {
_rawKeyboard.addListener(_convertRawEventAndStore);
}
final RawKeyEvent rawEvent = RawKeyEvent.fromMessage(message as Map<String, dynamic>);
// The following `handleRawKeyEvent` will call `_convertRawEventAndStore`
// unless the event is not dispatched.
bool handled = _rawKeyboard.handleRawKeyEvent(rawEvent);

for (final KeyEvent event in _keyEventsSinceLastMessage) {
handled = _hardwareKeyboard.handleKeyEvent(event) || handled;
}
if (_transitMode == KeyDataTransitMode.rawKeyData) {
assert(setEquals(_rawKeyboard.physicalKeysPressed, _hardwareKeyboard.physicalKeysPressed),
'RawKeyboard reported ${_rawKeyboard.physicalKeysPressed}, '
'while HardwareKeyboard reported ${_hardwareKeyboard.physicalKeysPressed}');
bool shouldDispatch = true;
if (rawEvent is RawKeyDownEvent) {
if (!rawEvent.data.shouldDispatchEvent()) {
shouldDispatch = false;
_skippedRawKeysPressed.add(rawEvent.physicalKey);
} else {
_skippedRawKeysPressed.remove(rawEvent.physicalKey);
}
} else if (rawEvent is RawKeyUpEvent) {
if (_skippedRawKeysPressed.contains(rawEvent.physicalKey)) {
_skippedRawKeysPressed.remove(rawEvent.physicalKey);
shouldDispatch = false;
}
}

handled = _dispatchKeyMessage(_keyEventsSinceLastMessage, rawEvent) || handled;
_keyEventsSinceLastMessage.clear();
bool handled = true;
if (shouldDispatch) {
// The following `handleRawKeyEvent` will call `_convertRawEventAndStore`
// unless the event is not dispatched.
handled = _rawKeyboard.handleRawKeyEvent(rawEvent);

for (final KeyEvent event in _keyEventsSinceLastMessage) {
handled = _hardwareKeyboard.handleKeyEvent(event) || handled;
}
if (_transitMode == KeyDataTransitMode.rawKeyData) {
assert(setEquals(_rawKeyboard.physicalKeysPressed, _hardwareKeyboard.physicalKeysPressed),
'RawKeyboard reported ${_rawKeyboard.physicalKeysPressed}, '
'while HardwareKeyboard reported ${_hardwareKeyboard.physicalKeysPressed}');
}

handled = _dispatchKeyMessage(_keyEventsSinceLastMessage, rawEvent) || handled;
_keyEventsSinceLastMessage.clear();
}

return <String, dynamic>{ 'handled': handled };
}
Expand Down
25 changes: 5 additions & 20 deletions packages/flutter/lib/src/services/raw_keyboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -661,27 +661,13 @@ class RawKeyboard {
/// Process a new [RawKeyEvent] by recording the state changes and
/// dispatching to listeners.
bool handleRawKeyEvent(RawKeyEvent event) {
bool shouldDispatch = true;
if (event is RawKeyDownEvent) {
if (event.data.shouldDispatchEvent()) {
_keysPressed[event.physicalKey] = event.logicalKey;
} else {
shouldDispatch = false;
_hiddenKeysPressed.add(event.physicalKey);
}
_keysPressed[event.physicalKey] = event.logicalKey;
} else if (event is RawKeyUpEvent) {
if (!_hiddenKeysPressed.contains(event.physicalKey)) {
// Use the physical key in the key up event to find the physical key from
// the corresponding key down event and remove it, even if the logical
// keys don't match.
_keysPressed.remove(event.physicalKey);
} else {
_hiddenKeysPressed.remove(event.physicalKey);
shouldDispatch = false;
}
}
if (!shouldDispatch) {
return true;
// Use the physical key in the key up event to find the physical key from
// the corresponding key down event and remove it, even if the logical
// keys don't match.
_keysPressed.remove(event.physicalKey);
}
// Make sure that the modifiers reflect reality, in case a modifier key was
// pressed/released while the app didn't have focus.
Expand Down Expand Up @@ -855,7 +841,6 @@ class RawKeyboard {
}

final Map<PhysicalKeyboardKey, LogicalKeyboardKey> _keysPressed = <PhysicalKeyboardKey, LogicalKeyboardKey>{};
final Set<PhysicalKeyboardKey> _hiddenKeysPressed = <PhysicalKeyboardKey>{};

/// Returns the set of keys currently pressed.
Set<LogicalKeyboardKey> get keysPressed => _keysPressed.values.toSet();
Expand Down
26 changes: 24 additions & 2 deletions packages/flutter/test/services/raw_keyboard_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1936,6 +1936,28 @@ void main() {
const String platform = 'windows';
bool lastHandled = true;
final List<RawKeyEvent> events = <RawKeyEvent>[];

// Test both code paths: addListener, and FocusNode.onKey.
RawKeyboard.instance.addListener(events.add);
final FocusNode node = FocusNode(
onKey: (_, RawKeyEvent event) {
events.add(event);
return KeyEventResult.ignored;
},
);
await tester.pumpWidget(RawKeyboardListener(
focusNode: node,
child: Container(),
));
node.requestFocus();
await tester.pumpAndSettle();

// Dispatch an arbitrary key press for the correct transit mode.
await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
await simulateKeyUpEvent(LogicalKeyboardKey.keyA);
expect(events, hasLength(4));
events.clear();

// Simulate raw events because VK_PROCESSKEY does not exist in the key mapping.
Future<void> simulateKeyEventMessage(String type, int keyCode, int scanCode) {
return ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
Expand All @@ -1953,7 +1975,7 @@ void main() {
},
);
}
RawKeyboard.instance.addListener(events.add);

await simulateKeyEventMessage('keydown', 229, 30);
expect(events, isEmpty);
expect(lastHandled, true);
Expand All @@ -1962,7 +1984,7 @@ void main() {
expect(events, isEmpty);
expect(lastHandled, true);
expect(RawKeyboard.instance.keysPressed, isEmpty);
});
}, variant: KeySimulatorTransitModeVariant.keyDataThenRawKeyData());

test('data.toString', () {
expect(RawKeyEvent.fromMessage(const <String, Object?>{
Expand Down

0 comments on commit baac9a8

Please sign in to comment.