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

Commit df6220d

Browse files
committed
Inject KeyboardConverter + add tests
1 parent 7bfc20b commit df6220d

File tree

4 files changed

+397
-119
lines changed

4 files changed

+397
-119
lines changed

lib/web_ui/lib/src/engine/embedder.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ class FlutterViewEmbedder {
323323
_sceneHostElement!.style.opacity = '0.3';
324324
}
325325

326-
PointerBinding.initInstance(glassPaneElement);
327326
KeyboardBinding.initInstance();
327+
PointerBinding.initInstance(glassPaneElement, KeyboardBinding.instance!.converter);
328328

329329
if (domWindow.visualViewport == null && isWebKit) {
330330
// Older Safari versions sometimes give us bogus innerWidth/innerHeight

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

Lines changed: 105 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:meta/meta.dart';
56
import 'package:ui/ui.dart' as ui;
67

78
import '../engine.dart' show registerHotRestartListener;
@@ -116,6 +117,7 @@ class KeyboardBinding {
116117
}
117118
}
118119

120+
KeyboardConverter get converter => _converter;
119121
late final KeyboardConverter _converter;
120122
final Map<String, DomEventListener> _listeners = <String, DomEventListener>{};
121123

@@ -162,104 +164,6 @@ class KeyboardBinding {
162164
_converter = KeyboardConverter(_onKeyData, onMacOs: operatingSystem == OperatingSystem.macOs);
163165
}
164166

165-
// Synthesize modifier keys up or down events only when the known pressing states are different.
166-
void synthesizeModifiersIfNeeded(
167-
bool altPressed,
168-
bool controlPressed,
169-
bool metaPressed,
170-
bool shiftPressed,
171-
num eventTimestamp,
172-
) {
173-
_synthesizeModifierIfNeeded(
174-
_kPhysicalAltLeft,
175-
_kPhysicalAltRight,
176-
_kLogicalAltLeft,
177-
_kLogicalAltRight,
178-
altPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
179-
eventTimestamp,
180-
);
181-
_synthesizeModifierIfNeeded(
182-
_kPhysicalControlLeft,
183-
_kPhysicalControlRight,
184-
_kLogicalControlLeft,
185-
_kLogicalControlRight,
186-
controlPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
187-
eventTimestamp,
188-
);
189-
_synthesizeModifierIfNeeded(
190-
_kPhysicalMetaLeft,
191-
_kPhysicalMetaRight,
192-
_kLogicalMetaLeft,
193-
_kLogicalMetaRight,
194-
metaPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
195-
eventTimestamp,
196-
);
197-
_synthesizeModifierIfNeeded(
198-
_kPhysicalShiftLeft,
199-
_kPhysicalShiftRight,
200-
_kLogicalShiftLeft,
201-
_kLogicalShiftRight,
202-
shiftPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
203-
eventTimestamp,
204-
);
205-
}
206-
207-
void _synthesizeModifierIfNeeded(
208-
int physicalLeft,
209-
int physicalRight,
210-
int logicalLeft,
211-
int logicalRight,
212-
ui.KeyEventType type,
213-
num domTimestamp,
214-
) {
215-
final bool leftPressed = _converter._pressingRecords.containsKey(physicalLeft);
216-
final bool rightPressed = _converter._pressingRecords.containsKey(physicalRight);
217-
final bool alreadyPressed = leftPressed || rightPressed;
218-
final bool synthesizeDown = type == ui.KeyEventType.down && !alreadyPressed;
219-
final bool synthesizeUp = type == ui.KeyEventType.up && alreadyPressed;
220-
221-
// Synthesize a down event only for the left key if right and left are not pressed
222-
if (synthesizeDown) {
223-
_synthesizeKeyDownEvent(domTimestamp, physicalLeft, logicalLeft);
224-
}
225-
226-
// Synthesize an up event for left key if pressed
227-
if (synthesizeUp && leftPressed) {
228-
_synthesizeKeyUpEvent(domTimestamp, physicalLeft, logicalLeft);
229-
}
230-
231-
// Synthesize an up event for right key if pressed
232-
if (synthesizeUp && rightPressed) {
233-
_synthesizeKeyUpEvent(domTimestamp, physicalRight, logicalRight);
234-
}
235-
}
236-
237-
void _synthesizeKeyDownEvent(num domTimestamp, int physical, int logical) {
238-
_converter.performDispatchKeyData(ui.KeyData(
239-
timeStamp: _eventTimeStampToDuration(domTimestamp),
240-
type: ui.KeyEventType.down,
241-
physical: physical,
242-
logical: logical,
243-
character: null,
244-
synthesized: true,
245-
));
246-
// Update pressing state
247-
_converter._pressingRecords[physical] = logical;
248-
}
249-
250-
void _synthesizeKeyUpEvent(num domTimestamp, int physical, int logical) {
251-
_converter.performDispatchKeyData(ui.KeyData(
252-
timeStamp: _eventTimeStampToDuration(domTimestamp),
253-
type: ui.KeyEventType.up,
254-
physical: physical,
255-
logical: logical,
256-
character: null,
257-
synthesized: true,
258-
));
259-
// Update pressing states
260-
_converter._pressingRecords.remove(physical);
261-
}
262-
263167
void _reset() {
264168
_clearListeners();
265169
_converter.dispose();
@@ -667,4 +571,107 @@ class KeyboardConverter {
667571
_dispatchKeyData = null;
668572
}
669573
}
574+
575+
// Synthesize modifier keys up or down events only when the known pressing states are different.
576+
void synthesizeModifiersIfNeeded(
577+
bool altPressed,
578+
bool controlPressed,
579+
bool metaPressed,
580+
bool shiftPressed,
581+
num eventTimestamp,
582+
) {
583+
_synthesizeModifierIfNeeded(
584+
_kPhysicalAltLeft,
585+
_kPhysicalAltRight,
586+
_kLogicalAltLeft,
587+
_kLogicalAltRight,
588+
altPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
589+
eventTimestamp,
590+
);
591+
_synthesizeModifierIfNeeded(
592+
_kPhysicalControlLeft,
593+
_kPhysicalControlRight,
594+
_kLogicalControlLeft,
595+
_kLogicalControlRight,
596+
controlPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
597+
eventTimestamp,
598+
);
599+
_synthesizeModifierIfNeeded(
600+
_kPhysicalMetaLeft,
601+
_kPhysicalMetaRight,
602+
_kLogicalMetaLeft,
603+
_kLogicalMetaRight,
604+
metaPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
605+
eventTimestamp,
606+
);
607+
_synthesizeModifierIfNeeded(
608+
_kPhysicalShiftLeft,
609+
_kPhysicalShiftRight,
610+
_kLogicalShiftLeft,
611+
_kLogicalShiftRight,
612+
shiftPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
613+
eventTimestamp,
614+
);
615+
}
616+
617+
void _synthesizeModifierIfNeeded(
618+
int physicalLeft,
619+
int physicalRight,
620+
int logicalLeft,
621+
int logicalRight,
622+
ui.KeyEventType type,
623+
num domTimestamp,
624+
) {
625+
final bool leftPressed = _pressingRecords.containsKey(physicalLeft);
626+
final bool rightPressed = _pressingRecords.containsKey(physicalRight);
627+
final bool alreadyPressed = leftPressed || rightPressed;
628+
final bool synthesizeDown = type == ui.KeyEventType.down && !alreadyPressed;
629+
final bool synthesizeUp = type == ui.KeyEventType.up && alreadyPressed;
630+
631+
// Synthesize a down event only for the left key if right and left are not pressed
632+
if (synthesizeDown) {
633+
_synthesizeKeyDownEvent(domTimestamp, physicalLeft, logicalLeft);
634+
}
635+
636+
// Synthesize an up event for left key if pressed
637+
if (synthesizeUp && leftPressed) {
638+
_synthesizeKeyUpEvent(domTimestamp, physicalLeft, logicalLeft);
639+
}
640+
641+
// Synthesize an up event for right key if pressed
642+
if (synthesizeUp && rightPressed) {
643+
_synthesizeKeyUpEvent(domTimestamp, physicalRight, logicalRight);
644+
}
645+
}
646+
647+
void _synthesizeKeyDownEvent(num domTimestamp, int physical, int logical) {
648+
performDispatchKeyData(ui.KeyData(
649+
timeStamp: _eventTimeStampToDuration(domTimestamp),
650+
type: ui.KeyEventType.down,
651+
physical: physical,
652+
logical: logical,
653+
character: null,
654+
synthesized: true,
655+
));
656+
// Update pressing state
657+
_pressingRecords[physical] = logical;
658+
}
659+
660+
void _synthesizeKeyUpEvent(num domTimestamp, int physical, int logical) {
661+
performDispatchKeyData(ui.KeyData(
662+
timeStamp: _eventTimeStampToDuration(domTimestamp),
663+
type: ui.KeyEventType.up,
664+
physical: physical,
665+
logical: logical,
666+
character: null,
667+
synthesized: true,
668+
));
669+
// Update pressing states
670+
_pressingRecords.remove(physical);
671+
}
672+
673+
@visibleForTesting
674+
bool debugKeyIsPressed(int physical) {
675+
return _pressingRecords.containsKey(physical);
676+
}
670677
}

lib/web_ui/lib/src/engine/pointer_binding.dart

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class SafariPointerEventWorkaround {
7474
}
7575

7676
class PointerBinding {
77-
PointerBinding(this.glassPaneElement)
77+
PointerBinding(this.glassPaneElement, this._keyboardConverter)
7878
: _pointerDataConverter = PointerDataConverter(),
7979
_detector = const PointerSupportDetector() {
8080
if (isIosSafari) {
@@ -87,9 +87,9 @@ class PointerBinding {
8787
static PointerBinding? get instance => _instance;
8888
static PointerBinding? _instance;
8989

90-
static void initInstance(DomElement glassPaneElement) {
90+
static void initInstance(DomElement glassPaneElement, KeyboardConverter keyboardConverter) {
9191
if (_instance == null) {
92-
_instance = PointerBinding(glassPaneElement);
92+
_instance = PointerBinding(glassPaneElement, keyboardConverter);
9393
assert(() {
9494
registerHotRestartListener(_instance!.dispose);
9595
return true;
@@ -108,6 +108,7 @@ class PointerBinding {
108108

109109
PointerSupportDetector _detector;
110110
final PointerDataConverter _pointerDataConverter;
111+
KeyboardConverter _keyboardConverter;
111112
late _BaseAdapter _adapter;
112113

113114
/// Should be used in tests to define custom detection of pointer support.
@@ -138,15 +139,23 @@ class PointerBinding {
138139
}
139140
}
140141

142+
@visibleForTesting
143+
void debugOverrideKeyboardConverter(KeyboardConverter keyboardConverter) {
144+
_keyboardConverter = keyboardConverter;
145+
_adapter.clearListeners();
146+
_adapter = _createAdapter();
147+
_pointerDataConverter.clearPointerState();
148+
}
149+
141150
_BaseAdapter _createAdapter() {
142151
if (_detector.hasPointerEvents) {
143-
return _PointerAdapter(_onPointerData, glassPaneElement, _pointerDataConverter);
152+
return _PointerAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
144153
}
145154
if (_detector.hasTouchEvents) {
146-
return _TouchAdapter(_onPointerData, glassPaneElement, _pointerDataConverter);
155+
return _TouchAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
147156
}
148157
if (_detector.hasMouseEvents) {
149-
return _MouseAdapter(_onPointerData, glassPaneElement, _pointerDataConverter);
158+
return _MouseAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
150159
}
151160
throw UnsupportedError('This browser does not support pointer, touch, or mouse events.');
152161
}
@@ -240,14 +249,20 @@ class _Listener {
240249

241250
/// Common functionality that's shared among adapters.
242251
abstract class _BaseAdapter {
243-
_BaseAdapter(this._callback, this.glassPaneElement, this._pointerDataConverter) {
252+
_BaseAdapter(
253+
this._callback,
254+
this.glassPaneElement,
255+
this._pointerDataConverter,
256+
this._keyboardConverter,
257+
) {
244258
setup();
245259
}
246260

247261
final List<_Listener> _listeners = <_Listener>[];
248262
final DomElement glassPaneElement;
249263
final _PointerDataCallback _callback;
250264
final PointerDataConverter _pointerDataConverter;
265+
final KeyboardConverter _keyboardConverter;
251266

252267
/// Each subclass is expected to override this method to attach its own event
253268
/// listeners and convert events into pointer events.
@@ -571,7 +586,8 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
571586
_PointerAdapter(
572587
super.callback,
573588
super.glassPaneElement,
574-
super.pointerDataConverter
589+
super.pointerDataConverter,
590+
super.keyboardConverter,
575591
);
576592

577593
final Map<int, _ButtonSanitizer> _sanitizers = <int, _ButtonSanitizer>{};
@@ -615,7 +631,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
615631
}
616632

617633
void _checkModifiersState(DomPointerEvent event) {
618-
KeyboardBinding.instance!.synthesizeModifiersIfNeeded(
634+
_keyboardConverter.synthesizeModifiersIfNeeded(
619635
event.getModifierState('Alt'),
620636
event.getModifierState('Control'),
621637
event.getModifierState('Meta'),
@@ -782,7 +798,8 @@ class _TouchAdapter extends _BaseAdapter {
782798
_TouchAdapter(
783799
super.callback,
784800
super.glassPaneElement,
785-
super.pointerDataConverter
801+
super.pointerDataConverter,
802+
super.keyboardConverter,
786803
);
787804

788805
final Set<int> _pressedTouches = <int>{};
@@ -801,7 +818,7 @@ class _TouchAdapter extends _BaseAdapter {
801818
}
802819

803820
void _checkModifiersState(DomTouchEvent event) {
804-
KeyboardBinding.instance!.synthesizeModifiersIfNeeded(
821+
_keyboardConverter.synthesizeModifiersIfNeeded(
805822
event.altKey,
806823
event.ctrlKey,
807824
event.metaKey,
@@ -938,7 +955,8 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
938955
_MouseAdapter(
939956
super.callback,
940957
super.glassPaneElement,
941-
super.pointerDataConverter
958+
super.pointerDataConverter,
959+
super.keyboardConverter,
942960
);
943961

944962
final _ButtonSanitizer _sanitizer = _ButtonSanitizer();
@@ -960,7 +978,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
960978
}
961979

962980
void _checkModifiersState(DomMouseEvent event) {
963-
KeyboardBinding.instance!.synthesizeModifiersIfNeeded(
981+
_keyboardConverter.synthesizeModifiersIfNeeded(
964982
event.getModifierState('Alt'),
965983
event.getModifierState('Control'),
966984
event.getModifierState('Meta'),

0 commit comments

Comments
 (0)