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 12 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion Libraries/Components/Button.js
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
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 @@ -268,6 +278,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
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 @@ -105,6 +108,8 @@ const PASSTHROUGH_PROPS = [
'onAccessibilityAction',
'onBlur',
'onFocus',
'onKeyDown',
'onKeyUp',
'onLayout',
'onMouseEnter', // [TODO(macOS ISS#2323203)
'onMouseLeave',
Expand Down Expand Up @@ -213,6 +218,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
Expand Up @@ -41,6 +41,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
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
Expand Up @@ -46,6 +46,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
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
22 changes: 22 additions & 0 deletions Libraries/Pressability/Pressability.js
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,10 @@ export type PressabilityConfig = $ReadOnly<{|
*/
onFocus?: ?(event: FocusEvent) => mixed,

onKeyDown?: ?(event: KeyEvent) => mixed,

onKeyUp?: ?(event: KeyEvent) => mixed,

/**
* Called when the hover is activated to provide visual feedback.
*/
Expand Down Expand Up @@ -156,6 +161,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 +452,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
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
7 changes: 7 additions & 0 deletions Libraries/Types/CoreEventTypes.js
Expand Up @@ -152,6 +152,13 @@ export type FocusEvent = SyntheticEvent<
|}>,
>;

export type KeyEvent = SyntheticEvent<
$ReadOnly<{|
characters: string,
HeyImChris marked this conversation as resolved.
Show resolved Hide resolved
modifier: number,
|}>,
>;

export type MouseEvent = SyntheticEvent<
$ReadOnly<{|
clientX: number,
Expand Down
58 changes: 29 additions & 29 deletions RNTester/Podfile.lock
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,8 +519,8 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
DoubleConversion: 56a44bcfd14ab2ff66f5a146b2e875eb4b69b19b
FBLazyVector: e99626a767684cd45377cfb2d270260b08612394
FBReactNativeSpec: 4caa8b770647aad3624a57c46871bf01c1e1ef59
FBLazyVector: 058eeb6417ef70b3b8949d2b4ea1588033186266
FBReactNativeSpec: 9b1b4c1fa7ba24ffa7712799e2cbb602b97eda5d
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
Expand All @@ -533,34 +533,34 @@ SPEC CHECKSUMS:
libevent: ee9265726a1fc599dea382964fa304378affaa5f
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
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: ec85d1ef4d51803337cde71de052e3db103d6472
RCTTypeSafety: a4430a19dff2b11c1c0521ddd9e90b0c033217b4
React: 58c48d390d9c13c7e28ee4832d59a931e26d4a5c
React-ART: d6002339cd61232b4b6b64abc4c519f22c5a53b4
React-callinvoker: ad1904b8c01625a218e541417c1f2269912eba6b
React-Core: d66de0613ce7984076226eaed78f75c6c46bf91c
React-CoreModules: 4be0efeb5b0434beae09740b7672d9c718856e78
React-cxxreact: 8386895677c64406ab269a2390f1bd7a9416a698
React-jsi: fcd89cccc1a8d1fea9a6a3b85645781a12daf712
React-jsiexecutor: 1c8f0bf5110a999922389d8d71f99d6ee942dcd8
React-jsinspector: 6dcbf0c485a9cefa3e3bf87dc3aa9f2ecea1a5dd
React-RCTActionSheet: 66b3b31c0566bab2e90b198c893432b4e8ac1883
React-RCTAnimation: 42fee356deb87bf0f351836625dcc40e610000cd
React-RCTBlob: b3a07d1b087bc30d9a70e5f6aad3828531d03c67
React-RCTImage: 50b248b2a61a4eba7a7c86de6f0ae1cb0b70c25d
React-RCTLinking: 2d5c14d4f96faa81c04e4754db0c7b05ea35de3e
React-RCTNetwork: 2707c0d37396d1f8876bc74eaa8e7374a8be788a
React-RCTPushNotification: 05efdc2e49aaa39c9a087d75d3d54fef2b8a9554
React-RCTSettings: 230c589aabbcb4f04212ea17a7afdbf2b21d716d
React-RCTTest: 56492ab0dec8652ecf7bd33bcefc1494a759470b
React-RCTText: 07ea13069c32f7c8c13deb374c00ddfd641dd925
React-RCTVibration: e71fbb228d655d109bc069bb3a50809761a078a8
React-TurboModuleCxx-RNW: 4da8eb44b10ab3c5bbab9fcb0a8ae415c20ea3c9
React-TurboModuleCxx-WinRTPort: cbe13444db8dc3af024947610c537a27cea1c1cb
ReactCommon: d849f99f384dafff03d56ac7e9fd173e117b1509
Yoga: 5ba9af3554885e152355679790ed9be0b1d01695
React-TurboModuleCxx-WinRTPort: 6c285289e9266a7e316cb87f44396469e23dba9b
ReactCommon: 378cffdae03f1a712467fa43af195df1f79080b3
Yoga: c3920c26a6383607e1776e35919aca16bbbf1991
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: 18ca7d3b0e7db79041574a8bb6200b9e1c2d5359

COCOAPODS: 1.8.4
COCOAPODS: 1.9.1