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

Add support for generic onKey** events, declarative properties for handling key strokes #520

Closed
harinikmsft opened this issue Jul 24, 2020 · 1 comment
Labels
help wanted Extra attention is needed Partner: Facebook

Comments

@harinikmsft
Copy link

Windows supports the following APIs that need to be implemented in macOS as well to complete the desktop story for keyboard handling:

onKeyXX callbacks

The following callbacks are available on View component (and get passed through to TextInput and Pressable) in Windows to cover the most common use cases where key stroke handling is likely to occur. Other individual components where they may be neeeded can wrap a View around themselves.

Note: The onKeyDown event fires repeatedly when a key is held down continuously which is also similar to how native Windows implements KeyDown.

API Args Returns Description
onKeyDown IKeyboardEvent void Occurs when a keyboard key is pressed when a component has focus. On Windows, this corresponds to KeyDown
onKeyDownCapture IKeyboardEvent void Occurs when the onKeyDown event is being routed. onKeyDown is the corresponding bubbling event. On Windows, this corresponds to PreviewKeyDown
onKeyUp IKeyboardEvent void Occurs when a keyboard key is released when a component has focus. On Windows, this corresponds to KeyUp
onKeyUpCapture IKeyboardEvent void Occurs when the onKeyUp event is being routed. onKeyUp is the corresponding bubbling event. On Windows, this corresponds to PreviewKeyUp

Where IKeyboardEvent is a new event type added to ReactNative.NativeSyntheticEvents of type INativeKeyboardEvent. The properties in NativeSyntheticEvent like target, bubbles, cancelable etc., are also available for IKeyboardEvent and follow the same behaviors as other events in react-native today.

INativeKeyboardEvent is a new interface and will expose the following properties:

Property Type Description Default
key string The character typed by the user.TODO: Document the w3c spec for how the keys show up string.Empty
altKey boolean The Alt (Alternative) key. Also maps to Apple Option key. false
ctrlKey boolean The Ctrl (Control) key. false
shiftKey boolean The Shift key. false
metaKey boolean Maps to Windows Logo key and the Apple Command key. False
repeat boolean Flag to represent if a key is being held down/repeated. Tracked by Windows Issue#5513, macOS Issue#502 False
eventPhase EventPhase Current phase of routing for the key event. Bubbling

Where EventPhase is an enum to detect whether the keystroke is being tunneled/bubbled to the target component that has focus. It has the following fields:

  • None : none
  • Capturing : when the keydown/keyup event is being captured while tunneling its way from the root to the target component
  • AtTarget : when the keydown/keyup event has reached the target component that is handling the corresponding event
  • Bubbling : when the keydown/keyup event is being captured while bubbling its way to the parent(s) of the target component

In the following example, the lastKeyDown prop will contain the key stroke from the end user when keyboard focus is on View.

  <View onKeyDown={this._onKeyDown} />
      
  private _onKeyDown = (event: IKeyboardEvent) => {
    this.setState({ lastKeyDown: event.nativeEvent.key });
  };

Declarative properties

To co-ordinate the handoffs of the onKeyXX events between the native layer and the JS layer, 2 corresponding properties on View and TextInput components are available. These are:

Property Type Description
keyDownEvents IHandledKeyboardEvents[] Specifies the key(s) that are handled in the JS layer by the onKeyDown/onKeyDownCapture events
keyUpEvents IHandledKeyboardEvents[] Specifies the key(s) that are handled in the JS layer by the onKeyUp/onKeyUpCapture events

Where IHandledKeyboardEvents is a new type which takes the following parameters:

  • a string parameter named key to declare the key strokes that are of interest to the JS layer
  • an eventPhase paramter of type EventPhase to declare the routing phase of interest to the JS layer.

When the onKeyXX events are handled by the app code, the corresponding native component will have KeyXX/PreviewKeyXX events marked as handled for the declared key strokes.

In the following example, the app's logic takes precedence when certain keystrokes are encountered at certain event routing phases in the TextInput before the native platform can handle them.

  <TextInput onKeyUp={this._onKeyUp} keyUpEvents={handledNativeKeyboardEvents} />
  
  const handledNativeKeyboardEvents: IHandledKeyboardEvent[] = [
     { key: 'Enter', eventPhase : EventPhase.Bubbling },
  ];
  
  private _onKeyUp = (event: IKeyboardEvent) => {
    if(event.nativeEvent.key == 'Enter'){
            //do something custom when Enter key is detected when focus is on the TextInput component AFTER the native TextBox has had a chance to handle it (eventPhase = Bubbling)
    }    
  };

Behavior details:

  • Because of the JS thread and the native thread being unconnected, all events are always dispatched to the JS layer regardless of whether they are handled in the native layer.
    • Note: This is the pragmatic choice that we are making to unblock ourselves based on our understanding of how Pointer events work in RN for iOS/Android. However, this is not the desired long term behavior since it would be nicer to not expose the seams between the 2 layers to the developer and ensure that the eventing system in RN behaves in a more predictable manner. We may make some updates here based on conversations with REACT and React Native community and contributors as well as expected updates in the fabric rearchitecture.
  • The declarative properties are simply a way for the JS side to communicate to the native layer that an event is being handled on the JS side. However, because of the above threading model limitation, there may be missed key strokes between the 2 layers since there is no guaranteed way to keep the handling of key strokes fully synchronous at this time.
  • If there are mismatches between the declared key strokes/eventPhases and the correspinding values in the event handlers, the event handlers will attempt to work as though the key strokes were declared. If the native layer did not handle those key strokes, the event handlers will work and if the native layer did handle them, the event handler may not fire since it was not declared correctly.
    • We may want to add a runtime warning for such user errors. This is a good-to-have.
  • It is possible to declare different keystrokes for different event phases on the same component. For example, the following is allowed:
  <TextInput onKeyUp={this._onKeyUp} keyUpEvents={handledNativeKeyboardEvents} />
  
  const handledNativeKeyboardEvents: IHandledKeyboardEvent[] = [
     { key: 'Esc' },
     { key: 'Enter', ctrlKey : true, eventPhase : EventPhase.Capturing }
  ];
  
  private _onKeyUp = (event: IKeyboardEvent) => {
    if(event.nativeEvent.key == 'Esc'){
       //do something custom when Escape key is detected when focus is on the TextInput component AFTER 
       //the native TextBox has had a chance to handle it (default eventPhase = Bubbling)
    } else if (event.nativeEvent.key == 'Enter' && 
               event.nativeEvent.eventPhase == EventPhase.Capturing && 
               event.nativeEvent.ctrlKey == true)
    {
       //do something custom when user presses Ctrl + Enter when focus is on the TextInput component BEFORE
       //the native TextBox has had a chance to handle it.
    }      
  }; 
@harinikmsft harinikmsft added the help wanted Extra attention is needed label Aug 20, 2020
@HeyImChris
Copy link

Keyboard support for keyUp and keyDown on macOS was added with this and this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed Partner: Facebook
Projects
None yet
Development

No branches or pull requests

2 participants