mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
W3CPointerEvents: emit click events based on pointerDown/pointerUp (#37541)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37541 This diff makes changes to emit bubbling click events (i.e. [these](https://www.w3.org/TR/uievents/#click)) based on the existing pointer-down/pointer-up events. As per the [pointer events spec](https://www.w3.org/TR/pointerevents3/#the-click-auxclick-and-contextmenu-events), click events are still dispatched as PointerEvents (i.e. conform to the PointerEvent interface). Changelog: [Internal] [Added] - W3CPointerEvents: emit click events based on pointerDown/pointerUp Reviewed By: NickGerleman Differential Revision: D45666344 fbshipit-source-id: 08829d3a24fef6851c1c3c29d4fa51b31ec68931
This commit is contained in:
committed by
Facebook GitHub Bot
parent
2f1c94efb1
commit
76d41864a2
+10
@@ -700,6 +700,16 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
|
||||
setPointerEventsFlag(view, PointerEventHelper.EVENT.MOVE_CAPTURE, value);
|
||||
}
|
||||
|
||||
@ReactProp(name = "onClick")
|
||||
public void setClick(@NonNull T view, boolean value) {
|
||||
setPointerEventsFlag(view, PointerEventHelper.EVENT.CLICK, value);
|
||||
}
|
||||
|
||||
@ReactProp(name = "onClickCapture")
|
||||
public void setClickCapture(@NonNull T view, boolean value) {
|
||||
setPointerEventsFlag(view, PointerEventHelper.EVENT.CLICK_CAPTURE, value);
|
||||
}
|
||||
|
||||
/* Experimental W3C Pointer events end */
|
||||
|
||||
@ReactProp(name = "onMoveShouldSetResponder")
|
||||
|
||||
+44
@@ -41,6 +41,7 @@ public class JSPointerDispatcher {
|
||||
|
||||
private Map<Integer, List<ViewTarget>> mLastHitPathByPointerId;
|
||||
private Map<Integer, float[]> mLastEventCoordinatesByPointerId;
|
||||
private Map<Integer, List<ViewTarget>> mCurrentlyDownPointerIdsToHitPath;
|
||||
private Set<Integer> mHoveringPointerIds = new HashSet<>();
|
||||
|
||||
private int mChildHandlingNativeGesture = -1;
|
||||
@@ -53,6 +54,7 @@ public class JSPointerDispatcher {
|
||||
|
||||
public JSPointerDispatcher(ViewGroup viewGroup) {
|
||||
mRootViewGroup = viewGroup;
|
||||
mCurrentlyDownPointerIdsToHitPath = new HashMap<>();
|
||||
}
|
||||
|
||||
public void onChildStartedNativeGesture(
|
||||
@@ -87,6 +89,30 @@ public class JSPointerDispatcher {
|
||||
mChildHandlingNativeGesture = -1;
|
||||
}
|
||||
|
||||
// returns the section of the hit path shared by both lists, or an empty list if there's no such
|
||||
// section
|
||||
private static List<ViewTarget> findHitPathIntersection(
|
||||
final List<ViewTarget> hitsA, final List<ViewTarget> hitsB) {
|
||||
if (hitsA.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
if (hitsB.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<ViewTarget> inA = new HashSet<>(hitsA);
|
||||
|
||||
List<ViewTarget> intersection = new ArrayList<>();
|
||||
|
||||
for (final ViewTarget vt : hitsB) {
|
||||
if (inA.contains(vt)) {
|
||||
intersection.add(vt);
|
||||
}
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
private void onUp(
|
||||
int activeTargetTag,
|
||||
PointerEventState eventState,
|
||||
@@ -127,6 +153,18 @@ public class JSPointerDispatcher {
|
||||
eventDispatcher);
|
||||
}
|
||||
|
||||
List<ViewTarget> hitPathDown = mCurrentlyDownPointerIdsToHitPath.remove(activePointerId);
|
||||
if (hitPathDown != null
|
||||
&& isAnyoneListeningForBubblingEvent(activeHitPath, EVENT.CLICK, EVENT.CLICK_CAPTURE)) {
|
||||
List<ViewTarget> hitPathForClick = findHitPathIntersection(hitPathDown, activeHitPath);
|
||||
if (!hitPathForClick.isEmpty()) {
|
||||
final ViewTarget clickTarget = hitPathForClick.get(0);
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.CLICK, clickTarget.getViewId(), eventState, motionEvent));
|
||||
}
|
||||
}
|
||||
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||
mPrimaryPointerId = UNSET_POINTER_ID;
|
||||
}
|
||||
@@ -175,6 +213,12 @@ public class JSPointerDispatcher {
|
||||
eventDispatcher);
|
||||
}
|
||||
|
||||
// store some information if we might need to emit a click later on
|
||||
if (isAnyoneListeningForBubblingEvent(activeHitPath, EVENT.CLICK, EVENT.CLICK_CAPTURE)) {
|
||||
mCurrentlyDownPointerIdsToHitPath.put(
|
||||
eventState.getActivePointerId(), new ArrayList<>(activeHitPath));
|
||||
}
|
||||
|
||||
boolean listeningForDown =
|
||||
isAnyoneListeningForBubblingEvent(activeHitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE);
|
||||
if (listeningForDown) {
|
||||
|
||||
+15
-5
@@ -91,6 +91,10 @@ public class PointerEvent extends Event<PointerEvent> {
|
||||
return mEventName;
|
||||
}
|
||||
|
||||
private boolean isClickEvent() {
|
||||
return mEventName.equals(PointerEventHelper.CLICK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
if (mMotionEvent == null) {
|
||||
@@ -190,7 +194,8 @@ public class PointerEvent extends Event<PointerEvent> {
|
||||
pointerEvent.putString("pointerType", pointerType);
|
||||
|
||||
boolean isPrimary =
|
||||
mEventState.supportsHover(pointerId) || pointerId == mEventState.mPrimaryPointerId;
|
||||
!isClickEvent() // compatibility click events should not be considered primary
|
||||
&& (mEventState.supportsHover(pointerId) || pointerId == mEventState.mPrimaryPointerId);
|
||||
pointerEvent.putBoolean("isPrimary", isPrimary);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
|
||||
@@ -223,8 +228,8 @@ public class PointerEvent extends Event<PointerEvent> {
|
||||
pointerEvent.putDouble("tiltY", 0);
|
||||
|
||||
pointerEvent.putInt("twist", 0);
|
||||
|
||||
if (pointerType.equals(PointerEventHelper.POINTER_TYPE_MOUSE)) {
|
||||
// note: click events should have width = height = 1
|
||||
if (pointerType.equals(PointerEventHelper.POINTER_TYPE_MOUSE) || isClickEvent()) {
|
||||
pointerEvent.putDouble("width", 1);
|
||||
pointerEvent.putDouble("height", 1);
|
||||
} else {
|
||||
@@ -241,8 +246,12 @@ public class PointerEvent extends Event<PointerEvent> {
|
||||
pointerEvent.putInt(
|
||||
"buttons", PointerEventHelper.getButtons(mEventName, pointerType, buttonState));
|
||||
|
||||
pointerEvent.putDouble(
|
||||
"pressure", PointerEventHelper.getPressure(pointerEvent.getInt("buttons"), mEventName));
|
||||
final double pressure =
|
||||
isClickEvent() // click events need pressure=0
|
||||
? 0
|
||||
: PointerEventHelper.getPressure(pointerEvent.getInt("buttons"), mEventName);
|
||||
|
||||
pointerEvent.putDouble("pressure", pressure);
|
||||
pointerEvent.putDouble("tangentialPressure", 0.0);
|
||||
|
||||
return pointerEvent;
|
||||
@@ -264,6 +273,7 @@ public class PointerEvent extends Event<PointerEvent> {
|
||||
case PointerEventHelper.POINTER_LEAVE:
|
||||
case PointerEventHelper.POINTER_OUT:
|
||||
case PointerEventHelper.POINTER_OVER:
|
||||
case PointerEventHelper.CLICK:
|
||||
pointersEventData = Arrays.asList(createW3CPointerEvent(activePointerIndex));
|
||||
break;
|
||||
}
|
||||
|
||||
+5
@@ -25,6 +25,8 @@ public class PointerEventHelper {
|
||||
public static enum EVENT {
|
||||
CANCEL,
|
||||
CANCEL_CAPTURE,
|
||||
CLICK,
|
||||
CLICK_CAPTURE,
|
||||
DOWN,
|
||||
DOWN_CAPTURE,
|
||||
ENTER,
|
||||
@@ -49,6 +51,7 @@ public class PointerEventHelper {
|
||||
public static final String POINTER_UP = "topPointerUp";
|
||||
public static final String POINTER_OVER = "topPointerOver";
|
||||
public static final String POINTER_OUT = "topPointerOut";
|
||||
public static final String CLICK = "topClick";
|
||||
|
||||
// https://w3c.github.io/pointerevents/#the-buttons-property
|
||||
public static int getButtons(String eventName, String pointerType, int buttonState) {
|
||||
@@ -117,6 +120,8 @@ public class PointerEventHelper {
|
||||
case UP_CAPTURE:
|
||||
case CANCEL:
|
||||
case CANCEL_CAPTURE:
|
||||
case CLICK:
|
||||
case CLICK_CAPTURE:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user