Adding support for logical and physical key events (#27627)

This adds support for logical and physical key information inside of RawKeyEvent. This allows developers to differentiate keys in a platform-agnostic way. They are able to tell the physical location of a key (PhysicalKeyboardKey) and a logical meaning of the key (LogicalKeyboardKey), as well as get notified of the character generated by the keypress. All of which is useful for handling keyboard shortcuts.

This PR builds on the previous PR (#27620) which generated the key code mappings and definitions.
This commit is contained in:
Greg Spencer 2019-02-08 12:42:34 -08:00 committed by GitHub
parent b7807ce84c
commit 431cfdafd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 4771 additions and 85 deletions

View file

@ -89,6 +89,8 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
dataText.add(Text('hidUsage: ${data.hidUsage} (${_asHex(data.hidUsage)})'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
}
dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}'));
for (ModifierKey modifier in data.modifiersPressed.keys) {
for (KeyboardSide side in KeyboardSide.values) {
if (data.isModifierPressed(modifier, side: side)) {

View file

@ -21,6 +21,87 @@ import 'package:flutter/foundation.dart';
/// in a particular location on the keyboard, without regard for the modifier
/// state, mode, or keyboard layout.
///
/// As an example, if you wanted to implement an app where the "Q" key "quit"
/// something, you'd want to look at the logical key to detect this, since you
/// would like to have it match the key with "Q" on it, instead of always
/// looking for "the key next next to the TAB key", since on a French keyboard,
/// the key next to the TAB key has an "A" on it.
///
/// Conversely, if you wanted a game where the key next to the CAPS LOCK (the
/// "A" key on a QWERTY keyboard) moved the player to the left, you'd want to
/// look at the physical key to make sure that regardless of the character the
/// key produces, you got the key that is in that location on the keyboard.
///
/// {@tool snippet --template=stateful_widget}
/// This example shows how to detect if the user has selected the logical "Q"
/// key.
///
/// ```dart imports
/// import 'package:flutter/foundation.dart';
/// import 'package:flutter/services.dart';
/// ```
///
/// ```dart
/// // The node used to request the keyboard focus.
/// final FocusNode _focusNode = FocusNode();
/// // The message to display.
/// String _message;
///
/// // Focus nodes need to be disposed.
/// @override
/// void dispose() {
/// _focusNode.dispose();
/// super.dispose();
/// }
///
/// // Handles the key events from the RawKeyboardListener and update the
/// // _message.
/// void _handleKeyEvent(RawKeyEvent event) {
/// setState(() {
/// if (event.logicalKey == LogicalKeyboardKey.keyQ) {
/// _message = 'Pressed the "Q" key!';
/// } else {
/// if (kReleaseMode) {
/// _message = 'Not a Q: Key label is "${event.logicalKey.keyLabel ?? '<none>'}"';
/// } else {
/// // This will only print useful information in debug mode.
/// _message = 'Not a Q: Pressed ${event.logicalKey.debugName}';
/// }
/// }
/// });
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// final TextTheme textTheme = Theme.of(context).textTheme;
/// return Container(
/// color: Colors.white,
/// alignment: Alignment.center,
/// child: DefaultTextStyle(
/// style: textTheme.display1,
/// child: RawKeyboardListener(
/// focusNode: _focusNode,
/// onKey: _handleKeyEvent,
/// child: AnimatedBuilder(
/// animation: _focusNode,
/// builder: (BuildContext context, Widget child) {
/// if (!_focusNode.hasFocus) {
/// return GestureDetector(
/// onTap: () {
/// FocusScope.of(context).requestFocus(_focusNode);
/// },
/// child: Text('Tap to focus'),
/// );
/// }
/// return Text(_message ?? 'Press a key');
/// },
/// ),
/// ),
/// ),
/// );
/// }
/// ```
/// {@end-tool}
/// See also:
///
/// * [RawKeyEvent], the keyboard event object received by widgets that listen
@ -28,24 +109,26 @@ import 'package:flutter/foundation.dart';
/// * [RawKeyboardListener], a widget used to listen to and supply handlers for
/// keyboard events.
class LogicalKeyboardKey extends Diagnosticable {
/// Defines a KeyboardKey value and optional debug name.
/// Creates a LogicalKeyboardKey object with an optional key label and debug
/// name.
///
/// To save executable size, it is recommended that the [debugName] be null in
/// release mode. You can do this using the [kReleaseMode] constant:
/// [keyId] must not be null.
///
/// {@tool sample}
/// const LogicalKeyboardKey mySpecialKey = LogicalKeyboardKey(
/// 0x0010000000a,
/// debugName: kReleaseMode ? null : 'Special Key',
/// );
/// To save executable size, it is recommended that the [debugName] be null in
/// release mode. You can do this by using the [kReleaseMode] constant.
///
/// ```dart
/// const LogicalKeyboardKey(0x0010000000a, debugName: kReleaseMode ? null : 'Special Key')
/// ```
/// {@end-tool}
const LogicalKeyboardKey(this.keyId, {this.debugName, this.keyLabel});
const LogicalKeyboardKey(this.keyId, {this.debugName, this.keyLabel})
: assert(keyId != null);
/// A unique code representing this key.
///
/// This is an opaque identifier, and should not be unpacked to derive
/// information from it, as the representation of the code could change at any
/// time.
/// This is an opaque code. It should not be unpacked to derive information
/// from it, as the representation of the code could change at any time.
final int keyId;
/// The debug string to print for this keyboard key, which will be null in
@ -79,7 +162,8 @@ class LogicalKeyboardKey extends Diagnosticable {
return keyId == typedOther.keyId;
}
/// Finds the [LogicalKeyboardKey] that matches the given ID.
/// Returns the [LogicalKeyboardKey] constant that matches the given ID, or
/// null, if not found.
static LogicalKeyboardKey findKeyByKeyId(int keyId) => _knownLogicalKeys[keyId];
@override
@ -131,30 +215,36 @@ class LogicalKeyboardKey extends Diagnosticable {
/// that had a "do what I mean" key from then on.
bool get isAutogenerated => (keyId & autogeneratedMask) != 0;
/// Mask for the 32-bit value portion of the key code. This is used by
/// Mask for the 32-bit value portion of the key code.
///
/// This is used by
/// platform-specific code to generate Flutter key codes.
static const int valueMask = 0x000FFFFFFFF;
/// Mask for the platform prefix portion of the key code. This is used by
/// platform-specific code to generate Flutter key codes.
/// Mask for the platform prefix portion of the key code.
///
/// This is used by platform-specific code to generate Flutter key codes.
static const int platformMask = 0x0FF00000000;
/// Mask for the autogenerated bit portion of the key code. This is used by
/// platform-specific code to generate new Flutter key codes for keys which
/// are not recognized.
/// Mask for the autogenerated bit portion of the key code.
///
/// This is used by platform-specific code to generate new Flutter key codes
/// for keys which are not recognized.
static const int autogeneratedMask = 0x10000000000;
/// The code prefix for keys which have a Unicode representation. This is used
/// by platform-specific code to generate Flutter key codes.
/// The code prefix for keys which have a Unicode representation.
///
/// This is used by platform-specific code to generate Flutter key codes.
static const int unicodePlane = 0x00000000000;
/// The code prefix for keys which do not have a Unicode representation. This
/// is used by platform-specific code to generate Flutter key codes using HID
/// Usage codes.
/// The code prefix for keys which do not have a Unicode representation.
///
/// This is used by platform-specific code to generate Flutter key codes using
/// HID Usage codes.
static const int hidPlane = 0x00100000000;
@@@LOGICAL_KEY_DEFINITIONS@@@
// A list of all predefined constant LogicalKeyboardKeys so they can be
// searched..
// searched.
static const Map<int, LogicalKeyboardKey> _knownLogicalKeys = <int, LogicalKeyboardKey>{
@@@LOGICAL_KEY_MAP@@@
};
@ -163,12 +253,88 @@ class LogicalKeyboardKey extends Diagnosticable {
/// A class with static values that describe the keys that are returned from
/// [RawKeyEvent.physicalKey].
///
/// These represent *physical* keys, which are keys which represent a
/// particular key location on the keyboard. It ignores any modifiers, modes,
/// or keyboard layouts which may be in effect. This is contrast to
/// These represent *physical* keys, which are keys which represent a particular
/// key location on a QWERTY keyboard. It ignores any modifiers, modes, or
/// keyboard layouts which may be in effect. This is contrast to
/// [LogicalKeyboardKey], which represents a logical key interpreted in the
/// context of modifiers, modes, and/or keyboard layouts.
///
/// As an example, if you wanted a game where the key next to the CAPS LOCK (the
/// "A" key on a QWERTY keyboard) moved the player to the left, you'd want to
/// look at the physical key to make sure that regardless of the character the
/// key produces, you got the key that is in that location on the keyboard.
///
/// Conversely, if you wanted to implement an app where the "Q" key "quit"
/// something, you'd want to look at the logical key to detect this, since you
/// would like to have it match the key with "Q" on it, instead of always
/// looking for "the key next next to the TAB key", since on a French keyboard,
/// the key next to the TAB key has an "A" on it.
///
/// {@tool snippet --template=stateful_widget}
/// This example shows how to detect if the user has selected the physical key
/// to the right of the CAPS LOCK key.
///
/// ```dart imports
/// import 'package:flutter/services.dart';
/// ```
///
/// ```dart
/// // The node used to request the keyboard focus.
/// final FocusNode _focusNode = FocusNode();
/// // The message to display.
/// String _message;
///
/// // Focus nodes need to be disposed.
/// @override
/// void dispose() {
/// _focusNode.dispose();
/// super.dispose();
/// }
///
/// // Handles the key events from the RawKeyboardListener and update the
/// // _message.
/// void _handleKeyEvent(RawKeyEvent event) {
/// setState(() {
/// if (event.physicalKey == PhysicalKeyboardKey.keyA) {
/// _message = 'Pressed the key next to CAPS LOCK!';
/// } else {
/// _message = 'Wrong key.';
/// }
/// });
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// final TextTheme textTheme = Theme.of(context).textTheme;
/// return Container(
/// color: Colors.white,
/// alignment: Alignment.center,
/// child: DefaultTextStyle(
/// style: textTheme.display1,
/// child: RawKeyboardListener(
/// focusNode: _focusNode,
/// onKey: _handleKeyEvent,
/// child: AnimatedBuilder(
/// animation: _focusNode,
/// builder: (BuildContext context, Widget child) {
/// if (!_focusNode.hasFocus) {
/// return GestureDetector(
/// onTap: () {
/// FocusScope.of(context).requestFocus(_focusNode);
/// },
/// child: Text('Tap to focus'),
/// );
/// }
/// return Text(_message ?? 'Press a key');
/// },
/// ),
/// ),
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [RawKeyEvent], the keyboard event object received by widgets that listen
@ -176,19 +342,20 @@ class LogicalKeyboardKey extends Diagnosticable {
/// * [RawKeyboardListener], a widget used to listen to and supply handlers for
/// keyboard events.
class PhysicalKeyboardKey extends Diagnosticable {
/// Defines a KeyboardKey value and optional debug name. The debug name must
/// be null in release mode.
/// Creates a PhysicalKeyboardKey object with an optional debug name.
///
/// To save executable size, it is recommended that the [debugName] be null in
/// release mode. You can do this using the [kReleaseMode] constant:
/// The [usbHidUsage] must not be null.
///
/// {@tool sample}
/// const PhysicalKeyboardKey mySpecialPhysicalKey = PhysicalKeyboardKey(
/// 0x0010000000a,
/// debugName: kReleaseMode ? null : 'Special Key',
/// );
/// To save executable size, it is recommended that the [debugName] be null in
/// release mode. You can do this using the [kReleaseMode] constant.
///
/// ```dart
/// const PhysicalKeyboardKey(0x0000ffff, debugName: kReleaseMode ? null : 'Special Key')
/// ```
/// {@end-tool}
const PhysicalKeyboardKey(this.usbHidUsage, {this.debugName});
const PhysicalKeyboardKey(this.usbHidUsage, {this.debugName})
: assert(usbHidUsage != null);
/// The unique USB HID usage ID of this physical key on the keyboard.
///

View file

@ -6,7 +6,7 @@
// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit dev/tools/gen_keycodes/data/keyboard_maps.tmpl instead.
// Edit the template dev/tools/gen_keycodes/data/keyboard_maps.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
import 'keyboard_key.dart';

View file

@ -15,32 +15,32 @@
"digit8": "8",
"digit9": "9",
"equal": "=",
"keyA": "A",
"keyB": "B",
"keyC": "C",
"keyD": "D",
"keyE": "E",
"keyF": "F",
"keyG": "G",
"keyH": "H",
"keyI": "I",
"keyJ": "J",
"keyK": "K",
"keyL": "L",
"keyM": "M",
"keyN": "N",
"keyO": "O",
"keyP": "P",
"keyQ": "Q",
"keyR": "R",
"keyS": "S",
"keyT": "T",
"keyU": "U",
"keyV": "V",
"keyW": "W",
"keyX": "X",
"keyY": "Y",
"keyZ": "Z",
"keyA": "a",
"keyB": "b",
"keyC": "c",
"keyD": "d",
"keyE": "e",
"keyF": "f",
"keyG": "g",
"keyH": "h",
"keyI": "i",
"keyJ": "j",
"keyK": "k",
"keyL": "l",
"keyM": "m",
"keyN": "n",
"keyO": "o",
"keyP": "p",
"keyQ": "q",
"keyR": "r",
"keyS": "s",
"keyT": "t",
"keyU": "u",
"keyV": "v",
"keyW": "w",
"keyX": "x",
"keyY": "y",
"keyZ": "z",
"minus": "-",
"numpad0": "0",
"numpad1": "1",

View file

@ -15,7 +15,7 @@ class CodeGenerator {
/// Given an [input] string, wraps the text at 80 characters and prepends each
/// line with the [prefix] string. Use for generated comments.
String wrapString(String input, String prefix) {
String wrapString(String input, {String prefix = ' /// '}) {
final int wrapWidth = 80 - prefix.length;
final StringBuffer result = StringBuffer();
final List<String> words = input.split(RegExp(r'\s+'));
@ -38,12 +38,14 @@ class CodeGenerator {
String get physicalDefinitions {
final StringBuffer definitions = StringBuffer();
for (Key entry in keyData.data) {
final String comment = wrapString('Represents the location of a '
'"${entry.commentName}" key on a generalized keyboard. See the function '
'[RawKeyEvent.physicalKey] for more information.', ' /// ');
final String firstComment = wrapString('Represents the location of the '
'"${entry.commentName}" key on a generalized keyboard.');
final String otherComments = wrapString('See the function '
'[RawKeyEvent.physicalKey] for more information.');
definitions.write('''
$comment static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeyboardKey(${toHex(entry.usbHidCode, digits: 8)}, debugName: kReleaseMode ? null : '${entry.commentName}');
$firstComment ///
$otherComments static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeyboardKey(${toHex(entry.usbHidCode, digits: 8)}, debugName: kReleaseMode ? null : '${entry.commentName}');
''');
}
return definitions.toString();
@ -54,17 +56,19 @@ $comment static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeybo
String escapeLabel(String label) => label.contains("'") ? 'r"$label"' : "r'$label'";
final StringBuffer definitions = StringBuffer();
for (Key entry in keyData.data) {
final String comment = wrapString('Represents a logical "${entry.commentName}" key on the '
'keyboard. See the function [RawKeyEvent.logicalKey] for more information.', ' /// ');
final String firstComment = wrapString('Represents the logical "${entry.commentName}" key on the keyboard.');
final String otherComments = wrapString('See the function [RawKeyEvent.logicalKey] for more information.');
if (entry.keyLabel == null) {
definitions.write('''
$comment static const LogicalKeyboardKey ${entry.constantName} = LogicalKeyboardKey(${toHex(entry.flutterId, digits: 11)}, debugName: kReleaseMode ? null : '${entry.commentName}');
$firstComment ///
$otherComments static const LogicalKeyboardKey ${entry.constantName} = LogicalKeyboardKey(${toHex(entry.flutterId, digits: 11)}, debugName: kReleaseMode ? null : '${entry.commentName}');
''');
} else {
definitions.write('''
$comment static const LogicalKeyboardKey ${entry.constantName} = LogicalKeyboardKey(${toHex(entry.flutterId, digits: 11)}, keyLabel: ${escapeLabel(entry.keyLabel)}, debugName: kReleaseMode ? null : '${entry.commentName}');
$firstComment ///
$otherComments static const LogicalKeyboardKey ${entry.constantName} = LogicalKeyboardKey(${toHex(entry.flutterId, digits: 11)}, keyLabel: ${escapeLabel(entry.keyLabel)}, debugName: kReleaseMode ? null : '${entry.commentName}');
''');
}
}

View file

@ -193,7 +193,7 @@ class KeyData {
/// A single entry in the key data structure.
///
/// Can be read from JSON with the [Key..fromJsonMapEntry] constructor, or
/// Can be read from JSON with the [Key.fromJsonMapEntry] constructor, or
/// written with the [toJson] method.
class Key {
/// Creates a single key entry from available data.

View file

@ -15,6 +15,7 @@ export 'src/services/binding.dart';
export 'src/services/clipboard.dart';
export 'src/services/font_loader.dart';
export 'src/services/haptic_feedback.dart';
export 'src/services/keyboard_key.dart';
export 'src/services/message_codec.dart';
export 'src/services/message_codecs.dart';
export 'src/services/platform_channel.dart';

View file

@ -152,6 +152,7 @@ abstract class DeletableChipAttributes {
/// have to do something similar to the following sample:
///
/// {@tool snippet --template=stateful_widget}
///
/// This sample shows how to use [onDeleted] to remove an entry when the
/// delete button is tapped.
///

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,844 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_maps.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
import 'keyboard_key.dart';
/// Maps Android-specific key codes to the matching [LogicalKeyboardKey].
const Map<int, LogicalKeyboardKey> kAndroidToLogicalKey = <int, LogicalKeyboardKey>{
0: LogicalKeyboardKey.none,
119: LogicalKeyboardKey.fn,
219: LogicalKeyboardKey.launchAssistant,
223: LogicalKeyboardKey.sleep,
224: LogicalKeyboardKey.wakeUp,
29: LogicalKeyboardKey.keyA,
30: LogicalKeyboardKey.keyB,
31: LogicalKeyboardKey.keyC,
32: LogicalKeyboardKey.keyD,
33: LogicalKeyboardKey.keyE,
34: LogicalKeyboardKey.keyF,
35: LogicalKeyboardKey.keyG,
36: LogicalKeyboardKey.keyH,
37: LogicalKeyboardKey.keyI,
38: LogicalKeyboardKey.keyJ,
39: LogicalKeyboardKey.keyK,
40: LogicalKeyboardKey.keyL,
41: LogicalKeyboardKey.keyM,
42: LogicalKeyboardKey.keyN,
43: LogicalKeyboardKey.keyO,
44: LogicalKeyboardKey.keyP,
45: LogicalKeyboardKey.keyQ,
46: LogicalKeyboardKey.keyR,
47: LogicalKeyboardKey.keyS,
48: LogicalKeyboardKey.keyT,
49: LogicalKeyboardKey.keyU,
50: LogicalKeyboardKey.keyV,
51: LogicalKeyboardKey.keyW,
52: LogicalKeyboardKey.keyX,
53: LogicalKeyboardKey.keyY,
54: LogicalKeyboardKey.keyZ,
8: LogicalKeyboardKey.digit1,
9: LogicalKeyboardKey.digit2,
10: LogicalKeyboardKey.digit3,
11: LogicalKeyboardKey.digit4,
12: LogicalKeyboardKey.digit5,
13: LogicalKeyboardKey.digit6,
14: LogicalKeyboardKey.digit7,
15: LogicalKeyboardKey.digit8,
16: LogicalKeyboardKey.digit9,
7: LogicalKeyboardKey.digit0,
66: LogicalKeyboardKey.enter,
111: LogicalKeyboardKey.escape,
67: LogicalKeyboardKey.backspace,
61: LogicalKeyboardKey.tab,
62: LogicalKeyboardKey.space,
69: LogicalKeyboardKey.minus,
70: LogicalKeyboardKey.equal,
71: LogicalKeyboardKey.bracketLeft,
72: LogicalKeyboardKey.bracketRight,
73: LogicalKeyboardKey.backslash,
74: LogicalKeyboardKey.semicolon,
75: LogicalKeyboardKey.quote,
68: LogicalKeyboardKey.backquote,
55: LogicalKeyboardKey.comma,
56: LogicalKeyboardKey.period,
76: LogicalKeyboardKey.slash,
115: LogicalKeyboardKey.capsLock,
131: LogicalKeyboardKey.f1,
132: LogicalKeyboardKey.f2,
133: LogicalKeyboardKey.f3,
134: LogicalKeyboardKey.f4,
135: LogicalKeyboardKey.f5,
136: LogicalKeyboardKey.f6,
137: LogicalKeyboardKey.f7,
138: LogicalKeyboardKey.f8,
139: LogicalKeyboardKey.f9,
140: LogicalKeyboardKey.f10,
141: LogicalKeyboardKey.f11,
142: LogicalKeyboardKey.f12,
120: LogicalKeyboardKey.printScreen,
116: LogicalKeyboardKey.scrollLock,
121: LogicalKeyboardKey.pause,
124: LogicalKeyboardKey.insert,
122: LogicalKeyboardKey.home,
92: LogicalKeyboardKey.pageUp,
112: LogicalKeyboardKey.delete,
123: LogicalKeyboardKey.end,
93: LogicalKeyboardKey.pageDown,
22: LogicalKeyboardKey.arrowRight,
21: LogicalKeyboardKey.arrowLeft,
20: LogicalKeyboardKey.arrowDown,
19: LogicalKeyboardKey.arrowUp,
143: LogicalKeyboardKey.numLock,
154: LogicalKeyboardKey.numpadDivide,
155: LogicalKeyboardKey.numpadMultiply,
156: LogicalKeyboardKey.numpadSubtract,
157: LogicalKeyboardKey.numpadAdd,
160: LogicalKeyboardKey.numpadEnter,
145: LogicalKeyboardKey.numpad1,
146: LogicalKeyboardKey.numpad2,
147: LogicalKeyboardKey.numpad3,
148: LogicalKeyboardKey.numpad4,
149: LogicalKeyboardKey.numpad5,
150: LogicalKeyboardKey.numpad6,
151: LogicalKeyboardKey.numpad7,
152: LogicalKeyboardKey.numpad8,
153: LogicalKeyboardKey.numpad9,
144: LogicalKeyboardKey.numpad0,
158: LogicalKeyboardKey.numpadDecimal,
82: LogicalKeyboardKey.contextMenu,
26: LogicalKeyboardKey.power,
161: LogicalKeyboardKey.numpadEqual,
259: LogicalKeyboardKey.help,
277: LogicalKeyboardKey.cut,
278: LogicalKeyboardKey.copy,
279: LogicalKeyboardKey.paste,
164: LogicalKeyboardKey.audioVolumeMute,
24: LogicalKeyboardKey.audioVolumeUp,
25: LogicalKeyboardKey.audioVolumeDown,
159: LogicalKeyboardKey.numpadComma,
214: LogicalKeyboardKey.convert,
213: LogicalKeyboardKey.nonConvert,
162: LogicalKeyboardKey.numpadParenLeft,
163: LogicalKeyboardKey.numpadParenRight,
113: LogicalKeyboardKey.controlLeft,
59: LogicalKeyboardKey.shiftLeft,
57: LogicalKeyboardKey.altLeft,
117: LogicalKeyboardKey.metaLeft,
114: LogicalKeyboardKey.controlRight,
60: LogicalKeyboardKey.shiftRight,
58: LogicalKeyboardKey.altRight,
118: LogicalKeyboardKey.metaRight,
165: LogicalKeyboardKey.info,
175: LogicalKeyboardKey.closedCaptionToggle,
221: LogicalKeyboardKey.brightnessUp,
220: LogicalKeyboardKey.brightnessDown,
229: LogicalKeyboardKey.mediaLast,
166: LogicalKeyboardKey.channelUp,
167: LogicalKeyboardKey.channelDown,
126: LogicalKeyboardKey.mediaPlay,
130: LogicalKeyboardKey.mediaRecord,
90: LogicalKeyboardKey.mediaFastForward,
89: LogicalKeyboardKey.mediaRewind,
87: LogicalKeyboardKey.mediaTrackNext,
88: LogicalKeyboardKey.mediaTrackPrevious,
86: LogicalKeyboardKey.mediaStop,
129: LogicalKeyboardKey.eject,
85: LogicalKeyboardKey.mediaPlayPause,
65: LogicalKeyboardKey.launchMail,
207: LogicalKeyboardKey.launchContacts,
208: LogicalKeyboardKey.launchCalendar,
128: LogicalKeyboardKey.close,
84: LogicalKeyboardKey.browserSearch,
125: LogicalKeyboardKey.browserForward,
174: LogicalKeyboardKey.browserFavorites,
168: LogicalKeyboardKey.zoomIn,
169: LogicalKeyboardKey.zoomOut,
255: LogicalKeyboardKey.zoomToggle,
};
/// Maps Android-specific scan codes to the matching [PhysicalKeyboardKey].
const Map<int, PhysicalKeyboardKey> kAndroidToPhysicalKey = <int, PhysicalKeyboardKey>{
464: PhysicalKeyboardKey.fn,
205: PhysicalKeyboardKey.suspend,
142: PhysicalKeyboardKey.sleep,
143: PhysicalKeyboardKey.wakeUp,
30: PhysicalKeyboardKey.keyA,
48: PhysicalKeyboardKey.keyB,
46: PhysicalKeyboardKey.keyC,
32: PhysicalKeyboardKey.keyD,
18: PhysicalKeyboardKey.keyE,
33: PhysicalKeyboardKey.keyF,
34: PhysicalKeyboardKey.keyG,
35: PhysicalKeyboardKey.keyH,
23: PhysicalKeyboardKey.keyI,
36: PhysicalKeyboardKey.keyJ,
37: PhysicalKeyboardKey.keyK,
38: PhysicalKeyboardKey.keyL,
50: PhysicalKeyboardKey.keyM,
49: PhysicalKeyboardKey.keyN,
24: PhysicalKeyboardKey.keyO,
25: PhysicalKeyboardKey.keyP,
16: PhysicalKeyboardKey.keyQ,
19: PhysicalKeyboardKey.keyR,
31: PhysicalKeyboardKey.keyS,
20: PhysicalKeyboardKey.keyT,
22: PhysicalKeyboardKey.keyU,
47: PhysicalKeyboardKey.keyV,
17: PhysicalKeyboardKey.keyW,
45: PhysicalKeyboardKey.keyX,
21: PhysicalKeyboardKey.keyY,
44: PhysicalKeyboardKey.keyZ,
2: PhysicalKeyboardKey.digit1,
3: PhysicalKeyboardKey.digit2,
4: PhysicalKeyboardKey.digit3,
5: PhysicalKeyboardKey.digit4,
6: PhysicalKeyboardKey.digit5,
7: PhysicalKeyboardKey.digit6,
8: PhysicalKeyboardKey.digit7,
9: PhysicalKeyboardKey.digit8,
10: PhysicalKeyboardKey.digit9,
11: PhysicalKeyboardKey.digit0,
28: PhysicalKeyboardKey.enter,
1: PhysicalKeyboardKey.escape,
14: PhysicalKeyboardKey.backspace,
15: PhysicalKeyboardKey.tab,
57: PhysicalKeyboardKey.space,
12: PhysicalKeyboardKey.minus,
13: PhysicalKeyboardKey.equal,
26: PhysicalKeyboardKey.bracketLeft,
27: PhysicalKeyboardKey.bracketRight,
43: PhysicalKeyboardKey.backslash,
86: PhysicalKeyboardKey.backslash,
39: PhysicalKeyboardKey.semicolon,
40: PhysicalKeyboardKey.quote,
41: PhysicalKeyboardKey.backquote,
51: PhysicalKeyboardKey.comma,
52: PhysicalKeyboardKey.period,
53: PhysicalKeyboardKey.slash,
58: PhysicalKeyboardKey.capsLock,
59: PhysicalKeyboardKey.f1,
60: PhysicalKeyboardKey.f2,
61: PhysicalKeyboardKey.f3,
62: PhysicalKeyboardKey.f4,
63: PhysicalKeyboardKey.f5,
64: PhysicalKeyboardKey.f6,
65: PhysicalKeyboardKey.f7,
66: PhysicalKeyboardKey.f8,
67: PhysicalKeyboardKey.f9,
68: PhysicalKeyboardKey.f10,
87: PhysicalKeyboardKey.f11,
88: PhysicalKeyboardKey.f12,
99: PhysicalKeyboardKey.printScreen,
70: PhysicalKeyboardKey.scrollLock,
119: PhysicalKeyboardKey.pause,
411: PhysicalKeyboardKey.pause,
110: PhysicalKeyboardKey.insert,
102: PhysicalKeyboardKey.home,
104: PhysicalKeyboardKey.pageUp,
177: PhysicalKeyboardKey.pageUp,
111: PhysicalKeyboardKey.delete,
107: PhysicalKeyboardKey.end,
109: PhysicalKeyboardKey.pageDown,
178: PhysicalKeyboardKey.pageDown,
106: PhysicalKeyboardKey.arrowRight,
105: PhysicalKeyboardKey.arrowLeft,
108: PhysicalKeyboardKey.arrowDown,
103: PhysicalKeyboardKey.arrowUp,
69: PhysicalKeyboardKey.numLock,
98: PhysicalKeyboardKey.numpadDivide,
55: PhysicalKeyboardKey.numpadMultiply,
74: PhysicalKeyboardKey.numpadSubtract,
78: PhysicalKeyboardKey.numpadAdd,
96: PhysicalKeyboardKey.numpadEnter,
79: PhysicalKeyboardKey.numpad1,
80: PhysicalKeyboardKey.numpad2,
81: PhysicalKeyboardKey.numpad3,
75: PhysicalKeyboardKey.numpad4,
76: PhysicalKeyboardKey.numpad5,
77: PhysicalKeyboardKey.numpad6,
71: PhysicalKeyboardKey.numpad7,
72: PhysicalKeyboardKey.numpad8,
73: PhysicalKeyboardKey.numpad9,
82: PhysicalKeyboardKey.numpad0,
83: PhysicalKeyboardKey.numpadDecimal,
127: PhysicalKeyboardKey.contextMenu,
139: PhysicalKeyboardKey.contextMenu,
116: PhysicalKeyboardKey.power,
152: PhysicalKeyboardKey.power,
117: PhysicalKeyboardKey.numpadEqual,
183: PhysicalKeyboardKey.f13,
184: PhysicalKeyboardKey.f14,
185: PhysicalKeyboardKey.f15,
186: PhysicalKeyboardKey.f16,
187: PhysicalKeyboardKey.f17,
188: PhysicalKeyboardKey.f18,
189: PhysicalKeyboardKey.f19,
190: PhysicalKeyboardKey.f20,
191: PhysicalKeyboardKey.f21,
192: PhysicalKeyboardKey.f22,
193: PhysicalKeyboardKey.f23,
194: PhysicalKeyboardKey.f24,
134: PhysicalKeyboardKey.open,
138: PhysicalKeyboardKey.help,
129: PhysicalKeyboardKey.again,
131: PhysicalKeyboardKey.undo,
137: PhysicalKeyboardKey.cut,
133: PhysicalKeyboardKey.copy,
135: PhysicalKeyboardKey.paste,
136: PhysicalKeyboardKey.find,
113: PhysicalKeyboardKey.audioVolumeMute,
115: PhysicalKeyboardKey.audioVolumeUp,
114: PhysicalKeyboardKey.audioVolumeDown,
95: PhysicalKeyboardKey.numpadComma,
121: PhysicalKeyboardKey.numpadComma,
92: PhysicalKeyboardKey.convert,
94: PhysicalKeyboardKey.nonConvert,
90: PhysicalKeyboardKey.lang3,
91: PhysicalKeyboardKey.lang4,
130: PhysicalKeyboardKey.props,
179: PhysicalKeyboardKey.numpadParenLeft,
180: PhysicalKeyboardKey.numpadParenRight,
29: PhysicalKeyboardKey.controlLeft,
42: PhysicalKeyboardKey.shiftLeft,
56: PhysicalKeyboardKey.altLeft,
125: PhysicalKeyboardKey.metaLeft,
97: PhysicalKeyboardKey.controlRight,
54: PhysicalKeyboardKey.shiftRight,
100: PhysicalKeyboardKey.altRight,
126: PhysicalKeyboardKey.metaRight,
358: PhysicalKeyboardKey.info,
225: PhysicalKeyboardKey.brightnessUp,
224: PhysicalKeyboardKey.brightnessDown,
174: PhysicalKeyboardKey.exit,
402: PhysicalKeyboardKey.channelUp,
403: PhysicalKeyboardKey.channelDown,
200: PhysicalKeyboardKey.mediaPlay,
207: PhysicalKeyboardKey.mediaPlay,
167: PhysicalKeyboardKey.mediaRecord,
208: PhysicalKeyboardKey.mediaFastForward,
168: PhysicalKeyboardKey.mediaRewind,
163: PhysicalKeyboardKey.mediaTrackNext,
165: PhysicalKeyboardKey.mediaTrackPrevious,
128: PhysicalKeyboardKey.mediaStop,
166: PhysicalKeyboardKey.mediaStop,
161: PhysicalKeyboardKey.eject,
162: PhysicalKeyboardKey.eject,
164: PhysicalKeyboardKey.mediaPlayPause,
209: PhysicalKeyboardKey.bassBoost,
155: PhysicalKeyboardKey.launchMail,
215: PhysicalKeyboardKey.launchMail,
429: PhysicalKeyboardKey.launchContacts,
397: PhysicalKeyboardKey.launchCalendar,
181: PhysicalKeyboardKey.newKey,
160: PhysicalKeyboardKey.close,
206: PhysicalKeyboardKey.close,
210: PhysicalKeyboardKey.print,
217: PhysicalKeyboardKey.browserSearch,
159: PhysicalKeyboardKey.browserForward,
156: PhysicalKeyboardKey.browserFavorites,
182: PhysicalKeyboardKey.redo,
};
/// A map of Android key codes which have printable representations, but appear
/// on the number pad. Used to provide different key objects for keys like
/// KEY_EQUALS and NUMPAD_EQUALS.
const Map<int, LogicalKeyboardKey> kAndroidNumPadMap = <int, LogicalKeyboardKey>{
154: LogicalKeyboardKey.numpadDivide,
155: LogicalKeyboardKey.numpadMultiply,
156: LogicalKeyboardKey.numpadSubtract,
157: LogicalKeyboardKey.numpadAdd,
145: LogicalKeyboardKey.numpad1,
146: LogicalKeyboardKey.numpad2,
147: LogicalKeyboardKey.numpad3,
148: LogicalKeyboardKey.numpad4,
149: LogicalKeyboardKey.numpad5,
150: LogicalKeyboardKey.numpad6,
151: LogicalKeyboardKey.numpad7,
152: LogicalKeyboardKey.numpad8,
153: LogicalKeyboardKey.numpad9,
144: LogicalKeyboardKey.numpad0,
158: LogicalKeyboardKey.numpadDecimal,
161: LogicalKeyboardKey.numpadEqual,
159: LogicalKeyboardKey.numpadComma,
162: LogicalKeyboardKey.numpadParenLeft,
163: LogicalKeyboardKey.numpadParenRight,
};
/// Maps Fuchsia-specific IDs to the matching [LogicalKeyboardKey].
const Map<int, LogicalKeyboardKey> kFuchsiaToLogicalKey = <int, LogicalKeyboardKey>{
0x100000000: LogicalKeyboardKey.none,
0x100000010: LogicalKeyboardKey.hyper,
0x100000011: LogicalKeyboardKey.superKey,
0x100000012: LogicalKeyboardKey.fn,
0x100000013: LogicalKeyboardKey.fnLock,
0x100000014: LogicalKeyboardKey.suspend,
0x100000015: LogicalKeyboardKey.resume,
0x100000016: LogicalKeyboardKey.turbo,
0x100000017: LogicalKeyboardKey.launchAssistant,
0x100010082: LogicalKeyboardKey.sleep,
0x100010083: LogicalKeyboardKey.wakeUp,
0x100070000: LogicalKeyboardKey.usbReserved,
0x100070001: LogicalKeyboardKey.usbErrorRollOver,
0x100070002: LogicalKeyboardKey.usbPostFail,
0x100070003: LogicalKeyboardKey.usbErrorUndefined,
0x00000061: LogicalKeyboardKey.keyA,
0x00000062: LogicalKeyboardKey.keyB,
0x00000063: LogicalKeyboardKey.keyC,
0x00000064: LogicalKeyboardKey.keyD,
0x00000065: LogicalKeyboardKey.keyE,
0x00000066: LogicalKeyboardKey.keyF,
0x00000067: LogicalKeyboardKey.keyG,
0x00000068: LogicalKeyboardKey.keyH,
0x00000069: LogicalKeyboardKey.keyI,
0x0000006a: LogicalKeyboardKey.keyJ,
0x0000006b: LogicalKeyboardKey.keyK,
0x0000006c: LogicalKeyboardKey.keyL,
0x0000006d: LogicalKeyboardKey.keyM,
0x0000006e: LogicalKeyboardKey.keyN,
0x0000006f: LogicalKeyboardKey.keyO,
0x00000070: LogicalKeyboardKey.keyP,
0x00000071: LogicalKeyboardKey.keyQ,
0x00000072: LogicalKeyboardKey.keyR,
0x00000073: LogicalKeyboardKey.keyS,
0x00000074: LogicalKeyboardKey.keyT,
0x00000075: LogicalKeyboardKey.keyU,
0x00000076: LogicalKeyboardKey.keyV,
0x00000077: LogicalKeyboardKey.keyW,
0x00000078: LogicalKeyboardKey.keyX,
0x00000079: LogicalKeyboardKey.keyY,
0x0000007a: LogicalKeyboardKey.keyZ,
0x00000031: LogicalKeyboardKey.digit1,
0x00000032: LogicalKeyboardKey.digit2,
0x00000033: LogicalKeyboardKey.digit3,
0x00000034: LogicalKeyboardKey.digit4,
0x00000035: LogicalKeyboardKey.digit5,
0x00000036: LogicalKeyboardKey.digit6,
0x00000037: LogicalKeyboardKey.digit7,
0x00000038: LogicalKeyboardKey.digit8,
0x00000039: LogicalKeyboardKey.digit9,
0x00000030: LogicalKeyboardKey.digit0,
0x100070028: LogicalKeyboardKey.enter,
0x100070029: LogicalKeyboardKey.escape,
0x10007002a: LogicalKeyboardKey.backspace,
0x10007002b: LogicalKeyboardKey.tab,
0x00000020: LogicalKeyboardKey.space,
0x0000002d: LogicalKeyboardKey.minus,
0x0000003d: LogicalKeyboardKey.equal,
0x0000005b: LogicalKeyboardKey.bracketLeft,
0x0000005d: LogicalKeyboardKey.bracketRight,
0x0000005c: LogicalKeyboardKey.backslash,
0x0000003b: LogicalKeyboardKey.semicolon,
0x00000027: LogicalKeyboardKey.quote,
0x00000060: LogicalKeyboardKey.backquote,
0x0000002c: LogicalKeyboardKey.comma,
0x0000002e: LogicalKeyboardKey.period,
0x0000002f: LogicalKeyboardKey.slash,
0x100070039: LogicalKeyboardKey.capsLock,
0x10007003a: LogicalKeyboardKey.f1,
0x10007003b: LogicalKeyboardKey.f2,
0x10007003c: LogicalKeyboardKey.f3,
0x10007003d: LogicalKeyboardKey.f4,
0x10007003e: LogicalKeyboardKey.f5,
0x10007003f: LogicalKeyboardKey.f6,
0x100070040: LogicalKeyboardKey.f7,
0x100070041: LogicalKeyboardKey.f8,
0x100070042: LogicalKeyboardKey.f9,
0x100070043: LogicalKeyboardKey.f10,
0x100070044: LogicalKeyboardKey.f11,
0x100070045: LogicalKeyboardKey.f12,
0x100070046: LogicalKeyboardKey.printScreen,
0x100070047: LogicalKeyboardKey.scrollLock,
0x100070048: LogicalKeyboardKey.pause,
0x100070049: LogicalKeyboardKey.insert,
0x10007004a: LogicalKeyboardKey.home,
0x10007004b: LogicalKeyboardKey.pageUp,
0x10007004c: LogicalKeyboardKey.delete,
0x10007004d: LogicalKeyboardKey.end,
0x10007004e: LogicalKeyboardKey.pageDown,
0x10007004f: LogicalKeyboardKey.arrowRight,
0x100070050: LogicalKeyboardKey.arrowLeft,
0x100070051: LogicalKeyboardKey.arrowDown,
0x100070052: LogicalKeyboardKey.arrowUp,
0x100070053: LogicalKeyboardKey.numLock,
0x100070054: LogicalKeyboardKey.numpadDivide,
0x100070055: LogicalKeyboardKey.numpadMultiply,
0x100070056: LogicalKeyboardKey.numpadSubtract,
0x100070057: LogicalKeyboardKey.numpadAdd,
0x100070058: LogicalKeyboardKey.numpadEnter,
0x100070059: LogicalKeyboardKey.numpad1,
0x10007005a: LogicalKeyboardKey.numpad2,
0x10007005b: LogicalKeyboardKey.numpad3,
0x10007005c: LogicalKeyboardKey.numpad4,
0x10007005d: LogicalKeyboardKey.numpad5,
0x10007005e: LogicalKeyboardKey.numpad6,
0x10007005f: LogicalKeyboardKey.numpad7,
0x100070060: LogicalKeyboardKey.numpad8,
0x100070061: LogicalKeyboardKey.numpad9,
0x100070062: LogicalKeyboardKey.numpad0,
0x100070063: LogicalKeyboardKey.numpadDecimal,
0x100070064: LogicalKeyboardKey.intlBackslash,
0x100070065: LogicalKeyboardKey.contextMenu,
0x100070066: LogicalKeyboardKey.power,
0x100070067: LogicalKeyboardKey.numpadEqual,
0x100070068: LogicalKeyboardKey.f13,
0x100070069: LogicalKeyboardKey.f14,
0x10007006a: LogicalKeyboardKey.f15,
0x10007006b: LogicalKeyboardKey.f16,
0x10007006c: LogicalKeyboardKey.f17,
0x10007006d: LogicalKeyboardKey.f18,
0x10007006e: LogicalKeyboardKey.f19,
0x10007006f: LogicalKeyboardKey.f20,
0x100070070: LogicalKeyboardKey.f21,
0x100070071: LogicalKeyboardKey.f22,
0x100070072: LogicalKeyboardKey.f23,
0x100070073: LogicalKeyboardKey.f24,
0x100070074: LogicalKeyboardKey.open,
0x100070075: LogicalKeyboardKey.help,
0x100070077: LogicalKeyboardKey.select,
0x100070079: LogicalKeyboardKey.again,
0x10007007a: LogicalKeyboardKey.undo,
0x10007007b: LogicalKeyboardKey.cut,
0x10007007c: LogicalKeyboardKey.copy,
0x10007007d: LogicalKeyboardKey.paste,
0x10007007e: LogicalKeyboardKey.find,
0x10007007f: LogicalKeyboardKey.audioVolumeMute,
0x100070080: LogicalKeyboardKey.audioVolumeUp,
0x100070081: LogicalKeyboardKey.audioVolumeDown,
0x100070085: LogicalKeyboardKey.numpadComma,
0x100070087: LogicalKeyboardKey.intlRo,
0x100070088: LogicalKeyboardKey.kanaMode,
0x100070089: LogicalKeyboardKey.intlYen,
0x10007008a: LogicalKeyboardKey.convert,
0x10007008b: LogicalKeyboardKey.nonConvert,
0x100070090: LogicalKeyboardKey.lang1,
0x100070091: LogicalKeyboardKey.lang2,
0x100070092: LogicalKeyboardKey.lang3,
0x100070093: LogicalKeyboardKey.lang4,
0x100070094: LogicalKeyboardKey.lang5,
0x10007009b: LogicalKeyboardKey.abort,
0x1000700a3: LogicalKeyboardKey.props,
0x1000700b6: LogicalKeyboardKey.numpadParenLeft,
0x1000700b7: LogicalKeyboardKey.numpadParenRight,
0x1000700bb: LogicalKeyboardKey.numpadBackspace,
0x1000700d0: LogicalKeyboardKey.numpadMemoryStore,
0x1000700d1: LogicalKeyboardKey.numpadMemoryRecall,
0x1000700d2: LogicalKeyboardKey.numpadMemoryClear,
0x1000700d3: LogicalKeyboardKey.numpadMemoryAdd,
0x1000700d4: LogicalKeyboardKey.numpadMemorySubtract,
0x1000700d7: LogicalKeyboardKey.numpadSignChange,
0x1000700d8: LogicalKeyboardKey.numpadClear,
0x1000700d9: LogicalKeyboardKey.numpadClearEntry,
0x1000700e0: LogicalKeyboardKey.controlLeft,
0x1000700e1: LogicalKeyboardKey.shiftLeft,
0x1000700e2: LogicalKeyboardKey.altLeft,
0x1000700e3: LogicalKeyboardKey.metaLeft,
0x1000700e4: LogicalKeyboardKey.controlRight,
0x1000700e5: LogicalKeyboardKey.shiftRight,
0x1000700e6: LogicalKeyboardKey.altRight,
0x1000700e7: LogicalKeyboardKey.metaRight,
0x1000c0060: LogicalKeyboardKey.info,
0x1000c0061: LogicalKeyboardKey.closedCaptionToggle,
0x1000c006f: LogicalKeyboardKey.brightnessUp,
0x1000c0070: LogicalKeyboardKey.brightnessDown,
0x1000c0072: LogicalKeyboardKey.brightnessToggle,
0x1000c0073: LogicalKeyboardKey.brightnessMinimum,
0x1000c0074: LogicalKeyboardKey.brightnessMaximum,
0x1000c0075: LogicalKeyboardKey.brightnessAuto,
0x1000c0083: LogicalKeyboardKey.mediaLast,
0x1000c008c: LogicalKeyboardKey.launchPhone,
0x1000c008d: LogicalKeyboardKey.programGuide,
0x1000c0094: LogicalKeyboardKey.exit,
0x1000c009c: LogicalKeyboardKey.channelUp,
0x1000c009d: LogicalKeyboardKey.channelDown,
0x1000c00b0: LogicalKeyboardKey.mediaPlay,
0x1000c00b2: LogicalKeyboardKey.mediaRecord,
0x1000c00b3: LogicalKeyboardKey.mediaFastForward,
0x1000c00b4: LogicalKeyboardKey.mediaRewind,
0x1000c00b5: LogicalKeyboardKey.mediaTrackNext,
0x1000c00b6: LogicalKeyboardKey.mediaTrackPrevious,
0x1000c00b7: LogicalKeyboardKey.mediaStop,
0x1000c00b8: LogicalKeyboardKey.eject,
0x1000c00cd: LogicalKeyboardKey.mediaPlayPause,
0x1000c00cf: LogicalKeyboardKey.speechInputToggle,
0x1000c00e5: LogicalKeyboardKey.bassBoost,
0x1000c0183: LogicalKeyboardKey.mediaSelect,
0x1000c0184: LogicalKeyboardKey.launchWordProcessor,
0x1000c0186: LogicalKeyboardKey.launchSpreadsheet,
0x1000c018a: LogicalKeyboardKey.launchMail,
0x1000c018d: LogicalKeyboardKey.launchContacts,
0x1000c018e: LogicalKeyboardKey.launchCalendar,
0x1000c0192: LogicalKeyboardKey.launchApp2,
0x1000c0194: LogicalKeyboardKey.launchApp1,
0x1000c0196: LogicalKeyboardKey.launchInternetBrowser,
0x1000c019c: LogicalKeyboardKey.logOff,
0x1000c019e: LogicalKeyboardKey.lockScreen,
0x1000c019f: LogicalKeyboardKey.launchControlPanel,
0x1000c01a2: LogicalKeyboardKey.selectTask,
0x1000c01a7: LogicalKeyboardKey.launchDocuments,
0x1000c01ab: LogicalKeyboardKey.spellCheck,
0x1000c01ae: LogicalKeyboardKey.launchKeyboardLayout,
0x1000c01b1: LogicalKeyboardKey.launchScreenSaver,
0x1000c01b7: LogicalKeyboardKey.launchAudioBrowser,
0x1000c0201: LogicalKeyboardKey.newKey,
0x1000c0203: LogicalKeyboardKey.close,
0x1000c0207: LogicalKeyboardKey.save,
0x1000c0208: LogicalKeyboardKey.print,
0x1000c0221: LogicalKeyboardKey.browserSearch,
0x1000c0223: LogicalKeyboardKey.browserHome,
0x1000c0224: LogicalKeyboardKey.browserBack,
0x1000c0225: LogicalKeyboardKey.browserForward,
0x1000c0226: LogicalKeyboardKey.browserStop,
0x1000c0227: LogicalKeyboardKey.browserRefresh,
0x1000c022a: LogicalKeyboardKey.browserFavorites,
0x1000c022d: LogicalKeyboardKey.zoomIn,
0x1000c022e: LogicalKeyboardKey.zoomOut,
0x1000c0232: LogicalKeyboardKey.zoomToggle,
0x1000c0279: LogicalKeyboardKey.redo,
0x1000c0289: LogicalKeyboardKey.mailReply,
0x1000c028b: LogicalKeyboardKey.mailForward,
0x1000c028c: LogicalKeyboardKey.mailSend,
};
/// Maps Fuchsia-specific USB HID Usage IDs to the matching
/// [PhysicalKeyboardKey].
const Map<int, PhysicalKeyboardKey> kFuchsiaToPhysicalKey = <int, PhysicalKeyboardKey>{
0x00000000: PhysicalKeyboardKey.none,
0x00000010: PhysicalKeyboardKey.hyper,
0x00000011: PhysicalKeyboardKey.superKey,
0x00000012: PhysicalKeyboardKey.fn,
0x00000013: PhysicalKeyboardKey.fnLock,
0x00000014: PhysicalKeyboardKey.suspend,
0x00000015: PhysicalKeyboardKey.resume,
0x00000016: PhysicalKeyboardKey.turbo,
0x00000017: PhysicalKeyboardKey.launchAssistant,
0x00010082: PhysicalKeyboardKey.sleep,
0x00010083: PhysicalKeyboardKey.wakeUp,
0x00070000: PhysicalKeyboardKey.usbReserved,
0x00070001: PhysicalKeyboardKey.usbErrorRollOver,
0x00070002: PhysicalKeyboardKey.usbPostFail,
0x00070003: PhysicalKeyboardKey.usbErrorUndefined,
0x00070004: PhysicalKeyboardKey.keyA,
0x00070005: PhysicalKeyboardKey.keyB,
0x00070006: PhysicalKeyboardKey.keyC,
0x00070007: PhysicalKeyboardKey.keyD,
0x00070008: PhysicalKeyboardKey.keyE,
0x00070009: PhysicalKeyboardKey.keyF,
0x0007000a: PhysicalKeyboardKey.keyG,
0x0007000b: PhysicalKeyboardKey.keyH,
0x0007000c: PhysicalKeyboardKey.keyI,
0x0007000d: PhysicalKeyboardKey.keyJ,
0x0007000e: PhysicalKeyboardKey.keyK,
0x0007000f: PhysicalKeyboardKey.keyL,
0x00070010: PhysicalKeyboardKey.keyM,
0x00070011: PhysicalKeyboardKey.keyN,
0x00070012: PhysicalKeyboardKey.keyO,
0x00070013: PhysicalKeyboardKey.keyP,
0x00070014: PhysicalKeyboardKey.keyQ,
0x00070015: PhysicalKeyboardKey.keyR,
0x00070016: PhysicalKeyboardKey.keyS,
0x00070017: PhysicalKeyboardKey.keyT,
0x00070018: PhysicalKeyboardKey.keyU,
0x00070019: PhysicalKeyboardKey.keyV,
0x0007001a: PhysicalKeyboardKey.keyW,
0x0007001b: PhysicalKeyboardKey.keyX,
0x0007001c: PhysicalKeyboardKey.keyY,
0x0007001d: PhysicalKeyboardKey.keyZ,
0x0007001e: PhysicalKeyboardKey.digit1,
0x0007001f: PhysicalKeyboardKey.digit2,
0x00070020: PhysicalKeyboardKey.digit3,
0x00070021: PhysicalKeyboardKey.digit4,
0x00070022: PhysicalKeyboardKey.digit5,
0x00070023: PhysicalKeyboardKey.digit6,
0x00070024: PhysicalKeyboardKey.digit7,
0x00070025: PhysicalKeyboardKey.digit8,
0x00070026: PhysicalKeyboardKey.digit9,
0x00070027: PhysicalKeyboardKey.digit0,
0x00070028: PhysicalKeyboardKey.enter,
0x00070029: PhysicalKeyboardKey.escape,
0x0007002a: PhysicalKeyboardKey.backspace,
0x0007002b: PhysicalKeyboardKey.tab,
0x0007002c: PhysicalKeyboardKey.space,
0x0007002d: PhysicalKeyboardKey.minus,
0x0007002e: PhysicalKeyboardKey.equal,
0x0007002f: PhysicalKeyboardKey.bracketLeft,
0x00070030: PhysicalKeyboardKey.bracketRight,
0x00070031: PhysicalKeyboardKey.backslash,
0x00070033: PhysicalKeyboardKey.semicolon,
0x00070034: PhysicalKeyboardKey.quote,
0x00070035: PhysicalKeyboardKey.backquote,
0x00070036: PhysicalKeyboardKey.comma,
0x00070037: PhysicalKeyboardKey.period,
0x00070038: PhysicalKeyboardKey.slash,
0x00070039: PhysicalKeyboardKey.capsLock,
0x0007003a: PhysicalKeyboardKey.f1,
0x0007003b: PhysicalKeyboardKey.f2,
0x0007003c: PhysicalKeyboardKey.f3,
0x0007003d: PhysicalKeyboardKey.f4,
0x0007003e: PhysicalKeyboardKey.f5,
0x0007003f: PhysicalKeyboardKey.f6,
0x00070040: PhysicalKeyboardKey.f7,
0x00070041: PhysicalKeyboardKey.f8,
0x00070042: PhysicalKeyboardKey.f9,
0x00070043: PhysicalKeyboardKey.f10,
0x00070044: PhysicalKeyboardKey.f11,
0x00070045: PhysicalKeyboardKey.f12,
0x00070046: PhysicalKeyboardKey.printScreen,
0x00070047: PhysicalKeyboardKey.scrollLock,
0x00070048: PhysicalKeyboardKey.pause,
0x00070049: PhysicalKeyboardKey.insert,
0x0007004a: PhysicalKeyboardKey.home,
0x0007004b: PhysicalKeyboardKey.pageUp,
0x0007004c: PhysicalKeyboardKey.delete,
0x0007004d: PhysicalKeyboardKey.end,
0x0007004e: PhysicalKeyboardKey.pageDown,
0x0007004f: PhysicalKeyboardKey.arrowRight,
0x00070050: PhysicalKeyboardKey.arrowLeft,
0x00070051: PhysicalKeyboardKey.arrowDown,
0x00070052: PhysicalKeyboardKey.arrowUp,
0x00070053: PhysicalKeyboardKey.numLock,
0x00070054: PhysicalKeyboardKey.numpadDivide,
0x00070055: PhysicalKeyboardKey.numpadMultiply,
0x00070056: PhysicalKeyboardKey.numpadSubtract,
0x00070057: PhysicalKeyboardKey.numpadAdd,
0x00070058: PhysicalKeyboardKey.numpadEnter,
0x00070059: PhysicalKeyboardKey.numpad1,
0x0007005a: PhysicalKeyboardKey.numpad2,
0x0007005b: PhysicalKeyboardKey.numpad3,
0x0007005c: PhysicalKeyboardKey.numpad4,
0x0007005d: PhysicalKeyboardKey.numpad5,
0x0007005e: PhysicalKeyboardKey.numpad6,
0x0007005f: PhysicalKeyboardKey.numpad7,
0x00070060: PhysicalKeyboardKey.numpad8,
0x00070061: PhysicalKeyboardKey.numpad9,
0x00070062: PhysicalKeyboardKey.numpad0,
0x00070063: PhysicalKeyboardKey.numpadDecimal,
0x00070064: PhysicalKeyboardKey.intlBackslash,
0x00070065: PhysicalKeyboardKey.contextMenu,
0x00070066: PhysicalKeyboardKey.power,
0x00070067: PhysicalKeyboardKey.numpadEqual,
0x00070068: PhysicalKeyboardKey.f13,
0x00070069: PhysicalKeyboardKey.f14,
0x0007006a: PhysicalKeyboardKey.f15,
0x0007006b: PhysicalKeyboardKey.f16,
0x0007006c: PhysicalKeyboardKey.f17,
0x0007006d: PhysicalKeyboardKey.f18,
0x0007006e: PhysicalKeyboardKey.f19,
0x0007006f: PhysicalKeyboardKey.f20,
0x00070070: PhysicalKeyboardKey.f21,
0x00070071: PhysicalKeyboardKey.f22,
0x00070072: PhysicalKeyboardKey.f23,
0x00070073: PhysicalKeyboardKey.f24,
0x00070074: PhysicalKeyboardKey.open,
0x00070075: PhysicalKeyboardKey.help,
0x00070077: PhysicalKeyboardKey.select,
0x00070079: PhysicalKeyboardKey.again,
0x0007007a: PhysicalKeyboardKey.undo,
0x0007007b: PhysicalKeyboardKey.cut,
0x0007007c: PhysicalKeyboardKey.copy,
0x0007007d: PhysicalKeyboardKey.paste,
0x0007007e: PhysicalKeyboardKey.find,
0x0007007f: PhysicalKeyboardKey.audioVolumeMute,
0x00070080: PhysicalKeyboardKey.audioVolumeUp,
0x00070081: PhysicalKeyboardKey.audioVolumeDown,
0x00070085: PhysicalKeyboardKey.numpadComma,
0x00070087: PhysicalKeyboardKey.intlRo,
0x00070088: PhysicalKeyboardKey.kanaMode,
0x00070089: PhysicalKeyboardKey.intlYen,
0x0007008a: PhysicalKeyboardKey.convert,
0x0007008b: PhysicalKeyboardKey.nonConvert,
0x00070090: PhysicalKeyboardKey.lang1,
0x00070091: PhysicalKeyboardKey.lang2,
0x00070092: PhysicalKeyboardKey.lang3,
0x00070093: PhysicalKeyboardKey.lang4,
0x00070094: PhysicalKeyboardKey.lang5,
0x0007009b: PhysicalKeyboardKey.abort,
0x000700a3: PhysicalKeyboardKey.props,
0x000700b6: PhysicalKeyboardKey.numpadParenLeft,
0x000700b7: PhysicalKeyboardKey.numpadParenRight,
0x000700bb: PhysicalKeyboardKey.numpadBackspace,
0x000700d0: PhysicalKeyboardKey.numpadMemoryStore,
0x000700d1: PhysicalKeyboardKey.numpadMemoryRecall,
0x000700d2: PhysicalKeyboardKey.numpadMemoryClear,
0x000700d3: PhysicalKeyboardKey.numpadMemoryAdd,
0x000700d4: PhysicalKeyboardKey.numpadMemorySubtract,
0x000700d7: PhysicalKeyboardKey.numpadSignChange,
0x000700d8: PhysicalKeyboardKey.numpadClear,
0x000700d9: PhysicalKeyboardKey.numpadClearEntry,
0x000700e0: PhysicalKeyboardKey.controlLeft,
0x000700e1: PhysicalKeyboardKey.shiftLeft,
0x000700e2: PhysicalKeyboardKey.altLeft,
0x000700e3: PhysicalKeyboardKey.metaLeft,
0x000700e4: PhysicalKeyboardKey.controlRight,
0x000700e5: PhysicalKeyboardKey.shiftRight,
0x000700e6: PhysicalKeyboardKey.altRight,
0x000700e7: PhysicalKeyboardKey.metaRight,
0x000c0060: PhysicalKeyboardKey.info,
0x000c0061: PhysicalKeyboardKey.closedCaptionToggle,
0x000c006f: PhysicalKeyboardKey.brightnessUp,
0x000c0070: PhysicalKeyboardKey.brightnessDown,
0x000c0072: PhysicalKeyboardKey.brightnessToggle,
0x000c0073: PhysicalKeyboardKey.brightnessMinimum,
0x000c0074: PhysicalKeyboardKey.brightnessMaximum,
0x000c0075: PhysicalKeyboardKey.brightnessAuto,
0x000c0083: PhysicalKeyboardKey.mediaLast,
0x000c008c: PhysicalKeyboardKey.launchPhone,
0x000c008d: PhysicalKeyboardKey.programGuide,
0x000c0094: PhysicalKeyboardKey.exit,
0x000c009c: PhysicalKeyboardKey.channelUp,
0x000c009d: PhysicalKeyboardKey.channelDown,
0x000c00b0: PhysicalKeyboardKey.mediaPlay,
0x000c00b2: PhysicalKeyboardKey.mediaRecord,
0x000c00b3: PhysicalKeyboardKey.mediaFastForward,
0x000c00b4: PhysicalKeyboardKey.mediaRewind,
0x000c00b5: PhysicalKeyboardKey.mediaTrackNext,
0x000c00b6: PhysicalKeyboardKey.mediaTrackPrevious,
0x000c00b7: PhysicalKeyboardKey.mediaStop,
0x000c00b8: PhysicalKeyboardKey.eject,
0x000c00cd: PhysicalKeyboardKey.mediaPlayPause,
0x000c00cf: PhysicalKeyboardKey.speechInputToggle,
0x000c00e5: PhysicalKeyboardKey.bassBoost,
0x000c0183: PhysicalKeyboardKey.mediaSelect,
0x000c0184: PhysicalKeyboardKey.launchWordProcessor,
0x000c0186: PhysicalKeyboardKey.launchSpreadsheet,
0x000c018a: PhysicalKeyboardKey.launchMail,
0x000c018d: PhysicalKeyboardKey.launchContacts,
0x000c018e: PhysicalKeyboardKey.launchCalendar,
0x000c0192: PhysicalKeyboardKey.launchApp2,
0x000c0194: PhysicalKeyboardKey.launchApp1,
0x000c0196: PhysicalKeyboardKey.launchInternetBrowser,
0x000c019c: PhysicalKeyboardKey.logOff,
0x000c019e: PhysicalKeyboardKey.lockScreen,
0x000c019f: PhysicalKeyboardKey.launchControlPanel,
0x000c01a2: PhysicalKeyboardKey.selectTask,
0x000c01a7: PhysicalKeyboardKey.launchDocuments,
0x000c01ab: PhysicalKeyboardKey.spellCheck,
0x000c01ae: PhysicalKeyboardKey.launchKeyboardLayout,
0x000c01b1: PhysicalKeyboardKey.launchScreenSaver,
0x000c01b7: PhysicalKeyboardKey.launchAudioBrowser,
0x000c0201: PhysicalKeyboardKey.newKey,
0x000c0203: PhysicalKeyboardKey.close,
0x000c0207: PhysicalKeyboardKey.save,
0x000c0208: PhysicalKeyboardKey.print,
0x000c0221: PhysicalKeyboardKey.browserSearch,
0x000c0223: PhysicalKeyboardKey.browserHome,
0x000c0224: PhysicalKeyboardKey.browserBack,
0x000c0225: PhysicalKeyboardKey.browserForward,
0x000c0226: PhysicalKeyboardKey.browserStop,
0x000c0227: PhysicalKeyboardKey.browserRefresh,
0x000c022a: PhysicalKeyboardKey.browserFavorites,
0x000c022d: PhysicalKeyboardKey.zoomIn,
0x000c022e: PhysicalKeyboardKey.zoomOut,
0x000c0232: PhysicalKeyboardKey.zoomToggle,
0x000c0279: PhysicalKeyboardKey.redo,
0x000c0289: PhysicalKeyboardKey.mailReply,
0x000c028b: PhysicalKeyboardKey.mailForward,
0x000c028c: PhysicalKeyboardKey.mailSend,
};

View file

@ -6,6 +6,7 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'raw_keyboard_android.dart';
import 'raw_keyboard_fuschia.dart';
import 'system_channels.dart';
@ -166,16 +167,63 @@ abstract class RawKeyEventData {
}
return result;
}
/// Returns an object representing the physical location of this key on a
/// QWERTY keyboard.
///
/// {@macro flutter.services.RawKeyEvent.physicalKey}
///
/// See also:
///
/// * [logicalKey] for the non-location-specific key generated by this event.
/// * [RawKeyEvent.physicalKey], where this value is available on the event.
PhysicalKeyboardKey get physicalKey;
/// Returns an object representing the logical key that was pressed.
///
/// {@macro flutter.services.RawKeyEvent.logicalKey}
///
/// See also:
///
/// * [physicalKey] for the location-specific key generated by this event.
/// * [RawKeyEvent.logicalKey], where this value is available on the event.
LogicalKeyboardKey get logicalKey;
/// Returns the Unicode string representing the label on this key.
///
/// {@template flutter.services.RawKeyEventData.keyLabel}
/// Do not use the [keyLabel] to compose a text string: it will be missing
/// special processing for Unicode strings for combining characters and other
/// special characters, and the effects of modifiers.
///
/// If you are looking for the character produced by a key event, use
/// [RawKeyEvent.character] instead.
///
/// If you are composing text strings, use the [TextField] or
/// [CupertinoTextField] widgets, since those automatically handle many of the
/// complexities of managing keyboard input, like showing a soft keyboard or
/// interacting with an input method editor (IME).
/// {@endtemplate}
String get keyLabel;
}
/// Base class for raw key events.
/// Defines the interface for raw key events.
///
/// Raw key events pass through as much information as possible from the
/// underlying platform's key events, which allows them to provide a high level
/// of fidelity but a low level of portability.
///
/// The event also provides an abstraction for the [physicalKey] and the
/// [logicalKey], describing the physical location of the key, and the logical
/// meaning of the key, respectively. These are more portable representations of
/// the key events, and should produce the same results regardless of platform.
///
/// See also:
///
/// * [LogicalKeyboardKey], an object that describes the logical meaning of a
/// key.
/// * [PhysicalKeyboardKey], an object that describes the physical location of
/// a key.
/// * [RawKeyDownEvent], a specialization for events representing the user
/// pressing a key.
/// * [RawKeyUpEvent], a specialization for events representing the user
@ -184,9 +232,11 @@ abstract class RawKeyEventData {
/// * [RawKeyboardListener], a widget that listens for raw key events.
@immutable
abstract class RawKeyEvent {
/// Initializes fields for subclasses.
/// Initializes fields for subclasses, and provides a const constructor for
/// const subclasses.
const RawKeyEvent({
@required this.data,
@required this.character,
});
/// Creates a concrete [RawKeyEvent] class from a message in the form received
@ -195,6 +245,7 @@ abstract class RawKeyEvent {
RawKeyEventData data;
final String keymap = message['keymap'];
final String character = message['character'];
switch (keymap) {
case 'android':
data = RawKeyEventDataAndroid(
@ -222,14 +273,119 @@ abstract class RawKeyEvent {
final String type = message['type'];
switch (type) {
case 'keydown':
return RawKeyDownEvent(data: data);
return RawKeyDownEvent(data: data, character: character);
case 'keyup':
return RawKeyUpEvent(data: data);
return RawKeyUpEvent(data: data, character: character);
default:
throw FlutterError('Unknown key event type: $type');
}
}
/// Returns true if the given [KeyboardKey] is pressed.
bool isKeyPressed(LogicalKeyboardKey key) => RawKeyboard.instance.keysPressed.contains(key);
/// Returns true if a CTRL modifier key is pressed, regardless of which side
/// of the keyboard it is on.
///
/// Use [isKeyPressed] if you need to know which control key was pressed.
bool get isControlPressed {
return isKeyPressed(LogicalKeyboardKey.controlLeft) || isKeyPressed(LogicalKeyboardKey.controlRight);
}
/// Returns true if a SHIFT modifier key is pressed, regardless of which side
/// of the keyboard it is on.
///
/// Use [isKeyPressed] if you need to know which shift key was pressed.
bool get isShiftPressed {
return isKeyPressed(LogicalKeyboardKey.shiftLeft) || isKeyPressed(LogicalKeyboardKey.shiftRight);
}
/// Returns true if a ALT modifier key is pressed, regardless of which side
/// of the keyboard it is on.
///
/// Note that the ALTGR key that appears on some keyboards is considered to be
/// the same as [LogicalKeyboardKey.altRight] on some platforms (notably
/// Android). On platforms that can distinguish between `altRight` and
/// `altGr`, a press of `altGr` will not return true here, and will need to be
/// tested for separately.
///
/// Use [isKeyPressed] if you need to know which alt key was pressed.
bool get isAltPressed {
return isKeyPressed(LogicalKeyboardKey.altLeft) || isKeyPressed(LogicalKeyboardKey.altRight);
}
/// Returns true if a META modifier key is pressed, regardless of which side
/// of the keyboard it is on.
///
/// Use [isKeyPressed] if you need to know which meta key was pressed.
bool get isMetaPressed {
return isKeyPressed(LogicalKeyboardKey.metaLeft) || isKeyPressed(LogicalKeyboardKey.metaRight);
}
/// Returns an object representing the physical location of this key.
///
/// {@template flutter.services.RawKeyEvent.physicalKey}
/// The [PhysicalKeyboardKey] ignores the key map, modifier keys (like SHIFT),
/// and the label on the key. It describes the location of the key as if it
/// were on a QWERTY keyboard regardless of the keyboard mapping in effect.
///
/// [PhysicalKeyboardKey]s are used to describe and test for keys in a
/// particular location.
///
/// For instance, if you wanted to make a game where the key to the right of
/// the CAPS LOCK key made the player move left, you would be comparing the
/// result of this `physicalKey` with [PhysicalKeyboardKey.keyA], since that
/// is the key next to the CAPS LOCK key on a QWERTY keyboard. This would
/// return the same thing even on a French keyboard where the key next to the
/// CAPS LOCK produces a "Q" when pressed.
///
/// If you want to make your app respond to a key with a particular character
/// on it regardless of location of the key, use [RawKeyEvent.logicalKey] instead.
/// {@endtemplate}
///
/// See also:
///
/// * [logicalKey] for the non-location specific key generated by this event.
/// * [character] for the character generated by this keypress (if any).
PhysicalKeyboardKey get physicalKey => data.physicalKey;
/// Returns an object representing the logical key that was pressed.
///
/// {@template flutter.services.RawKeyEvent.logicalKey}
/// This method takes into account the key map and modifier keys (like SHIFT)
/// to determine which logical key to return.
///
/// If you are looking for the character produced by a key event, use
/// [RawKeyEvent.character] instead.
///
/// If you are collecting text strings, use the [TextField] or
/// [CupertinoTextField] widgets, since those automatically handle many of the
/// complexities of managing keyboard input, like showing a soft keyboard or
/// interacting with an input method editor (IME).
/// {@endtemplate}
LogicalKeyboardKey get logicalKey => data.logicalKey;
/// Returns the Unicode character (grapheme cluster) completed by this
/// keystroke, if any.
///
/// This will only return a character if this keystroke, combined with any
/// preceding keystroke(s), generated a character, and only on a "key down"
/// event. It will return null if no character has been generated by the
/// keystroke (e.g. a "dead" or "combining" key), or if the corresponding key
/// is a key without a visual representation, such as a modifier key or a
/// control key.
///
/// This can return multiple Unicode code points, since some characters (more
/// accurately referred to as grapheme clusters) are made up of more than one
/// code point.
///
/// The `character` doesn't take into account edits by an input method editor
/// (IME), or manage the visibility of the soft keyboard on touch devices. For
/// composing text, use the [TextField] or [CupertinoTextField] widgets, since
/// those automatically handle many of the complexities of managing keyboard
/// input.
final String character;
/// Platform-specific information about the key event.
final RawKeyEventData data;
}
@ -243,7 +399,8 @@ class RawKeyDownEvent extends RawKeyEvent {
/// Creates a key event that represents the user pressing a key.
const RawKeyDownEvent({
@required RawKeyEventData data,
}) : super(data: data);
@required String character,
}) : super(data: data, character: character);
}
/// The user has released a key on the keyboard.
@ -255,7 +412,8 @@ class RawKeyUpEvent extends RawKeyEvent {
/// Creates a key event that represents the user releasing a key.
const RawKeyUpEvent({
@required RawKeyEventData data,
}) : super(data: data);
@required String character,
}) : super(data: data, character: character);
}
/// An interface for listening to raw key events.
@ -300,17 +458,30 @@ class RawKeyboard {
}
Future<dynamic> _handleKeyEvent(dynamic message) async {
if (_listeners.isEmpty) {
return;
}
final RawKeyEvent event = RawKeyEvent.fromMessage(message);
if (event == null) {
return;
}
if (event is RawKeyDownEvent) {
_keysPressed.add(event.logicalKey);
}
if (event is RawKeyUpEvent) {
_keysPressed.remove(event.logicalKey);
}
if (_listeners.isEmpty) {
return;
}
for (ValueChanged<RawKeyEvent> listener in List<ValueChanged<RawKeyEvent>>.from(_listeners)) {
if (_listeners.contains(listener)) {
listener(event);
}
}
}
}
final Set<LogicalKeyboardKey> _keysPressed = Set<LogicalKeyboardKey>();
/// Returns the set of keys currently pressed.
Set<LogicalKeyboardKey> get keysPressed {
return _keysPressed.toSet();
}
}

View file

@ -2,8 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'keyboard_maps.dart';
import 'raw_keyboard.dart';
// Android sets the 0x80000000 bit on a character to indicate that it is a
// combining character, so we use this mask to remove that bit to make it a
// valid Unicode character again.
const int _kCombiningCharacterMask = 0x7fffffff;
/// Platform-specific key event data for Android.
///
/// This object contains information about key events obtained from Android's
@ -82,6 +91,60 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
/// * [isMetaPressed], to see if a META key is pressed.
final int metaState;
// Android only reports a single code point for the key label.
@override
String get keyLabel => codePoint == 0 ? null : String.fromCharCode(_combinedCodePoint).toLowerCase();
// Handles the logic for removing Android's "combining character" flag on the
// codePoint.
int get _combinedCodePoint => codePoint & _kCombiningCharacterMask;
// In order for letter keys to match the corresponding constant, they need to
// be a consistent case.
int get _lowercaseCodePoint => String.fromCharCode(_combinedCodePoint).toLowerCase().codeUnitAt(0);
@override
PhysicalKeyboardKey get physicalKey => kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
@override
LogicalKeyboardKey get logicalKey {
// Look to see if the keyCode is a printable number pad key, so that a
// difference between regular keys (e.g. "=") and the number pad version
// (e.g. the "=" on the number pad) can be determined.
final LogicalKeyboardKey numPadKey = kAndroidNumPadMap[keyCode];
if (numPadKey != null) {
return numPadKey;
}
// If it has a non-control-character label, then either return the existing
// constant, or construct a new Unicode-based key from it. Don't mark it as
// autogenerated, since the label uniquely identifies an ID from the Unicode
// plane.
if (keyLabel != null && keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) {
final int keyId = LogicalKeyboardKey.unicodePlane | (_lowercaseCodePoint & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
}
// Look to see if the keyCode is one we know about and have a mapping for.
LogicalKeyboardKey newKey = kAndroidToLogicalKey[keyCode];
if (newKey != null) {
return newKey;
}
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int androidKeyIdPlane = 0x00200000000;
newKey ??= LogicalKeyboardKey(
androidKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown Android key code $keyCode',
);
return newKey;
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
if (metaState & anyMask == 0) {
return false;
@ -308,7 +371,8 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
@override
String toString() {
return '$runtimeType(flags: $flags, codePoint: $codePoint, keyCode: $keyCode, '
'scanCode: $scanCode, metaState: $metaState, modifiers down: $modifiersPressed)';
return '$runtimeType(keyLabel: $keyLabel flags: $flags, codePoint: $codePoint, '
'keyCode: $keyCode, scanCode: $scanCode, metaState: $metaState, '
'modifiers down: $modifiersPressed)';
}
}

View file

@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'keyboard_maps.dart';
import 'raw_keyboard.dart';
/// Platform-specific key event data for Fuchsia.
@ -54,6 +58,41 @@ class RawKeyEventDataFuchsia extends RawKeyEventData {
/// * [isMetaPressed], to see if a META key is pressed.
final int modifiers;
// Fuchsia only reports a single code point for the key label.
@override
String get keyLabel => codePoint == 0 ? null : String.fromCharCode(codePoint);
@override
LogicalKeyboardKey get logicalKey {
// If the key has a printable representation, then make a logical key based
// on that.
if (codePoint != 0) {
return LogicalKeyboardKey(
LogicalKeyboardKey.unicodePlane | codePoint & LogicalKeyboardKey.valueMask,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key $keyLabel',
);
}
// Look to see if the hidUsage is one we know about and have a mapping for.
LogicalKeyboardKey newKey = kFuchsiaToLogicalKey[hidUsage | LogicalKeyboardKey.hidPlane];
if (newKey != null) {
return newKey;
}
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int fuchsiaKeyIdPlane = 0x00300000000;
newKey ??= LogicalKeyboardKey(
fuchsiaKeyIdPlane | hidUsage | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Ephemeral Fuchsia key code $hidUsage',
);
return newKey;
}
@override
PhysicalKeyboardKey get physicalKey => kFuchsiaToPhysicalKey[hidUsage] ?? PhysicalKeyboardKey.none;
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
if (modifiers & anyMask == 0) {
return false;

View file

@ -0,0 +1,54 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group(PhysicalKeyboardKey, () {
test('Various classes of keys can be looked up by code.', () async {
// Check a letter key
expect(PhysicalKeyboardKey.findKeyByCode(0x00070004), equals(PhysicalKeyboardKey.keyA));
// Check a control key
expect(PhysicalKeyboardKey.findKeyByCode(0x00070029), equals(PhysicalKeyboardKey.escape));
// Check a modifier key
expect(PhysicalKeyboardKey.findKeyByCode(0x000700e1), equals(PhysicalKeyboardKey.shiftLeft));
});
test('Equality is only based on HID code.', () async {
const PhysicalKeyboardKey key1 = PhysicalKeyboardKey(0x01, debugName: 'key1');
const PhysicalKeyboardKey key2 = PhysicalKeyboardKey(0x01, debugName: 'key2');
expect(key1, equals(key1));
expect(key1, equals(key2));
});
});
group(LogicalKeyboardKey, () {
test('Various classes of keys can be looked up by code', () async {
// Check a letter key
expect(LogicalKeyboardKey.findKeyByKeyId(0x0000000061), equals(LogicalKeyboardKey.keyA));
// Check a control key
expect(LogicalKeyboardKey.findKeyByKeyId(0x0100070029), equals(LogicalKeyboardKey.escape));
// Check a modifier key
expect(LogicalKeyboardKey.findKeyByKeyId(0x01000700e1), equals(LogicalKeyboardKey.shiftLeft));
});
test('Control characters are recognized as such', () async {
// Check some common control characters
expect(LogicalKeyboardKey.isControlCharacter('\x08'), isTrue); // BACKSPACE
expect(LogicalKeyboardKey.isControlCharacter('\x0a'), isTrue); // LINE FEED
expect(LogicalKeyboardKey.isControlCharacter('\x0d'), isTrue); // RETURN
expect(LogicalKeyboardKey.isControlCharacter('\x1b'), isTrue); // ESC
expect(LogicalKeyboardKey.isControlCharacter('\x7f'), isTrue); // DELETE
// Check non-control characters
expect(LogicalKeyboardKey.isControlCharacter('A'), isFalse);
expect(LogicalKeyboardKey.isControlCharacter(' '), isFalse);
expect(LogicalKeyboardKey.isControlCharacter('~'), isFalse);
expect(LogicalKeyboardKey.isControlCharacter('\xa0'), isFalse); // NO-BREAK SPACE
});
test('Equality is only based on ID.', () async {
const LogicalKeyboardKey key1 = LogicalKeyboardKey(0x01, keyLabel: 'label1', debugName: 'key1');
const LogicalKeyboardKey key2 = LogicalKeyboardKey(0x01, keyLabel: 'label2', debugName: 'key2');
expect(key1, equals(key1));
expect(key1, equals(key2));
});
});
}

View file

@ -97,6 +97,51 @@ void main() {
}
}
});
test('Printable keyboard keys are correctly translated', () {
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(<String, dynamic>{
'type': 'keydown',
'keymap': 'android',
'keyCode': 29,
'codePoint': 'A'.codeUnitAt(0),
'character': 'A',
'scanCode': 30,
'metaState': 0x0,
});
final RawKeyEventDataAndroid data = keyAEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
expect(data.logicalKey, equals(LogicalKeyboardKey.keyA));
expect(data.keyLabel, equals('a'));
});
test('Control keyboard keys are correctly translated', () {
final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'android',
'keyCode': 111,
'codePoint': null,
'character': null,
'scanCode': 1,
'metaState': 0x0,
});
final RawKeyEventDataAndroid data = escapeKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.escape));
expect(data.logicalKey, equals(LogicalKeyboardKey.escape));
expect(data.keyLabel, isNull);
});
test('Modifier keyboard keys are correctly translated', () {
final RawKeyEvent shiftLeftKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'android',
'keyCode': 59,
'codePoint': null,
'character': null,
'scanCode': 42,
'metaState': RawKeyEventDataAndroid.modifierLeftShift,
});
final RawKeyEventDataAndroid data = shiftLeftKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
expect(data.keyLabel, isNull);
});
});
group('RawKeyEventDataFuchsia', () {
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
@ -175,5 +220,44 @@ void main() {
}
}
});
test('Printable keyboard keys are correctly translated', () {
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(<String, dynamic>{
'type': 'keydown',
'keymap': 'fuchsia',
'hidUsage': 0x00070004,
'codePoint': 'a'.codeUnitAt(0),
'character': 'a',
});
final RawKeyEventDataFuchsia data = keyAEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
expect(data.logicalKey, equals(LogicalKeyboardKey.keyA));
expect(data.keyLabel, equals('a'));
});
test('Control keyboard keys are correctly translated', () {
final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'fuchsia',
'hidUsage': 0x00070029,
'codePoint': null,
'character': null,
});
final RawKeyEventDataFuchsia data = escapeKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.escape));
expect(data.logicalKey, equals(LogicalKeyboardKey.escape));
expect(data.keyLabel, isNull);
});
test('Modifier keyboard keys are correctly translated', () {
final RawKeyEvent shiftLeftKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'fuchsia',
'hidUsage': 0x000700e1,
'codePoint': null,
'character': null,
});
final RawKeyEventDataFuchsia data = shiftLeftKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
expect(data.keyLabel, isNull);
});
});
}