mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
PointerEvents: Refactor JSPointerDispatcher to share logic for UP/DOWN for secondary pointers
Summary:
Changelog: [Internal] - Refactor JSPointerDispatcher to group secondary actions (pointer_up, pointer_down) to use same logic and emit non-direct events
This diff re-arranges some code and re-uses some logic for different MotionEvent types.
Specifically, before our code for handling a motionEvent looked like
```
func onMotionEvent(event) {
if (event is HOVER_MOVE) {
handleHoverEvent(event)
}
if (event is ACTION_DOWN) {
// logic for dispatching down, enter, over
}
if (event is ACTION_POINTER_DOWN) { // this represents any secondary pointer going down
// logic for dispatching down
}
if (event is ACTION_MOVE) {
// logic for dispatching touch move
}
if (event is ACTION_UP) {
// logic for dispatching up, out, leave
}
if (event is ACTION_POINTER_UP) { // this represents any secondary pointer going up
// logic for dispatching up
}
....
}
```
now, we've refactored it to be like:
```
func onMotionEvent(event) {
if (event is HOVER_MOVE) { // Still keep this as separate because it does some special things. Will refactor in follow-up diff
handleHoverEvent(event)
}
switch() {
case ACTION_DOWN or ACTION_POINTER_DOWN:
// do same logic
break;
case ACTION_UP or ACTION_POINTER_UP:
// do same logic
break;
case ACTION_MOVE:
// do same logic, we will want to converge this logic with HOVER_MOVE, todo in a follow-up
break;
...
}
....
}
```
This refactor adds the functionality of `enter, over, out, leave` to secondary pointer events in which they weren't fired before.
Reviewed By: vincentriemer
Differential Revision: D39142820
fbshipit-source-id: 8b89db6dc22f24583f8c14dbb8392d4dc8ff6e4d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c52df02f84
commit
64ae12e553
+151
-171
@@ -74,6 +74,123 @@ public class JSPointerDispatcher {
|
||||
mChildHandlingNativeGesture = -1;
|
||||
}
|
||||
|
||||
private void onUp(
|
||||
int activeTargetTag,
|
||||
List<ViewTarget> hitPath,
|
||||
int surfaceId,
|
||||
MotionEvent motionEvent,
|
||||
EventDispatcher eventDispatcher) {
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||
// End of a "down" coalescing key
|
||||
mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
|
||||
mDownStartTime = TouchEvent.UNSET;
|
||||
} else {
|
||||
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime);
|
||||
}
|
||||
|
||||
boolean supportsHover = PointerEventHelper.supportsHover(motionEvent);
|
||||
boolean listeningForUp = isAnyoneListeningForBubblingEvent(hitPath, EVENT.UP, EVENT.UP_CAPTURE);
|
||||
if (listeningForUp) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_UP,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
if (!supportsHover) {
|
||||
boolean listeningForOut =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.OUT, EVENT.OUT_CAPTURE);
|
||||
if (listeningForOut) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_OUT,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
List<ViewTarget> leaveViewTargets =
|
||||
filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false);
|
||||
|
||||
// target -> root
|
||||
dispatchEventForViewTargets(
|
||||
PointerEventHelper.POINTER_LEAVE,
|
||||
leaveViewTargets,
|
||||
eventDispatcher,
|
||||
surfaceId,
|
||||
motionEvent);
|
||||
}
|
||||
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||
mPrimaryPointerId = UNSET_POINTER_ID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void onDown(
|
||||
int activeTargetTag,
|
||||
List<ViewTarget> hitPath,
|
||||
int surfaceId,
|
||||
MotionEvent motionEvent,
|
||||
EventDispatcher eventDispatcher) {
|
||||
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
mPrimaryPointerId = motionEvent.getPointerId(0);
|
||||
mDownStartTime = motionEvent.getEventTime();
|
||||
mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime);
|
||||
} else {
|
||||
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime);
|
||||
}
|
||||
|
||||
boolean supportsHover = PointerEventHelper.supportsHover(motionEvent);
|
||||
if (!supportsHover) {
|
||||
// Indirect OVER event dispatches before ENTER
|
||||
boolean listeningForOver =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.OVER, EVENT.OVER_CAPTURE);
|
||||
if (listeningForOver) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_OVER,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
List<ViewTarget> enterViewTargets =
|
||||
filterByShouldDispatch(hitPath, EVENT.ENTER, EVENT.ENTER_CAPTURE, false);
|
||||
|
||||
// Dispatch root -> target, we need to reverse order of enterViewTargets
|
||||
Collections.reverse(enterViewTargets);
|
||||
dispatchEventForViewTargets(
|
||||
PointerEventHelper.POINTER_ENTER,
|
||||
enterViewTargets,
|
||||
eventDispatcher,
|
||||
surfaceId,
|
||||
motionEvent);
|
||||
}
|
||||
|
||||
boolean listeningForDown =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE);
|
||||
if (listeningForDown) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_DOWN,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDispatcher) {
|
||||
int action = motionEvent.getActionMasked();
|
||||
|
||||
@@ -103,195 +220,58 @@ public class JSPointerDispatcher {
|
||||
int activeTargetTag = activeViewTarget.getViewId();
|
||||
|
||||
if (action == MotionEvent.ACTION_HOVER_MOVE) {
|
||||
handleHoverEvent(motionEvent, eventDispatcher, surfaceId, hitPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// First down pointer
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mPrimaryPointerId = motionEvent.getPointerId(actionIndex);
|
||||
|
||||
// Start a "down" coalescing key
|
||||
mDownStartTime = motionEvent.getEventTime();
|
||||
mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime);
|
||||
|
||||
if (!supportsHover) {
|
||||
// Indirect OVER event dispatches before ENTER
|
||||
boolean listeningForOver =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.OVER, EVENT.OVER_CAPTURE);
|
||||
if (listeningForOver) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_OVER,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
List<ViewTarget> enterViewTargets =
|
||||
filterByShouldDispatch(hitPath, EVENT.ENTER, EVENT.ENTER_CAPTURE, false);
|
||||
|
||||
// Dispatch root -> target, we need to reverse order of enterViewTargets
|
||||
Collections.reverse(enterViewTargets);
|
||||
dispatchEventForViewTargets(
|
||||
PointerEventHelper.POINTER_ENTER,
|
||||
enterViewTargets,
|
||||
eventDispatcher,
|
||||
surfaceId,
|
||||
motionEvent);
|
||||
}
|
||||
|
||||
boolean listeningForDown =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE);
|
||||
if (listeningForDown) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_DOWN,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
onMove(motionEvent, eventDispatcher, surfaceId, hitPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(luwe) - Update this to properly handle native gesture handling for non-hover move events
|
||||
// If the touch was intercepted by a child, we've already sent a cancel event to JS for this
|
||||
// gesture, so we shouldn't send any more pointer events related to it.
|
||||
if (mChildHandlingNativeGesture != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer
|
||||
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime);
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
onDown(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// TODO(luwe) - converge this with ACTION_HOVER_MOVE
|
||||
int coalescingKey = mTouchEventCoalescingKeyHelper.getCoalescingKey(mDownStartTime);
|
||||
|
||||
// TODO(luwe) We need to fire indirect over,enter events for secondary pointers
|
||||
|
||||
boolean listeningForDown =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.DOWN, EVENT.DOWN_CAPTURE);
|
||||
if (listeningForDown) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_DOWN,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
int coalescingKey = mTouchEventCoalescingKeyHelper.getCoalescingKey(mDownStartTime);
|
||||
|
||||
boolean listeningForMove =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.MOVE, EVENT.MOVE_CAPTURE);
|
||||
if (listeningForMove) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_MOVE,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
coalescingKey,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Exactly one of the pointers goes up, not the last one
|
||||
if (action == MotionEvent.ACTION_POINTER_UP) {
|
||||
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mDownStartTime);
|
||||
|
||||
// TODO(luwe) We need to fire indirect out,leave events for secondary pointers
|
||||
|
||||
boolean listeningForUp =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.UP, EVENT.UP_CAPTURE);
|
||||
if (listeningForUp) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_UP,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Last pointer comes up
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
|
||||
// End of a "down" coalescing key
|
||||
mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
|
||||
mDownStartTime = TouchEvent.UNSET;
|
||||
|
||||
boolean listeningForUp =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.UP, EVENT.UP_CAPTURE);
|
||||
if (listeningForUp) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_UP,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
if (!supportsHover) {
|
||||
boolean listeningForOut =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.OUT, EVENT.OUT_CAPTURE);
|
||||
if (listeningForOut) {
|
||||
boolean listeningForMove =
|
||||
isAnyoneListeningForBubblingEvent(hitPath, EVENT.MOVE, EVENT.MOVE_CAPTURE);
|
||||
if (listeningForMove) {
|
||||
eventDispatcher.dispatchEvent(
|
||||
PointerEvent.obtain(
|
||||
PointerEventHelper.POINTER_OUT,
|
||||
PointerEventHelper.POINTER_MOVE,
|
||||
surfaceId,
|
||||
activeTargetTag,
|
||||
motionEvent,
|
||||
mTargetCoordinates,
|
||||
coalescingKey,
|
||||
mPrimaryPointerId));
|
||||
}
|
||||
|
||||
List<ViewTarget> leaveViewTargets =
|
||||
filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false);
|
||||
|
||||
// target -> root
|
||||
dispatchEventForViewTargets(
|
||||
PointerEventHelper.POINTER_LEAVE,
|
||||
leaveViewTargets,
|
||||
eventDispatcher,
|
||||
surfaceId,
|
||||
motionEvent);
|
||||
}
|
||||
|
||||
mPrimaryPointerId = UNSET_POINTER_ID;
|
||||
return;
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
onUp(activeTargetTag, hitPath, surfaceId, motionEvent, eventDispatcher);
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
dispatchCancelEvent(hitPath, motionEvent, eventDispatcher);
|
||||
break;
|
||||
default:
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"Warning : Motion Event was ignored. Action="
|
||||
+ action
|
||||
+ " Target="
|
||||
+ activeTargetTag
|
||||
+ " Supports Hover="
|
||||
+ supportsHover);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_CANCEL) {
|
||||
dispatchCancelEvent(hitPath, motionEvent, eventDispatcher);
|
||||
return;
|
||||
}
|
||||
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"Warning : Motion Event was ignored. Action="
|
||||
+ action
|
||||
+ " Target="
|
||||
+ activeTargetTag
|
||||
+ " Supports Hover="
|
||||
+ supportsHover);
|
||||
}
|
||||
|
||||
private static boolean isAnyoneListeningForBubblingEvent(
|
||||
@@ -356,7 +336,7 @@ public class JSPointerDispatcher {
|
||||
}
|
||||
|
||||
// called on hover_move motion events only
|
||||
private void handleHoverEvent(
|
||||
private void onMove(
|
||||
MotionEvent motionEvent,
|
||||
EventDispatcher eventDispatcher,
|
||||
int surfaceId,
|
||||
|
||||
Reference in New Issue
Block a user