Unverified Commit baac9a8a authored by Tong Mu's avatar Tong Mu Committed by GitHub

[Keyboard, Windows] Fix that IME events are still dispatched to FocusNode.onKey (#104244)

parent a79233cb
......@@ -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.
......@@ -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 };
}
......
......@@ -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.
......@@ -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();
......
......@@ -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(
......@@ -1953,7 +1975,7 @@ void main() {
},
);
}
RawKeyboard.instance.addListener(events.add);
await simulateKeyEventMessage('keydown', 229, 30);
expect(events, isEmpty);
expect(lastHandled, true);
......@@ -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?>{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment