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

Commit efd9a7b

Browse files
Keyboard guarantee non empty events (#28648)
* Web * macos * Linux * Easier web impl * doc and format * Better linux impl * Format * Better impl mac * Format * Windows * Format * Apply suggestions from code review Co-authored-by: Greg Spencer <[email protected]> Co-authored-by: Greg Spencer <[email protected]>
1 parent 2d0c025 commit efd9a7b

7 files changed

+236
-143
lines changed

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

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,18 @@ class FlutterHtmlKeyboardEvent {
204204
// [dispatchKeyData] as given in the constructor. Some key data might be
205205
// dispatched asynchronously.
206206
class KeyboardConverter {
207-
KeyboardConverter(this.dispatchKeyData, {this.onMacOs = false});
207+
KeyboardConverter(this.performDispatchKeyData, {this.onMacOs = false});
208208

209-
final DispatchKeyData dispatchKeyData;
209+
final DispatchKeyData performDispatchKeyData;
210210
final bool onMacOs;
211211

212+
// The `performDispatchKeyData` wrapped with tracking logic.
213+
//
214+
// It is non-null only during `handleEvent`. All events during `handleEvent`
215+
// should be dispatched with `_dispatchKeyData`, others with
216+
// `performDispatchKeyData`.
217+
DispatchKeyData? _dispatchKeyData;
218+
212219
bool _disposed = false;
213220
void dispose() {
214221
_disposed = true;
@@ -294,7 +301,9 @@ class KeyboardConverter {
294301
Future<void>.delayed(duration).then<void>((_) {
295302
if (!canceled && !_disposed) {
296303
callback();
297-
dispatchKeyData(getData());
304+
// This dispatch is performed asynchronously, therefore should not use
305+
// `_dispatchKeyData`.
306+
performDispatchKeyData(getData());
298307
}
299308
});
300309
return () { canceled = true; };
@@ -335,17 +344,7 @@ class KeyboardConverter {
335344
_keyGuards.remove(physicalKey)?.call();
336345
}
337346

338-
// Parse the HTML event, update states, and dispatch Flutter key data through
339-
// [dispatchKeyData].
340-
//
341-
// * The method might dispatch some synthesized key data first to update states,
342-
// results discarded.
343-
// * Then it dispatches exactly one non-synthesized key data that corresponds
344-
// to the `event`, i.e. the primary key data. If this dispatching returns
345-
// true, then this event will be invoked `preventDefault`.
346-
// * Some key data might be synthesized to update states after the main key
347-
// data. They are always scheduled asynchronously with results discarded.
348-
void handleEvent(FlutterHtmlKeyboardEvent event) {
347+
void _handleEvent(FlutterHtmlKeyboardEvent event) {
349348
final Duration timeStamp = _eventTimeStampToDuration(event.timeStamp!);
350349

351350
final String eventKey = event.key!;
@@ -411,7 +410,6 @@ class KeyboardConverter {
411410
// a currently pressed one, usually indicating multiple keyboards are
412411
// pressing keys with the same physical key, or the up event was lost
413412
// during a loss of focus. The down event is ignored.
414-
dispatchKeyData(_emptyKeyData);
415413
event.preventDefault();
416414
return;
417415
}
@@ -425,7 +423,6 @@ class KeyboardConverter {
425423
if (lastLogicalRecord == null) {
426424
// The physical key has been released before. It indicates multiple
427425
// keyboards pressed keys with the same physical key. Ignore the up event.
428-
dispatchKeyData(_emptyKeyData);
429426
event.preventDefault();
430427
return;
431428
}
@@ -465,7 +462,7 @@ class KeyboardConverter {
465462
if (logicalRecord != logicalKey)
466463
return false;
467464

468-
dispatchKeyData(ui.KeyData(
465+
_dispatchKeyData!(ui.KeyData(
469466
timeStamp: timeStamp,
470467
type: ui.KeyEventType.up,
471468
physical: physicalKey,
@@ -497,9 +494,36 @@ class KeyboardConverter {
497494
synthesized: false,
498495
);
499496

500-
final bool primaryHandled = dispatchKeyData(keyData);
497+
final bool primaryHandled = _dispatchKeyData!(keyData);
501498
if (primaryHandled) {
502499
event.preventDefault();
503500
}
504501
}
502+
503+
// Parse the HTML event, update states, and dispatch Flutter key data through
504+
// [performDispatchKeyData].
505+
//
506+
// * The method might dispatch some synthesized key data first to update states,
507+
// results discarded.
508+
// * Then it dispatches exactly one non-synthesized key data that corresponds
509+
// to the `event`, i.e. the primary key data. If this dispatching returns
510+
// true, then this event will be invoked `preventDefault`.
511+
// * Some key data might be synthesized to update states after the main key
512+
// data. They are always scheduled asynchronously with results discarded.
513+
void handleEvent(FlutterHtmlKeyboardEvent event) {
514+
assert(_dispatchKeyData == null);
515+
bool sentAnyEvents = false;
516+
_dispatchKeyData = (ui.KeyData data) {
517+
sentAnyEvents = true;
518+
return performDispatchKeyData(data);
519+
};
520+
try {
521+
_handleEvent(event);
522+
} finally {
523+
if (!sentAnyEvents) {
524+
_dispatchKeyData!(_emptyKeyData);
525+
}
526+
_dispatchKeyData = null;
527+
}
528+
}
505529
}

0 commit comments

Comments
 (0)