未验证 提交 e9edb8e1 编写于 作者: M Mouad Debbar 提交者: GitHub

Support correct keymap for web (#12712)

上级 73c0397d
......@@ -52,28 +52,51 @@ class Keyboard {
void _handleHtmlEvent(html.KeyboardEvent event) {
final Map<String, dynamic> eventData = <String, dynamic>{
'type': event.type,
// TODO(yjbanov): this emulates Android because that the only reasonable
// thing to map to right now (the other choice is fuchsia).
// However, eventually we need to have something that maps
// better to Web.
'keymap': 'android',
'keyCode': event.keyCode,
'keymap': 'web',
'code': event.code,
'key': event.key,
'metaState': _getMetaState(event),
};
// TODO(yjbanov): The browser does not report `charCode` for 'keydown' and
// 'keyup', only for 'keypress'. This restores the value
// from the 'key' field. However, we need to verify how
// many code units a single key can have. Right now it
// assumes exactly one unit (that's what Flutter framework
// expects). But we'll need a different strategy if other
// code unit counts are possible.
if (event.key.codeUnits.length == 1) {
eventData['codePoint'] = event.key.codeUnits.first;
}
ui.window.onPlatformMessage('flutter/keyevent',
_messageCodec.encodeMessage(eventData), _noopCallback);
}
}
const int _modifierNone = 0x00;
const int _modifierShift = 0x01;
const int _modifierAlt = 0x02;
const int _modifierControl = 0x04;
const int _modifierMeta = 0x08;
const int _modifierNumLock = 0x10;
const int _modifierCapsLock = 0x20;
const int _modifierScrollLock = 0x40;
/// Creates a bitmask representing the meta state of the [event].
int _getMetaState(html.KeyboardEvent event) {
int metaState = _modifierNone;
if (event.getModifierState('Shift')) {
metaState |= _modifierShift;
}
if (event.getModifierState('Alt')) {
metaState |= _modifierAlt;
}
if (event.getModifierState('Control')) {
metaState |= _modifierControl;
}
if (event.getModifierState('Meta')) {
metaState |= _modifierMeta;
}
if (event.getModifierState('NumLock')) {
metaState |= _modifierNumLock;
}
if (event.getModifierState('CapsLock')) {
metaState |= _modifierCapsLock;
}
if (event.getModifierState('ScrollLock')) {
metaState |= _modifierScrollLock;
}
return metaState;
}
void _noopCallback(ByteData data) {}
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:html' as html;
import 'dart:js_util' as js_util;
import 'dart:typed_data';
import 'package:ui/src/engine.dart';
......@@ -15,7 +16,7 @@ void main() {
test('initializes', () {
expect(Keyboard.instance, isNull);
Keyboard.initialize();
expect(Keyboard.instance, isNotNull);
expect(Keyboard.instance, isA<Keyboard>());
});
test('dispatches keyup to flutter/keyevent channel', () {
......@@ -27,17 +28,16 @@ void main() {
dataReceived = const JSONMessageCodec().decodeMessage(data);
};
html.window.dispatchEvent(html.KeyboardEvent('keyup'));
dispatchKeyboardEvent('keyup', key: 'SomeKey', code: 'SomeCode');
expect(channelReceived, 'flutter/keyevent');
expect(dataReceived['type'], 'keyup');
expect(dataReceived['keymap'], 'android');
// Unfortunately there's no way to fake `keyCode`.
expect(dataReceived['keyCode'], 0);
// Unfortunately there's no way to fake `key`.
expect(dataReceived, isNot(contains('codePoint')));
expect(dataReceived, <String, dynamic>{
'type': 'keyup',
'keymap': 'web',
'code': 'SomeCode',
'key': 'SomeKey',
'metaState': 0x0,
});
});
test('dispatches keydown to flutter/keyevent channel', () {
......@@ -49,17 +49,96 @@ void main() {
dataReceived = const JSONMessageCodec().decodeMessage(data);
};
html.window.dispatchEvent(html.KeyboardEvent('keydown'));
dispatchKeyboardEvent('keydown', key: 'SomeKey', code: 'SomeCode');
expect(channelReceived, 'flutter/keyevent');
expect(dataReceived['type'], 'keydown');
expect(dataReceived['keymap'], 'android');
expect(dataReceived, <String, dynamic>{
'type': 'keydown',
'keymap': 'web',
'code': 'SomeCode',
'key': 'SomeKey',
'metaState': 0x0,
});
});
// Unfortunately there's no way to fake `keyCode`.
expect(dataReceived['keyCode'], 0);
test('dispatches correct meta state', () {
Map<String, dynamic> dataReceived;
ui.window.onPlatformMessage = (String channel, ByteData data,
ui.PlatformMessageResponseCallback callback) {
dataReceived = const JSONMessageCodec().decodeMessage(data);
};
// Unfortunately there's no way to fake `key`.
expect(dataReceived, isNot(contains('codePoint')));
dispatchKeyboardEvent(
'keydown',
key: 'SomeKey',
code: 'SomeCode',
isControlPressed: true,
);
expect(dataReceived, <String, dynamic>{
'type': 'keydown',
'keymap': 'web',
'code': 'SomeCode',
'key': 'SomeKey',
// ctrl
'metaState': 0x4,
});
dispatchKeyboardEvent(
'keydown',
key: 'SomeKey',
code: 'SomeCode',
isShiftPressed: true,
isAltPressed: true,
isMetaPressed: true,
);
expect(dataReceived, <String, dynamic>{
'type': 'keydown',
'keymap': 'web',
'code': 'SomeCode',
'key': 'SomeKey',
// shift alt meta
'metaState': 0x1 | 0x2 | 0x8,
});
});
test('dispatches repeat events', () {
List<Map<String, dynamic>> messages = <Map<String, dynamic>>[];
ui.window.onPlatformMessage = (String channel, ByteData data,
ui.PlatformMessageResponseCallback callback) {
messages.add(const JSONMessageCodec().decodeMessage(data));
};
dispatchKeyboardEvent(
'keydown',
key: 'SomeKey',
code: 'SomeCode',
repeat: true,
);
dispatchKeyboardEvent(
'keydown',
key: 'SomeKey',
code: 'SomeCode',
repeat: true,
);
dispatchKeyboardEvent(
'keydown',
key: 'SomeKey',
code: 'SomeCode',
repeat: true,
);
final Map<String, dynamic> expectedMessage = <String, dynamic>{
'type': 'keydown',
'keymap': 'web',
'code': 'SomeCode',
'key': 'SomeKey',
'metaState': 0,
};
expect(messages, <Map<String, dynamic>>[
expectedMessage,
expectedMessage,
expectedMessage,
]);
});
test('stops dispatching events after dispose', () {
......@@ -69,19 +148,48 @@ void main() {
count += 1;
};
html.window.dispatchEvent(html.KeyboardEvent('keydown'));
dispatchKeyboardEvent('keydown');
expect(count, 1);
html.window.dispatchEvent(html.KeyboardEvent('keyup'));
dispatchKeyboardEvent('keyup');
expect(count, 2);
Keyboard.instance.dispose();
expect(Keyboard.instance, isNull);
// No more event dispatching.
html.window.dispatchEvent(html.KeyboardEvent('keydown'));
dispatchKeyboardEvent('keydown');
expect(count, 2);
html.window.dispatchEvent(html.KeyboardEvent('keyup'));
dispatchKeyboardEvent('keyup');
expect(count, 2);
});
});
}
void dispatchKeyboardEvent(
String type, {
String key,
String code,
bool repeat = false,
bool isShiftPressed = false,
bool isAltPressed = false,
bool isControlPressed = false,
bool isMetaPressed = false,
}) {
final Function jsKeyboardEvent =
js_util.getProperty(html.window, 'KeyboardEvent');
final List<dynamic> eventArgs = <dynamic>[
type,
<String, dynamic>{
'key': key,
'code': code,
'repeat': repeat,
'shiftKey': isShiftPressed,
'altKey': isAltPressed,
'ctrlKey': isControlPressed,
'metaKey': isMetaPressed,
}
];
final event =
js_util.callConstructor(jsKeyboardEvent, js_util.jsify(eventArgs));
html.window.dispatchEvent(event);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册