Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS Keyboard Support #657

Merged
merged 39 commits into from
Dec 12, 2020
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8df39e1
Update RCTCxxBridge.mm
HeyImChris May 14, 2020
4a9bcbd
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Jun 1, 2020
e8d2d8b
Merge pull request #1 from microsoft/master
HeyImChris Aug 17, 2020
ab71c3b
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Sep 3, 2020
e40c784
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Sep 8, 2020
204ddb3
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Sep 16, 2020
b30a024
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Sep 17, 2020
4ac284e
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Oct 7, 2020
7f1863a
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Oct 15, 2020
eec7a79
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Nov 11, 2020
136032a
macOS keyboard support
HeyImChris Nov 26, 2020
119f027
update comment
HeyImChris Nov 26, 2020
a76bcc1
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Nov 27, 2020
7d85560
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Dec 4, 2020
90f0cff
add key value checks so we don't send all events
HeyImChris Dec 10, 2020
508ac92
macOS keyboard support
HeyImChris Nov 26, 2020
1d1ff14
update comment
HeyImChris Nov 26, 2020
78d8e19
add key value checks so we don't send all events
HeyImChris Dec 10, 2020
5dcd7ce
[ado] Workaround homebrew openssl issue (#669)
alloy Dec 8, 2020
c56dff7
Fix detox yarn error with Xcode 12 (#670)
HeyImChris Dec 9, 2020
6b3487d
only use valid keys, bubble events to super
HeyImChris Dec 11, 2020
8568622
Merge branch 'heyimchris/keyboarding' of https://github.com/HeyImChri…
HeyImChris Dec 11, 2020
7011dc4
Merge branch 'master' of https://github.com/microsoft/react-native-macos
HeyImChris Dec 11, 2020
5894111
macOS keyboard support
HeyImChris Nov 26, 2020
ecc351c
update comment
HeyImChris Nov 26, 2020
2fee2cc
add key value checks so we don't send all events
HeyImChris Dec 10, 2020
b40eace
only use valid keys, bubble events to super
HeyImChris Dec 11, 2020
8620c29
macOS keyboard support
HeyImChris Nov 26, 2020
dfb3d4a
add key value checks so we don't send all events
HeyImChris Dec 10, 2020
9a43360
Merge branch 'heyimchris/keyboarding' of https://github.com/HeyImChri…
HeyImChris Dec 11, 2020
0c46c4a
resolve bad merge
HeyImChris Dec 11, 2020
5ee1b50
update valid key bug, api typo
HeyImChris Dec 12, 2020
7ff3849
spacing fix
HeyImChris Dec 12, 2020
9eefe91
Merge branch 'master' into heyimchris/keyboarding
HeyImChris Dec 12, 2020
974da4b
fix flow errors
HeyImChris Dec 12, 2020
c29f431
Merge branch 'heyimchris/keyboarding' of https://github.com/HeyImChri…
HeyImChris Dec 12, 2020
6655bb9
fix snapshot tests for new APIs
HeyImChris Dec 12, 2020
c87d38b
yarn lint --fix
HeyImChris Dec 12, 2020
263182e
fix flipper
HeyImChris Dec 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Libraries/Components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const View = require('./View/View');

const invariant = require('invariant');

import type {PressEvent} from '../Types/CoreEventTypes';
import type {PressEvent, KeyEvent} from '../Types/CoreEventTypes';
import type {FocusEvent, BlurEvent} from './TextInput/TextInput'; // TODO(OSS Candidate ISS#2710739)
import type {ColorValue} from '../StyleSheet/StyleSheetTypes';

Expand Down Expand Up @@ -113,6 +113,17 @@ type ButtonProps = $ReadOnly<{|
* Handler to be called when the button loses key focus
*/
onFocus?: ?(e: FocusEvent) => void,

/**
* Handler to be called when a key down press is detected
*/
onKeyDown?: ?(e: KeyEvent) => void,

/**
* Handler to be called when a key up press is detected
*/
onKeyUp?: ?(e: KeyEvent) => void,

// ]TODO(OSS Candidate ISS#2710739)
|}>;

Expand Down Expand Up @@ -163,6 +174,8 @@ class Button extends React.Component<ButtonProps> {
testID,
onFocus, // TODO(OSS Candidate ISS#2710739)
onBlur, // TODO(OSS Candidate ISS#2710739)
onKeyDown,
onKeyUp,
} = this.props;
const buttonStyles = [styles.button];
const textStyles = [styles.text];
Expand Down Expand Up @@ -207,6 +220,8 @@ class Button extends React.Component<ButtonProps> {
onPress={onPress}
onFocus={onFocus} // TODO(OSS Candidate ISS#2710739)
onBlur={onBlur} // TODO(OSS Candidate ISS#2710739)
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
touchSoundDisabled={touchSoundDisabled}>
<View style={buttonStyles}>
<Text style={textStyles} disabled={disabled}>
Expand Down
12 changes: 12 additions & 0 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ class TouchableOpacity extends React.Component<Props, State> {
this.props.onFocus(event);
}
},
onKeyDown: event => {
if (this.props.onKeyDown != null) {
this.props.onKeyDown(event);
}
},
onKeyUp: event => {
if (this.props.onKeyUp != null) {
this.props.onKeyUp(event);
}
},
onLongPress: this.props.onLongPress,
onPress: this.props.onPress,
onPressIn: event => {
Expand Down Expand Up @@ -279,6 +289,8 @@ class TouchableOpacity extends React.Component<Props, State> {
onDrop={this.props.onDrop}
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
onKeyDown={this.props.onKeyDown}
onKeyUp={this.props.onKeyUp}
draggedTypes={this.props.draggedTypes} // ]TODO(macOS ISS#2323203)
ref={this.props.hostRef}
{...eventHandlersWithoutBlurAndFocus}>
Expand Down
7 changes: 7 additions & 0 deletions Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
import type {
BlurEvent,
FocusEvent,
KeyEvent,
LayoutEvent,
PressEvent,
MouseEvent, // TODO(macOS ISS#2323203)
Expand Down Expand Up @@ -64,6 +65,8 @@ type Props = $ReadOnly<{|
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
onBlur?: ?(event: BlurEvent) => mixed,
onFocus?: ?(event: FocusEvent) => mixed,
onKeyDown?: ?(event: KeyEvent) => mixed,
onKeyUp?: ?(event: KeyEvent) => mixed,
onLayout?: ?(event: LayoutEvent) => mixed,
onLongPress?: ?(event: PressEvent) => mixed,
onPress?: ?(event: PressEvent) => mixed,
Expand Down Expand Up @@ -106,6 +109,8 @@ const PASSTHROUGH_PROPS = [
'onAccessibilityAction',
'onBlur',
'onFocus',
'onKeyDown',
'onKeyUp',
'onLayout',
'onMouseEnter', // [TODO(macOS ISS#2323203)
'onMouseLeave',
Expand Down Expand Up @@ -227,6 +232,8 @@ function createPressabilityConfig(props: Props): PressabilityConfig {
android_disableSound: props.touchSoundDisabled,
onBlur: props.onBlur,
onFocus: props.onFocus,
onKeyDown: props.onKeyDown,
onKeyUp: props.onKeyUp,
onLongPress: props.onLongPress,
onPress: props.onPress,
onPressIn: props.onPressIn,
Expand Down
2 changes: 2 additions & 0 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const UIView = {
onDragEnter: true,
onDragLeave: true,
onDrop: true,
onKeyDown: true,
onKeyUp: true,
draggedTypes: true, // ]TODO(macOS ISS#2323203)
style: ReactNativeStyleAttributes,
};
Expand Down
12 changes: 12 additions & 0 deletions Libraries/Components/View/ReactNativeViewViewConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ const ReactNativeViewConfig = {
captured: 'onFocusCapture',
},
},
topKeyUp: {
phasedRegistrationNames: {
bubbled: 'onKeyUp',
captured: 'onKeyUpCapture',
},
},
topKeyDown: {
phasedRegistrationNames: {
bubbled: 'onKeyDown',
captured: 'onKeyDownCapture',
},
},
topKeyPress: {
phasedRegistrationNames: {
bubbled: 'onKeyPress',
Expand Down
2 changes: 2 additions & 0 deletions Libraries/Components/View/ReactNativeViewViewConfigMacOS.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const ReactNativeViewViewConfigMacOS = {
onDragLeave: true,
onDrop: true,
onFocus: true,
onKeyDown: true,
onKeyUp: true,
onMouseEnter: true,
onMouseLeave: true,
tooltip: true,
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
Layout,
LayoutEvent,
ScrollEvent, // TODO(macOS ISS#2323203)
KeyEvent,
} from '../../Types/CoreEventTypes';
import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
import type {Node} from 'react';
Expand All @@ -41,6 +42,8 @@ export type ViewLayoutEvent = LayoutEvent;
type BubblingEventProps = $ReadOnly<{|
onBlur?: ?(event: BlurEvent) => mixed,
onFocus?: ?(event: FocusEvent) => mixed,
onKeyDown?: ?(event: KeyEvent) => mixed,
onKeyUp?: ?(event: KeyEvent) => mixed,
|}>;

type DirectEventProps = $ReadOnly<{|
Expand Down
28 changes: 28 additions & 0 deletions Libraries/Pressability/Pressability.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {normalizeRect, type RectOrSize} from '../StyleSheet/Rect';
import type {
BlurEvent,
FocusEvent,
KeyEvent,
PressEvent,
MouseEvent,
} from '../Types/CoreEventTypes';
Expand Down Expand Up @@ -93,6 +94,16 @@ export type PressabilityConfig = $ReadOnly<{|
*/
onFocus?: ?(event: FocusEvent) => mixed,

/*
* Called after a key down event is detected.
*/
onKeyDown?: ?(event: KeyEvent) => mixed,

/*
* Called after a key up event is detected.
*/
onKeyUp?: ?(event: KeyEvent) => mixed,

/**
* Called when the hover is activated to provide visual feedback.
*/
Expand Down Expand Up @@ -156,6 +167,8 @@ export type EventHandlers = $ReadOnly<{|
onBlur: (event: BlurEvent) => void,
onClick: (event: PressEvent) => void,
onFocus: (event: FocusEvent) => void,
onKeyDown: (event: KeyDown) => void,
onKeyUp: (event: KeyDown) => void,
onMouseEnter?: (event: MouseEvent) => void,
onMouseLeave?: (event: MouseEvent) => void,
onResponderGrant: (event: PressEvent) => void,
Expand Down Expand Up @@ -445,6 +458,21 @@ export default class Pressability {
}
},
};

const keyEventHandlers = {
onKeyDown: (event: KeyEvent): void => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For extra context, react-native-windows did this a slightly different way. There, we translate key events seen by Pressability into press events, instead of having Pressability emit key events. This matches some legacy behavior, but it also means press event shape is unexpected when press happens by keyboard activation.

If you want raw pass through, it might be simpler to just avoid passing the handlers from Pressable/Touchable to Pressability.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we get state change visuals on keyboard with this? IIRC hooking into the Pressability state machine gave us those for free.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mimics the pattern we used for onFocus and onBlur for macOS. What do you mean by state change visuals on keyboard?

Copy link
Collaborator

@Saadnajmi Saadnajmi Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand properly, windows treats onKeyUp as onPressIn, and onKeyDown as onPressOut. Then, any visuals that might be associated with the pressed state of a perusable also happen on key press. This sounds like a desirable behavior that I think we'd want to incorporate...
I just checked and it looks like react-native-windows is still doing the same thing, so I guess the flow problems were worth the trade off?

const {onKeyDown} = this._config;
if (onKeyDown != null) {
onKeyDown(event);
}
},
onKeyUp: (event: KeyEvent): void => {
const {onKeyUp} = this._config;
if (onKeyUp != null) {
onKeyUp(event);
}
},
};

const responderEventHandlers = {
onStartShouldSetResponder: (): boolean => {
Expand Down
2 changes: 2 additions & 0 deletions Libraries/Renderer/shims/ReactNativeTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export type ViewConfigGetter = () => ReactNativeBaseComponentViewConfig<>;
export type NativeMethods = {
blur(): void,
focus(): void,
onKeyDown(): void,
onKeyUp(): void,
measure(callback: MeasureOnSuccessCallback): void,
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
measureLayout(
Expand Down
20 changes: 20 additions & 0 deletions Libraries/Types/CoreEventTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,26 @@ export type FocusEvent = SyntheticEvent<
|}>,
>;

export type KeyEvent = SyntheticEvent<
$ReadOnly<{|
// Modifier keys
capsLockKey: boolean,
shiftKey: boolean,
controlKey: boolean,
optionKey: boolean,
commandKey: boolean,
numericPadKey: boolean,
helpKey: boolean,
functionKey: boolean,
// Key options
leftArrowKey: boolean,
rightArrowKey: boolean,
upArrowKey: boolean,
downArrowKey: boolean,
key: string,
|}>,
>;

export type MouseEvent = SyntheticEvent<
$ReadOnly<{|
clientX: number,
Expand Down
72 changes: 36 additions & 36 deletions RNTester/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ PODS:
- Flipper-Folly (~> 2.2)
- Flipper-RSocket (~> 1.1)
- Flipper-DoubleConversion (1.1.7)
- Flipper-Folly (2.2.0):
- Flipper-Folly (2.3.0):
- boost-for-react-native
- CocoaLibEvent (~> 1.0)
- Flipper-DoubleConversion
- Flipper-Glog
- OpenSSL-Universal (= 1.0.2.19)
- OpenSSL-Universal (= 1.0.2.20)
- Flipper-Glog (0.3.6)
- Flipper-PeerTalk (0.0.4)
- Flipper-RSocket (1.1.0):
Expand Down Expand Up @@ -64,9 +64,9 @@ PODS:
- libevent/core (2.1.11):
- libevent/event2-headers
- libevent/event2-headers (2.1.11)
- OpenSSL-Universal (1.0.2.19):
- OpenSSL-Universal/Static (= 1.0.2.19)
- OpenSSL-Universal/Static (1.0.2.19)
- OpenSSL-Universal (1.0.2.20):
- OpenSSL-Universal/Static (= 1.0.2.20)
- OpenSSL-Universal/Static (1.0.2.20)
- RCT-Folly (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
Expand Down Expand Up @@ -431,7 +431,7 @@ DEPENDENCIES:
- Yoga (from `../ReactCommon/yoga`)

SPEC REPOS:
https://cdn.cocoapods.org/:
trunk:
- CocoaAsyncSocket
- CocoaLibEvent
- Flipper
Expand Down Expand Up @@ -519,48 +519,48 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
DoubleConversion: 56a44bcfd14ab2ff66f5a146b2e875eb4b69b19b
FBLazyVector: e99626a767684cd45377cfb2d270260b08612394
FBReactNativeSpec: 4caa8b770647aad3624a57c46871bf01c1e1ef59
FBLazyVector: 15d29e4a0b8ae8fc03616b36fb5b9b8d554bfede
FBReactNativeSpec: 24e41f4afb47a2d5af7687f85b8dec0eb4163347
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
glog: 1cb7c408c781ae8f35bbababe459b45e3dee4ec1
hermes: 12d049af0d8e8379c5b3b54ffb1919d670045bdc
libevent: ee9265726a1fc599dea382964fa304378affaa5f
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
RCT-Folly: 1347093ffe75e152d846f7e45a3ef901b60021aa
RCTRequired: 7803a0a3a3d56e3cec84ae1ccbe0f4a2476f9f69
RCTTypeSafety: ae513041fa8773699d99833a8691d2e8b8d6ee8a
React: d6306405782c4669fd3d4d0125a7b2cc2bb19233
React-ART: 65684f957e38c119918b2ad8e2f2c386790b20d0
React-callinvoker: edc229ccab488b3b57895e830d2b825e79f9f04c
React-Core: 50805a8b5eb817533763ae0132a0a61fc740fddf
React-CoreModules: 3147038a51e5a1a1819be556efb3c22855816aec
React-cxxreact: 88f9c5fbd0c391488fb884ab4bf8af85c548230c
React-jsi: e7b3496404f24299beb067b0e4fa38e836f1d6a4
React-jsiexecutor: f214cb9bebf408f6deeea9f6fe6ca610ab88eb6e
React-jsinspector: 7fba9bc98d5047bf5c8dae0cd8acd36b86448d04
React-RCTActionSheet: c4b92af34b6f64c786a972f5c196a375b7ce6cea
React-RCTAnimation: 5a0f7f7b3eaf0520e53d119f8352fdc622d0a90b
React-RCTBlob: 37befa1be701fe18ab0d63eb986b65829c843fc6
React-RCTImage: a02ff4f1882f35574ae589556b5c94b9eee973da
React-RCTLinking: 70240d29450241879339d7384b0903232d30e2c6
React-RCTNetwork: 17a1cd202a4566c25244444644a1fadd092466db
React-RCTPushNotification: eec1e289dfb203ee5360a9065c4009de0c16e097
React-RCTSettings: 490435d0c7767d085ebc9218d6cc9f9e2018ff1c
React-RCTTest: 06e4d480573015d42b0acb110aeb343c16d0c595
React-RCTText: a2a64b9c882d9b9a8e26c27773ca305ce910f4a6
React-RCTVibration: d32b07b6c9e821cb784b04e1c65cee3b35099b54
RCTRequired: 37f40377c0ea795d4b906f3499655fbd0ff42e27
RCTTypeSafety: 3f230f2e3335fbe45ed5c9d4bd2eba467392479f
React: 707958373426608d07889c030a5db60b826e159e
React-ART: d81c7e291454d9cbaa6088758f264e8ea21099fc
React-callinvoker: 6d9636c3513c6cd0acc46914d3de03ddbdc84007
React-Core: c71c9b022c3151ab6f61f6184291e8ecd706d2a1
React-CoreModules: 45fb897af5ba457cb6cb66a82b5faa4d117bfe15
React-cxxreact: 0e079067a4a355eb15be1492aa96cfd68720ad3e
React-jsi: e150452a6ba4ef7f3eb55a6b0e4991605ddf1ddd
React-jsiexecutor: 160af6637a201038dbbfd232caa8f1abe73a4a79
React-jsinspector: f0e7ba53d923a58da6aa4c2abacfa178db78cb31
React-RCTActionSheet: bca975a416caea8f77677616cfa06c2d6245d5cf
React-RCTAnimation: ea3f8968ad420f0bf55ed9a85c7fc9c225f3dc9a
React-RCTBlob: 2d237a23740260aee9716e66ac4c388a29c23ee3
React-RCTImage: 57e1ce8afe2b04ab4a44254f9ac9ae54b473cdc9
React-RCTLinking: ccf93f64ecf303ef4fac7fe50424ea55d74bc072
React-RCTNetwork: 4d075d146f0ed6a6e3c657a57ae17b011e17df71
React-RCTPushNotification: e9b3686f7f9ca6b74617a5427e7ffcba3d70f41c
React-RCTSettings: fc9afc6981735f0cce2b37c2ed90b7a800a38308
React-RCTTest: c2c26c856d466e948dee7ff939372637d1c80069
React-RCTText: 7effa140484e70f24e340450d24e7e0ed3f1ae88
React-RCTVibration: c1867f60d25efafbf8705b445e6dc11089312cc7
React-TurboModuleCxx-RNW: 4da8eb44b10ab3c5bbab9fcb0a8ae415c20ea3c9
React-TurboModuleCxx-WinRTPort: cbe13444db8dc3af024947610c537a27cea1c1cb
ReactCommon: d849f99f384dafff03d56ac7e9fd173e117b1509
Yoga: 5ba9af3554885e152355679790ed9be0b1d01695
React-TurboModuleCxx-WinRTPort: d7bf672478f94748329c159893310100ad26c34e
ReactCommon: 047272186b16e3cab56317cbd4c119a80329a5ab
Yoga: 56a44c9ff7008e519e20fcf7cc257b5a86f46695
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: 18ca7d3b0e7db79041574a8bb6200b9e1c2d5359

COCOAPODS: 1.8.4
COCOAPODS: 1.9.1
Loading