mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Web] Synthesize key events for shift key on pointer events. (flutter/engine#36724)
This commit is contained in:
parent
b938b28387
commit
31b251007a
@ -323,8 +323,8 @@ class FlutterViewEmbedder {
|
||||
_sceneHostElement!.style.opacity = '0.3';
|
||||
}
|
||||
|
||||
PointerBinding.initInstance(glassPaneElement);
|
||||
KeyboardBinding.initInstance();
|
||||
PointerBinding.initInstance(glassPaneElement, KeyboardBinding.instance!.converter);
|
||||
|
||||
if (domWindow.visualViewport == null && isWebKit) {
|
||||
// Older Safari versions sometimes give us bogus innerWidth/innerHeight
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../engine.dart' show registerHotRestartListener;
|
||||
@ -30,6 +31,16 @@ final int _kLogicalShiftLeft = kWebLogicalLocationMap['Shift']![_kLocationLeft]!
|
||||
final int _kLogicalShiftRight = kWebLogicalLocationMap['Shift']![_kLocationRight]!;
|
||||
final int _kLogicalMetaLeft = kWebLogicalLocationMap['Meta']![_kLocationLeft]!;
|
||||
final int _kLogicalMetaRight = kWebLogicalLocationMap['Meta']![_kLocationRight]!;
|
||||
|
||||
final int _kPhysicalAltLeft = kWebToPhysicalKey['AltLeft']!;
|
||||
final int _kPhysicalAltRight = kWebToPhysicalKey['AltRight']!;
|
||||
final int _kPhysicalControlLeft = kWebToPhysicalKey['ControlLeft']!;
|
||||
final int _kPhysicalControlRight = kWebToPhysicalKey['ControlRight']!;
|
||||
final int _kPhysicalShiftLeft = kWebToPhysicalKey['ShiftLeft']!;
|
||||
final int _kPhysicalShiftRight = kWebToPhysicalKey['ShiftRight']!;
|
||||
final int _kPhysicalMetaLeft = kWebToPhysicalKey['MetaLeft']!;
|
||||
final int _kPhysicalMetaRight = kWebToPhysicalKey['MetaRight']!;
|
||||
|
||||
// Map logical keys for modifier keys to the functions that can get their
|
||||
// modifier flag out of an event.
|
||||
final Map<int, _ModifierGetter> _kLogicalKeyToModifierGetter = <int, _ModifierGetter>{
|
||||
@ -106,6 +117,7 @@ class KeyboardBinding {
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardConverter get converter => _converter;
|
||||
late final KeyboardConverter _converter;
|
||||
final Map<String, DomEventListener> _listeners = <String, DomEventListener>{};
|
||||
|
||||
@ -559,4 +571,107 @@ class KeyboardConverter {
|
||||
_dispatchKeyData = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Synthesize modifier keys up or down events only when the known pressing states are different.
|
||||
void synthesizeModifiersIfNeeded(
|
||||
bool altPressed,
|
||||
bool controlPressed,
|
||||
bool metaPressed,
|
||||
bool shiftPressed,
|
||||
num eventTimestamp,
|
||||
) {
|
||||
_synthesizeModifierIfNeeded(
|
||||
_kPhysicalAltLeft,
|
||||
_kPhysicalAltRight,
|
||||
_kLogicalAltLeft,
|
||||
_kLogicalAltRight,
|
||||
altPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
|
||||
eventTimestamp,
|
||||
);
|
||||
_synthesizeModifierIfNeeded(
|
||||
_kPhysicalControlLeft,
|
||||
_kPhysicalControlRight,
|
||||
_kLogicalControlLeft,
|
||||
_kLogicalControlRight,
|
||||
controlPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
|
||||
eventTimestamp,
|
||||
);
|
||||
_synthesizeModifierIfNeeded(
|
||||
_kPhysicalMetaLeft,
|
||||
_kPhysicalMetaRight,
|
||||
_kLogicalMetaLeft,
|
||||
_kLogicalMetaRight,
|
||||
metaPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
|
||||
eventTimestamp,
|
||||
);
|
||||
_synthesizeModifierIfNeeded(
|
||||
_kPhysicalShiftLeft,
|
||||
_kPhysicalShiftRight,
|
||||
_kLogicalShiftLeft,
|
||||
_kLogicalShiftRight,
|
||||
shiftPressed ? ui.KeyEventType.down : ui.KeyEventType.up,
|
||||
eventTimestamp,
|
||||
);
|
||||
}
|
||||
|
||||
void _synthesizeModifierIfNeeded(
|
||||
int physicalLeft,
|
||||
int physicalRight,
|
||||
int logicalLeft,
|
||||
int logicalRight,
|
||||
ui.KeyEventType type,
|
||||
num domTimestamp,
|
||||
) {
|
||||
final bool leftPressed = _pressingRecords.containsKey(physicalLeft);
|
||||
final bool rightPressed = _pressingRecords.containsKey(physicalRight);
|
||||
final bool alreadyPressed = leftPressed || rightPressed;
|
||||
final bool synthesizeDown = type == ui.KeyEventType.down && !alreadyPressed;
|
||||
final bool synthesizeUp = type == ui.KeyEventType.up && alreadyPressed;
|
||||
|
||||
// Synthesize a down event only for the left key if right and left are not pressed
|
||||
if (synthesizeDown) {
|
||||
_synthesizeKeyDownEvent(domTimestamp, physicalLeft, logicalLeft);
|
||||
}
|
||||
|
||||
// Synthesize an up event for left key if pressed
|
||||
if (synthesizeUp && leftPressed) {
|
||||
_synthesizeKeyUpEvent(domTimestamp, physicalLeft, logicalLeft);
|
||||
}
|
||||
|
||||
// Synthesize an up event for right key if pressed
|
||||
if (synthesizeUp && rightPressed) {
|
||||
_synthesizeKeyUpEvent(domTimestamp, physicalRight, logicalRight);
|
||||
}
|
||||
}
|
||||
|
||||
void _synthesizeKeyDownEvent(num domTimestamp, int physical, int logical) {
|
||||
performDispatchKeyData(ui.KeyData(
|
||||
timeStamp: _eventTimeStampToDuration(domTimestamp),
|
||||
type: ui.KeyEventType.down,
|
||||
physical: physical,
|
||||
logical: logical,
|
||||
character: null,
|
||||
synthesized: true,
|
||||
));
|
||||
// Update pressing state
|
||||
_pressingRecords[physical] = logical;
|
||||
}
|
||||
|
||||
void _synthesizeKeyUpEvent(num domTimestamp, int physical, int logical) {
|
||||
performDispatchKeyData(ui.KeyData(
|
||||
timeStamp: _eventTimeStampToDuration(domTimestamp),
|
||||
type: ui.KeyEventType.up,
|
||||
physical: physical,
|
||||
logical: logical,
|
||||
character: null,
|
||||
synthesized: true,
|
||||
));
|
||||
// Update pressing states
|
||||
_pressingRecords.remove(physical);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
bool debugKeyIsPressed(int physical) {
|
||||
return _pressingRecords.containsKey(physical);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:ui/src/engine/keyboard_binding.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../engine.dart' show registerHotRestartListener;
|
||||
@ -73,7 +74,7 @@ class SafariPointerEventWorkaround {
|
||||
}
|
||||
|
||||
class PointerBinding {
|
||||
PointerBinding(this.glassPaneElement)
|
||||
PointerBinding(this.glassPaneElement, this._keyboardConverter)
|
||||
: _pointerDataConverter = PointerDataConverter(),
|
||||
_detector = const PointerSupportDetector() {
|
||||
if (isIosSafari) {
|
||||
@ -86,9 +87,9 @@ class PointerBinding {
|
||||
static PointerBinding? get instance => _instance;
|
||||
static PointerBinding? _instance;
|
||||
|
||||
static void initInstance(DomElement glassPaneElement) {
|
||||
static void initInstance(DomElement glassPaneElement, KeyboardConverter keyboardConverter) {
|
||||
if (_instance == null) {
|
||||
_instance = PointerBinding(glassPaneElement);
|
||||
_instance = PointerBinding(glassPaneElement, keyboardConverter);
|
||||
assert(() {
|
||||
registerHotRestartListener(_instance!.dispose);
|
||||
return true;
|
||||
@ -107,6 +108,7 @@ class PointerBinding {
|
||||
|
||||
PointerSupportDetector _detector;
|
||||
final PointerDataConverter _pointerDataConverter;
|
||||
KeyboardConverter _keyboardConverter;
|
||||
late _BaseAdapter _adapter;
|
||||
|
||||
/// Should be used in tests to define custom detection of pointer support.
|
||||
@ -137,15 +139,23 @@ class PointerBinding {
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
void debugOverrideKeyboardConverter(KeyboardConverter keyboardConverter) {
|
||||
_keyboardConverter = keyboardConverter;
|
||||
_adapter.clearListeners();
|
||||
_adapter = _createAdapter();
|
||||
_pointerDataConverter.clearPointerState();
|
||||
}
|
||||
|
||||
_BaseAdapter _createAdapter() {
|
||||
if (_detector.hasPointerEvents) {
|
||||
return _PointerAdapter(_onPointerData, glassPaneElement, _pointerDataConverter);
|
||||
return _PointerAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
|
||||
}
|
||||
if (_detector.hasTouchEvents) {
|
||||
return _TouchAdapter(_onPointerData, glassPaneElement, _pointerDataConverter);
|
||||
return _TouchAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
|
||||
}
|
||||
if (_detector.hasMouseEvents) {
|
||||
return _MouseAdapter(_onPointerData, glassPaneElement, _pointerDataConverter);
|
||||
return _MouseAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
|
||||
}
|
||||
throw UnsupportedError('This browser does not support pointer, touch, or mouse events.');
|
||||
}
|
||||
@ -239,7 +249,12 @@ class _Listener {
|
||||
|
||||
/// Common functionality that's shared among adapters.
|
||||
abstract class _BaseAdapter {
|
||||
_BaseAdapter(this._callback, this.glassPaneElement, this._pointerDataConverter) {
|
||||
_BaseAdapter(
|
||||
this._callback,
|
||||
this.glassPaneElement,
|
||||
this._pointerDataConverter,
|
||||
this._keyboardConverter,
|
||||
) {
|
||||
setup();
|
||||
}
|
||||
|
||||
@ -247,6 +262,7 @@ abstract class _BaseAdapter {
|
||||
final DomElement glassPaneElement;
|
||||
final _PointerDataCallback _callback;
|
||||
final PointerDataConverter _pointerDataConverter;
|
||||
final KeyboardConverter _keyboardConverter;
|
||||
|
||||
/// Each subclass is expected to override this method to attach its own event
|
||||
/// listeners and convert events into pointer events.
|
||||
@ -570,7 +586,8 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_PointerAdapter(
|
||||
super.callback,
|
||||
super.glassPaneElement,
|
||||
super.pointerDataConverter
|
||||
super.pointerDataConverter,
|
||||
super.keyboardConverter,
|
||||
);
|
||||
|
||||
final Map<int, _ButtonSanitizer> _sanitizers = <int, _ButtonSanitizer>{};
|
||||
@ -602,13 +619,27 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
String eventName,
|
||||
_PointerEventListener handler, {
|
||||
bool useCapture = true,
|
||||
bool checkModifiers = true,
|
||||
}) {
|
||||
addEventListener(target, eventName, (DomEvent event) {
|
||||
final DomPointerEvent pointerEvent = event as DomPointerEvent;
|
||||
if (checkModifiers) {
|
||||
_checkModifiersState(event);
|
||||
}
|
||||
handler(pointerEvent);
|
||||
}, useCapture: useCapture);
|
||||
}
|
||||
|
||||
void _checkModifiersState(DomPointerEvent event) {
|
||||
_keyboardConverter.synthesizeModifiersIfNeeded(
|
||||
event.getModifierState('Alt'),
|
||||
event.getModifierState('Control'),
|
||||
event.getModifierState('Meta'),
|
||||
event.getModifierState('Shift'),
|
||||
event.timeStamp!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
_addPointerEventListener(glassPaneElement, 'pointerdown', (DomPointerEvent event) {
|
||||
@ -654,7 +685,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_convertEventsToPointerData(data: pointerData, event: event, details: details);
|
||||
_callback(pointerData);
|
||||
}
|
||||
}, useCapture: false);
|
||||
}, useCapture: false, checkModifiers: false);
|
||||
|
||||
_addPointerEventListener(domWindow, 'pointerup', (DomPointerEvent event) {
|
||||
final int device = _getPointerId(event);
|
||||
@ -680,7 +711,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_convertEventsToPointerData(data: pointerData, event: event, details: details);
|
||||
_callback(pointerData);
|
||||
}
|
||||
});
|
||||
}, checkModifiers: false);
|
||||
|
||||
_addWheelEventListener((DomEvent event) {
|
||||
_handleWheelEvent(event);
|
||||
@ -767,7 +798,8 @@ class _TouchAdapter extends _BaseAdapter {
|
||||
_TouchAdapter(
|
||||
super.callback,
|
||||
super.glassPaneElement,
|
||||
super.pointerDataConverter
|
||||
super.pointerDataConverter,
|
||||
super.keyboardConverter,
|
||||
);
|
||||
|
||||
final Set<int> _pressedTouches = <int>{};
|
||||
@ -775,13 +807,26 @@ class _TouchAdapter extends _BaseAdapter {
|
||||
void _pressTouch(int identifier) { _pressedTouches.add(identifier); }
|
||||
void _unpressTouch(int identifier) { _pressedTouches.remove(identifier); }
|
||||
|
||||
void _addTouchEventListener(DomEventTarget target, String eventName, _TouchEventListener handler) {
|
||||
void _addTouchEventListener(DomEventTarget target, String eventName, _TouchEventListener handler, {bool checkModifiers = true,}) {
|
||||
addEventListener(target, eventName, (DomEvent event) {
|
||||
final DomTouchEvent touchEvent = event as DomTouchEvent;
|
||||
if (checkModifiers) {
|
||||
_checkModifiersState(event);
|
||||
}
|
||||
handler(touchEvent);
|
||||
});
|
||||
}
|
||||
|
||||
void _checkModifiersState(DomTouchEvent event) {
|
||||
_keyboardConverter.synthesizeModifiersIfNeeded(
|
||||
event.altKey,
|
||||
event.ctrlKey,
|
||||
event.metaKey,
|
||||
event.shiftKey,
|
||||
event.timeStamp!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
_addTouchEventListener(glassPaneElement, 'touchstart', (DomTouchEvent event) {
|
||||
@ -910,7 +955,8 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_MouseAdapter(
|
||||
super.callback,
|
||||
super.glassPaneElement,
|
||||
super.pointerDataConverter
|
||||
super.pointerDataConverter,
|
||||
super.keyboardConverter,
|
||||
);
|
||||
|
||||
final _ButtonSanitizer _sanitizer = _ButtonSanitizer();
|
||||
@ -920,13 +966,27 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
String eventName,
|
||||
_MouseEventListener handler, {
|
||||
bool useCapture = true,
|
||||
bool checkModifiers = true,
|
||||
}) {
|
||||
addEventListener(target, eventName, (DomEvent event) {
|
||||
final DomMouseEvent mouseEvent = event as DomMouseEvent;
|
||||
if (checkModifiers) {
|
||||
_checkModifiersState(event);
|
||||
}
|
||||
handler(mouseEvent);
|
||||
}, useCapture: useCapture);
|
||||
}
|
||||
|
||||
void _checkModifiersState(DomMouseEvent event) {
|
||||
_keyboardConverter.synthesizeModifiersIfNeeded(
|
||||
event.getModifierState('Alt'),
|
||||
event.getModifierState('Control'),
|
||||
event.getModifierState('Meta'),
|
||||
event.getModifierState('Shift'),
|
||||
event.timeStamp!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
_addMouseEventListener(glassPaneElement, 'mousedown', (DomMouseEvent event) {
|
||||
|
||||
@ -9,6 +9,8 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../keyboard_converter_test.dart';
|
||||
|
||||
const int _kNoButtonChange = -1;
|
||||
const PointerSupportDetector _defaultSupportDetector = PointerSupportDetector();
|
||||
|
||||
@ -46,6 +48,13 @@ void testMain() {
|
||||
dpi = window.devicePixelRatio;
|
||||
});
|
||||
|
||||
KeyboardConverter createKeyboardConverter(List<ui.KeyData> keyDataList) {
|
||||
return KeyboardConverter((ui.KeyData key) {
|
||||
keyDataList.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
test('ios workaround', () {
|
||||
debugEmulateIosSafari = true;
|
||||
addTearDown(() {
|
||||
@ -55,7 +64,9 @@ void testMain() {
|
||||
final MockSafariPointerEventWorkaround mockSafariPointer =
|
||||
MockSafariPointerEventWorkaround();
|
||||
SafariPointerEventWorkaround.instance = mockSafariPointer;
|
||||
final PointerBinding instance = PointerBinding(createDomHTMLDivElement());
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
final PointerBinding instance = PointerBinding(createDomHTMLDivElement(), keyboardConverter);
|
||||
expect(mockSafariPointer.workAroundInvoked, isIosSafari);
|
||||
instance.dispose();
|
||||
}, skip: !isSafari);
|
||||
@ -507,6 +518,248 @@ void testMain() {
|
||||
},
|
||||
);
|
||||
|
||||
_testEach<_BasicEventContext>(
|
||||
<_BasicEventContext>[
|
||||
_PointerEventContext(),
|
||||
_MouseEventContext(),
|
||||
_TouchEventContext(),
|
||||
],
|
||||
'synthesize modifier keys left down event if left or right are not pressed',
|
||||
(_BasicEventContext context) {
|
||||
PointerBinding.instance!.debugOverrideDetector(context);
|
||||
|
||||
// Should synthesize a modifier left key down event when DOM event indicates
|
||||
// that the modifier key is pressed and known pressing state doesn't contain
|
||||
// the modifier left key nor the modifier right key.
|
||||
void shouldSynthesizeLeftDownIfNotPressed(String key) {
|
||||
final int physicalLeft = kWebToPhysicalKey['${key}Left']!;
|
||||
final int physicalRight = kWebToPhysicalKey['${key}Right']!;
|
||||
final int logicalLeft = kWebLogicalLocationMap[key]![kLocationLeft]!;
|
||||
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
PointerBinding.instance!.debugOverrideKeyboardConverter(keyboardConverter);
|
||||
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), false);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), false);
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.down,
|
||||
physical: physicalLeft,
|
||||
logical: logicalLeft,
|
||||
character: null,
|
||||
synthesized: true,
|
||||
);
|
||||
}
|
||||
|
||||
context.altPressed = true;
|
||||
shouldSynthesizeLeftDownIfNotPressed('Alt');
|
||||
context.unpressAllModifiers();
|
||||
context.ctrlPressed = true;
|
||||
shouldSynthesizeLeftDownIfNotPressed('Control');
|
||||
context.unpressAllModifiers();
|
||||
context.metaPressed = true;
|
||||
shouldSynthesizeLeftDownIfNotPressed('Meta');
|
||||
context.unpressAllModifiers();
|
||||
context.shiftPressed = true;
|
||||
shouldSynthesizeLeftDownIfNotPressed('Shift');
|
||||
context.unpressAllModifiers();
|
||||
},
|
||||
);
|
||||
|
||||
_testEach<_BasicEventContext>(
|
||||
<_BasicEventContext>[
|
||||
_PointerEventContext(),
|
||||
_MouseEventContext(),
|
||||
_TouchEventContext(),
|
||||
],
|
||||
'should not synthesize modifier keys down event if left or right are pressed',
|
||||
(_BasicEventContext context) {
|
||||
PointerBinding.instance!.debugOverrideDetector(context);
|
||||
|
||||
// Should not synthesize a modifier down event when DOM event indicates
|
||||
// that the modifier key is pressed and known pressing state contains
|
||||
// the modifier left key.
|
||||
void shouldNotSynthesizeDownIfLeftPressed(String key, int modifiers) {
|
||||
final int physicalLeft = kWebToPhysicalKey['${key}Left']!;
|
||||
final int physicalRight = kWebToPhysicalKey['${key}Right']!;
|
||||
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
PointerBinding.instance!.debugOverrideKeyboardConverter(keyboardConverter);
|
||||
|
||||
keyboardConverter.handleEvent(keyDownEvent('${key}Left', key, modifiers, kLocationLeft));
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), true);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), false);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 0);
|
||||
}
|
||||
|
||||
// Should not synthesize a modifier down event when DOM event indicates
|
||||
// that the modifier key is pressed and known pressing state contains
|
||||
// the modifier right key.
|
||||
void shouldNotSynthesizeDownIfRightPressed(String key, int modifiers) {
|
||||
final int physicalLeft = kWebToPhysicalKey['${key}Left']!;
|
||||
final int physicalRight = kWebToPhysicalKey['${key}Right']!;
|
||||
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
PointerBinding.instance!.debugOverrideKeyboardConverter(keyboardConverter);
|
||||
|
||||
keyboardConverter.handleEvent(keyDownEvent('${key}Right', key, modifiers, kLocationRight));
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), false);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), true);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 0);
|
||||
}
|
||||
|
||||
context.altPressed = true;
|
||||
shouldNotSynthesizeDownIfLeftPressed('Alt', kAlt);
|
||||
shouldNotSynthesizeDownIfRightPressed('Alt', kAlt);
|
||||
context.unpressAllModifiers();
|
||||
context.ctrlPressed = true;
|
||||
shouldNotSynthesizeDownIfLeftPressed('Control', kCtrl);
|
||||
shouldNotSynthesizeDownIfRightPressed('Control', kCtrl);
|
||||
context.unpressAllModifiers();
|
||||
context.metaPressed = true;
|
||||
shouldNotSynthesizeDownIfLeftPressed('Meta', kMeta);
|
||||
shouldNotSynthesizeDownIfRightPressed('Meta', kMeta);
|
||||
context.unpressAllModifiers();
|
||||
context.shiftPressed = true;
|
||||
shouldNotSynthesizeDownIfLeftPressed('Shift', kShift);
|
||||
shouldNotSynthesizeDownIfRightPressed('Shift', kShift);
|
||||
context.unpressAllModifiers();
|
||||
},
|
||||
);
|
||||
|
||||
_testEach<_BasicEventContext>(
|
||||
<_BasicEventContext>[
|
||||
_PointerEventContext(),
|
||||
_MouseEventContext(),
|
||||
_TouchEventContext(),
|
||||
],
|
||||
'synthesize modifier keys up event if left or right are pressed',
|
||||
(_BasicEventContext context) {
|
||||
PointerBinding.instance!.debugOverrideDetector(context);
|
||||
|
||||
// Should synthesize a modifier left key up event when DOM event indicates
|
||||
// that the modifier key is not pressed and known pressing state contains
|
||||
// the modifier left key.
|
||||
void shouldSynthesizeLeftUpIfLeftPressed(String key, int modifiers) {
|
||||
final int physicalLeft = kWebToPhysicalKey['${key}Left']!;
|
||||
final int physicalRight = kWebToPhysicalKey['${key}Right']!;
|
||||
final int logicalLeft = kWebLogicalLocationMap[key]![kLocationLeft]!;
|
||||
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
PointerBinding.instance!.debugOverrideKeyboardConverter(keyboardConverter);
|
||||
|
||||
keyboardConverter.handleEvent(keyDownEvent('${key}Left', key, modifiers, kLocationLeft));
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), true);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), false);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.up,
|
||||
physical: physicalLeft,
|
||||
logical: logicalLeft,
|
||||
character: null,
|
||||
synthesized: true,
|
||||
);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), false);
|
||||
}
|
||||
|
||||
// Should synthesize a modifier right key up event when DOM event indicates
|
||||
// that the modifier key is not pressed and known pressing state contains
|
||||
// the modifier right key.
|
||||
void shouldSynthesizeRightUpIfRightPressed(String key, int modifiers) {
|
||||
final int physicalLeft = kWebToPhysicalKey['${key}Left']!;
|
||||
final int physicalRight = kWebToPhysicalKey['${key}Right']!;
|
||||
final int logicalRight = kWebLogicalLocationMap[key]![kLocationRight]!;
|
||||
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
PointerBinding.instance!.debugOverrideKeyboardConverter(keyboardConverter);
|
||||
|
||||
keyboardConverter.handleEvent(keyDownEvent('${key}Right', key, modifiers, kLocationRight));
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), false);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), true);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.up,
|
||||
physical: physicalRight,
|
||||
logical: logicalRight,
|
||||
character: null,
|
||||
synthesized: true,
|
||||
);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), false);
|
||||
}
|
||||
|
||||
context.altPressed = false;
|
||||
shouldSynthesizeLeftUpIfLeftPressed('Alt', kAlt);
|
||||
shouldSynthesizeRightUpIfRightPressed('Alt', kAlt);
|
||||
context.ctrlPressed = false;
|
||||
shouldSynthesizeLeftUpIfLeftPressed('Control', kCtrl);
|
||||
shouldSynthesizeRightUpIfRightPressed('Control', kCtrl);
|
||||
context.metaPressed = false;
|
||||
shouldSynthesizeLeftUpIfLeftPressed('Meta', kMeta);
|
||||
shouldSynthesizeRightUpIfRightPressed('Meta', kMeta);
|
||||
context.shiftPressed = false;
|
||||
shouldSynthesizeLeftUpIfLeftPressed('Shift', kShift);
|
||||
shouldSynthesizeRightUpIfRightPressed('Shift', kShift);
|
||||
},
|
||||
);
|
||||
|
||||
_testEach<_BasicEventContext>(
|
||||
<_BasicEventContext>[
|
||||
_PointerEventContext(),
|
||||
_MouseEventContext(),
|
||||
_TouchEventContext(),
|
||||
],
|
||||
'should not synthesize modifier keys up event if left or right are not pressed',
|
||||
(_BasicEventContext context) {
|
||||
PointerBinding.instance!.debugOverrideDetector(context);
|
||||
|
||||
// Should not synthesize a modifier up event when DOM event indicates
|
||||
// that the modifier key is not pressed and known pressing state does
|
||||
// not contain the modifier left key nor the modifier right key.
|
||||
void shouldNotSynthesizeUpIfNotPressed(String key) {
|
||||
final int physicalLeft = kWebToPhysicalKey['${key}Left']!;
|
||||
final int physicalRight = kWebToPhysicalKey['${key}Right']!;
|
||||
|
||||
final List<ui.KeyData> keyDataList = <ui.KeyData>[];
|
||||
final KeyboardConverter keyboardConverter = createKeyboardConverter(keyDataList);
|
||||
PointerBinding.instance!.debugOverrideKeyboardConverter(keyboardConverter);
|
||||
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalLeft), false);
|
||||
expect(keyboardConverter.debugKeyIsPressed(physicalRight), false);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 0);
|
||||
}
|
||||
|
||||
context.altPressed = false;
|
||||
shouldNotSynthesizeUpIfNotPressed('Alt');
|
||||
context.ctrlPressed = false;
|
||||
shouldNotSynthesizeUpIfNotPressed('Control');
|
||||
context.metaPressed = false;
|
||||
shouldNotSynthesizeUpIfNotPressed('Meta');
|
||||
context.shiftPressed = false;
|
||||
shouldNotSynthesizeUpIfNotPressed('Shift');
|
||||
},
|
||||
);
|
||||
|
||||
_testEach<_ButtonedEventMixin>(
|
||||
<_ButtonedEventMixin>[
|
||||
if (!isIosSafari) _PointerEventContext(),
|
||||
@ -2466,7 +2719,7 @@ abstract class _BasicEventContext implements PointerSupportDetector {
|
||||
bool altPressed = false;
|
||||
bool ctrlPressed = false;
|
||||
bool metaPressed = false;
|
||||
bool shitPressed = false;
|
||||
bool shiftPressed = false;
|
||||
|
||||
// Generate an event that is:
|
||||
//
|
||||
@ -2490,14 +2743,14 @@ abstract class _BasicEventContext implements PointerSupportDetector {
|
||||
altPressed = true;
|
||||
ctrlPressed = true;
|
||||
metaPressed = true;
|
||||
shitPressed = true;
|
||||
shiftPressed = true;
|
||||
}
|
||||
|
||||
void unpressAllModifiers() {
|
||||
altPressed = false;
|
||||
ctrlPressed = false;
|
||||
metaPressed = false;
|
||||
shitPressed = false;
|
||||
shiftPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2687,7 +2940,7 @@ class _TouchEventContext extends _BasicEventContext
|
||||
'altKey': altPressed,
|
||||
'ctrlKey': ctrlPressed,
|
||||
'metaKey': metaPressed,
|
||||
'shiftKey': shitPressed,
|
||||
'shiftKey': shiftPressed,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -2826,7 +3079,7 @@ class _MouseEventContext extends _BasicEventContext
|
||||
'altKey': altPressed,
|
||||
'ctrlKey': ctrlPressed,
|
||||
'metaKey': metaPressed,
|
||||
'shiftKey': shitPressed,
|
||||
'shiftKey': shiftPressed,
|
||||
}
|
||||
];
|
||||
return js_util.callConstructor<DomMouseEvent>(
|
||||
@ -2908,7 +3161,7 @@ class _PointerEventContext extends _BasicEventContext
|
||||
'altKey': altPressed,
|
||||
'ctrlKey': ctrlPressed,
|
||||
'metaKey': metaPressed,
|
||||
'shiftKey': shitPressed,
|
||||
'shiftKey': shiftPressed,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user