microsoft / react-native-windows

A framework for building native Windows apps with React.
https://microsoft.github.io/react-native-windows/
Other
16.28k stars 1.14k forks source link

Components in rnwcpp need to show mouse states #2099

Closed YuliKl closed 1 year ago

YuliKl commented 5 years ago

kikisaints: I'm taking over this issue to post the draft proposal for this spec in an easily accessible place where people can provide feedback, as a gist is too private.

Mouse Event APIs

Summary

Components need the ability to handle pointer events and implement custom logic when interacting with a React Native app using any pointing device. This proposal outlines the basic props and events needed to enable those scenarios.

Motivation

There are several use-cases where end users may interact with react-native applications using a pointing device.

These include:

Pointer/pointing device = generical term for any device used to control the movement of a cursor on a display screen.

Scope

This proposal deals with the minimal set of APIs needed to achieve fundamental pointing device support. The exact details of the scope of this proposal can be seen in the "Pointer events" section below. All Events listed there are musts.

Examples

Basic pointer event handling

A simple example of how to handle a hover over/pointer over an element. In this case a View component.

  <View onPointerEnter={this._onPointerEnter} onPointerLeave={this._onPointerLeave}/>

  private _onPointerEnter = (event: IPointerEvent) => {
    this.setState({ pointerOverElement: true });
  };

  private _onPointerLeave = (event: IPointerEvent) => {
    this.setState({ pointerOverElement: false });
  };

Custom pointer event handling in native components

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

  <View onPointerDown={this._onPointerDown} pointerDownEvents={handledNativePointerEvents} />

  const handledNativePointerEvents: IHandledPointerEvent[] = [
     { button: 0, eventPhase : EventPhase.Bubbling },
  ];

  private _onPointerDown = (event: IPointEvent) => {
    if(event.nativeEvent.button == 0){
            //do something AFTER the native control has had a chance to handle it (eventPhase = Bubbling)
    }    
  };

Detailed design

The APIs being introduced here will follow the models created by :

Pointer events

The following events will be introduced on the View component, as it covers the most common/prevalent use cases where listening for a mouse event (of any kind) would be needed.

Other individual components where they may be needed, can wrap a View around their desired element/component to capture the mouse events for the children within.

API Args Returns Description
onPointerOver IPointerEvent void Fires when a pointing device is moved within the hit test boundaries of a element.
onPointerEnter IPointerEvent void Fires when a pointing device is moved into the hit test boundaries of an element, including its children.
onPointerDown IPointerEvent void Fires when a pointing device's button (or buttons) state is non-negative.
onPointerMove IPointerEvent void Fires when a pointer changes coordinates when within the hit test boundaries of an element.
onPointerUp IPointerEvent void Fires when a pointing device's button (or buttons) return to negative from being non-negative.
onPointerLeave IPointerEvent void Fires when a pointing device is moved out of the hit test boundaries of an element and all of its children.
onPointerOverCapture IPointerEvent void Occurs when the onPointerOver event is being routed. onPointerOver is the corresponding bubbling event.
onPointerEnterCapture IPointerEvent void Occurs when the onPointerEnter event is being routed. onPointerEnter is the corresponding bubbling event.
onPointerDownCapture IPointerEvent void Occurs when the onPointerDown event is being routed. onPointerDown is the corresponding bubbling event.
onPointerMoveCapture IPointerEvent void Occurs when the onPointerMove event is being routed. onPointerMove is the corresponding bubbling event.
onPointerUpCapture IPointerEvent void Occurs when the onPointerUp event is being routed. onPointerUp is the corresponding bubbling event.
onPointerLeaveCapture IPointerEvent void Occurs when the onPointerLeave event is being routed. onPointerLeave is the corresponding bubbling event.

Where IPointerEvent will be a new event type added to ReactNative.NativeSyntheticEvents of type INativePointerEvent.

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

Property Type Description Default
button number Read-only property that indicates which button was pressed on the mouse that triggered an event fire.

0 - Main button
1 - Auxiliary button
2 - Secondary button
3 - Fourth button
4 - Fifth button
5 - Sixth button, typically pen eraser
-1
buttons number The buttons property gives the current state of the device buttons as a bitmask.

1 - Left Mouse, Touch Contact, Pen contact
4 - Middle Mouse
2 - Right Mouse, Pen barrel button
8 - X1 (back) Mouse
16 - X2 (forward) Mouse
32 - Pen eraser button
0
eventPhase EventPhase Current routing phase for the event. Bubbling

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

Note: In the implementation of these events, the properties in NativeSyntheticEvent like target, bubbles, cancelable etc., should be hooked up. For now, we shall follow the same behaviors for these as other events in react-native today.

Declarative properties

To co-ordinate the handoffs of these pointer events between the native layer and the JS layer, we are also introducing 2 corresponding properties on the View component. Those are:

Property Type Description
pointerOverEvents IHandledPointerEvents[] Specifies the pointer over events that are handled in the JS layer by the onPointerOver/onPointerOverCapture events
pointerOutEvents IHandledPointerEvents[] Specifies the pointer over events that are handled in the JS layer by the onPointerOut/onPointerOutCapture events
pointerDownEvents IHandledPointerEvents[] Specifies the button or buttons that are handled in the JS layer by the onPointerDown/onPointerDownCapture events
pointerMoveEvents IHandledPointerEvents[] Specifies pointer movement events that are handled in the JS layer by the onPointerDown/onPointerDownCapture events
pointerUpEvents IHandledPointerEvents[] Specifies the button or buttons that are handled in the JS layer by the onPointerUp/onPointerUpCapture events

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

Adoption strategy

This will be a new API and not a breaking change. This is being implemented in the react-native-windows out-of-tree platform first to validate the APIs and implementations. Once vetted, we propose to add this to react-native and add documentation in the official API documentation.

How we teach this

These APIs should be presented as a continuation of React and Windows patterns. As such, it should be very familiar to existing web/React developers as well as desktop developers who can relate these APIs to concepts they already know.

Once implemented, these APIs should be documented as part of official react-native API documentation.

Open Questions

ahimberg commented 5 years ago

The vnext implementation today will fire onMouseEnter, onMouseLeave, onMouseMove events if they have been registered for on elements only.

harinikmsft commented 5 years ago

@ahimberg - are those new APIs introduced in the vnext implementation? At what layer (each component or on View/TouchableXX)? If these are new APIs, we may want to change the API names to be more about PointerXX instead of MouseXX in accordance with UWP Pointer events as well as to match the React API surface

kmelmon commented 5 years ago

Load balancing this over to kmelmon

FalseLobster commented 5 years ago

@kikisaints @jonthysell -- Some early thoughts

Someone should verify these statements I'm making, they're mostly based off my interpretations of specs & documentation.

We'll also need to reconcile the various IHandledPointerEvents props with the existing CSS-inspired pointerEvents prop. That prop is sort of what inspired my IHandled... keyboard implementation, but the prop seems overloaded. As far as I can tell, pointerEvents both dictates JS-side hit testing and native event propagation.

chrisglein commented 4 years ago

Hover implementation on Web was added in this patch.

chrisglein commented 4 years ago

I think our overall goals are:

This issue is from quite some time ago and doesn't seem up to date with those goals. We need to get to clarity on where we are versus where want to go. What of this proposal is wanted now, what is wanted eventually, and what is no longer relevant?

chrisglein commented 4 years ago

One way to go about it would be like the Keyboard reconciliation doc, where the overall plan is captured there and there are fine grain issues linked from there.