Skip to content

Commit

Permalink
Dispatch enter/leave for ancestor hit path
Browse files Browse the repository at this point in the history
Summary: Changelog: [Internal] - Fix pointer event dispatch to also fire enter/leave for ancestors in the hit path. Compared the event order with web on the RNTester W3C pointer example

Reviewed By: appden

Differential Revision: D35403076

fbshipit-source-id: 726e45e49a901b1d97ad3e20f5898701fd1f763b
  • Loading branch information
Luna Wei authored and facebook-github-bot committed Apr 12, 2022
1 parent 9bac0b7 commit de09bd3
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class JSPointerDispatcher {

// Set globally for hover interactions, referenced for coalescing hover events
private long mHoverInteractionKey = TouchEvent.UNSET;
private List<Integer> mLastHitState = Collections.emptyList();
private List<Integer> mLastHitPath = Collections.emptyList();
private final float[] mLastEventCoordinates = new float[2];

public JSPointerDispatcher(ViewGroup viewGroup) {
Expand All @@ -56,8 +56,10 @@ public void onChildStartedNativeGesture(
return;
}

int targetTag = findTargetTagAndSetCoordinates(motionEvent);
dispatchCancelEvent(targetTag, motionEvent, eventDispatcher);
List<Integer> hitPath =
TouchTargetHelper.findTargetPathAndCoordinatesForTouch(
motionEvent.getX(), motionEvent.getY(), mRootViewGroup, mTargetCoordinates);
dispatchCancelEvent(hitPath, motionEvent, eventDispatcher);
mChildHandlingNativeGesture = childView.getId();
}

Expand All @@ -78,11 +80,15 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp

int surfaceId = UIManagerHelper.getSurfaceId(mRootViewGroup);
int action = motionEvent.getActionMasked();
int targetTag = findTargetTagAndSetCoordinates(motionEvent);
List<Integer> hitPath =
TouchTargetHelper.findTargetPathAndCoordinatesForTouch(
motionEvent.getX(), motionEvent.getY(), mRootViewGroup, mTargetCoordinates);

int targetTag = hitPath.get(0);

if (supportsHover) {
if (action == MotionEvent.ACTION_HOVER_MOVE) {
handleHoverEvent(motionEvent, eventDispatcher, surfaceId);
handleHoverEvent(motionEvent, eventDispatcher, surfaceId, hitPath);
return;
}

Expand All @@ -100,9 +106,12 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime);

if (!supportsHover) {
eventDispatcher.dispatchEvent(
PointerEvent.obtain(
PointerEventHelper.POINTER_ENTER, surfaceId, targetTag, motionEvent));
// Enter root -> child
for (int i = hitPath.size(); i-- > 0; ) {
int tag = hitPath.get(i);
eventDispatcher.dispatchEvent(
PointerEvent.obtain(PointerEventHelper.POINTER_ENTER, surfaceId, tag, motionEvent));
}
}
eventDispatcher.dispatchEvent(
PointerEvent.obtain(PointerEventHelper.POINTER_DOWN, surfaceId, targetTag, motionEvent));
Expand All @@ -113,12 +122,6 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
// 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);

if (!supportsHover) {
eventDispatcher.dispatchEvent(
PointerEvent.obtain(
PointerEventHelper.POINTER_ENTER, surfaceId, targetTag, motionEvent));
}
eventDispatcher.dispatchEvent(
PointerEvent.obtain(PointerEventHelper.POINTER_DOWN, surfaceId, targetTag, motionEvent));

Expand Down Expand Up @@ -153,16 +156,18 @@ public void handleMotionEvent(MotionEvent motionEvent, EventDispatcher eventDisp
PointerEvent.obtain(PointerEventHelper.POINTER_UP, surfaceId, targetTag, motionEvent));

if (!supportsHover) {
eventDispatcher.dispatchEvent(
PointerEvent.obtain(
PointerEventHelper.POINTER_LEAVE, surfaceId, targetTag, motionEvent));
// Leave child -> root
for (int i = 0; i < hitPath.size(); i++) {
int tag = hitPath.get(i);
eventDispatcher.dispatchEvent(
PointerEvent.obtain(PointerEventHelper.POINTER_LEAVE, surfaceId, tag, motionEvent));
}
}
return;
}

if (action == MotionEvent.ACTION_CANCEL) {
dispatchCancelEvent(targetTag, motionEvent, eventDispatcher);

dispatchCancelEvent(hitPath, motionEvent, eventDispatcher);
return;
}

Expand All @@ -184,7 +189,10 @@ private int findTargetTagAndSetCoordinates(MotionEvent ev) {

// called on hover_move motion events only
private void handleHoverEvent(
MotionEvent motionEvent, EventDispatcher eventDispatcher, int surfaceId) {
MotionEvent motionEvent,
EventDispatcher eventDispatcher,
int surfaceId,
List<Integer> hitPath) {

int action = motionEvent.getActionMasked();
if (action != MotionEvent.ACTION_HOVER_MOVE) {
Expand All @@ -209,19 +217,15 @@ private void handleHoverEvent(
mTouchEventCoalescingKeyHelper.addCoalescingKey(mHoverInteractionKey);
}

List<Integer> currHitState =
TouchTargetHelper.findTargetPathAndCoordinatesForTouch(
x, y, mRootViewGroup, mTargetCoordinates);

// If child is handling, eliminate target tags under handling child
if (mChildHandlingNativeGesture > 0) {
int index = currHitState.indexOf(mChildHandlingNativeGesture);
int index = hitPath.indexOf(mChildHandlingNativeGesture);
if (index > 0) {
currHitState.subList(0, index).clear();
hitPath.subList(0, index).clear();
}
}

int targetTag = currHitState.size() > 0 ? currHitState.get(0) : -1;
int targetTag = hitPath.size() > 0 ? hitPath.get(0) : -1;
// If targetTag is empty, we should bail?
if (targetTag == -1) {
return;
Expand All @@ -231,24 +235,25 @@ private void handleHoverEvent(
// Traverse hitState back-to-front to find the first divergence with mLastHitState
// FIXME: this may generate incorrect events when view collapsing changes the hierarchy
int firstDivergentIndex = 0;
while (firstDivergentIndex < Math.min(currHitState.size(), mLastHitState.size())
&& currHitState
.get(currHitState.size() - 1 - firstDivergentIndex)
.equals(mLastHitState.get(mLastHitState.size() - 1 - firstDivergentIndex))) {
while (firstDivergentIndex < Math.min(hitPath.size(), mLastHitPath.size())
&& hitPath
.get(hitPath.size() - 1 - firstDivergentIndex)
.equals(mLastHitPath.get(mLastHitPath.size() - 1 - firstDivergentIndex))) {
firstDivergentIndex++;
}

boolean hasDiverged = firstDivergentIndex < Math.max(currHitState.size(), mLastHitState.size());
boolean hasDiverged = firstDivergentIndex < Math.max(hitPath.size(), mLastHitPath.size());

// Fire all relevant enter events
if (hasDiverged) {
// If something has changed in either enter/exit, let's start a new coalescing key
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey);

List<Integer> enterTargetTags =
currHitState.subList(0, currHitState.size() - firstDivergentIndex);
List<Integer> enterTargetTags = hitPath.subList(0, hitPath.size() - firstDivergentIndex);
if (enterTargetTags.size() > 0) {
for (Integer enterTargetTag : enterTargetTags) {
// root -> child
for (int i = enterTargetTags.size(); i-- > 0; ) {
int enterTargetTag = enterTargetTags.get(i);
eventDispatcher.dispatchEvent(
PointerEvent.obtain(
PointerEventHelper.POINTER_ENTER, surfaceId, enterTargetTag, motionEvent));
Expand All @@ -257,8 +262,9 @@ private void handleHoverEvent(

// Fire all relevant exit events
List<Integer> exitTargetTags =
mLastHitState.subList(0, mLastHitState.size() - firstDivergentIndex);
mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndex);
if (exitTargetTags.size() > 0) {
// child -> root
for (Integer exitTargetTag : exitTargetTags) {
eventDispatcher.dispatchEvent(
PointerEvent.obtain(
Expand All @@ -272,13 +278,13 @@ private void handleHoverEvent(
PointerEvent.obtain(
PointerEventHelper.POINTER_MOVE, surfaceId, targetTag, motionEvent, coalescingKey));

mLastHitState = currHitState;
mLastHitPath = hitPath;
mLastEventCoordinates[0] = x;
mLastEventCoordinates[1] = y;
}

private void dispatchCancelEvent(
int targetTag, MotionEvent motionEvent, EventDispatcher eventDispatcher) {
List<Integer> hitPath, MotionEvent motionEvent, EventDispatcher eventDispatcher) {
// This means the gesture has already ended, via some other CANCEL or UP event. This is not
// expected to happen very often as it would mean some child View has decided to intercept the
// touch stream and start a native gesture only upon receiving the UP/CANCEL event.
Expand All @@ -288,13 +294,17 @@ private void dispatchCancelEvent(
"Expected to not have already sent a cancel for this gesture");
int surfaceId = UIManagerHelper.getSurfaceId(mRootViewGroup);

int targetTag = hitPath.get(0);
// Question: Does cancel fire on all in hit path?
Assertions.assertNotNull(eventDispatcher)
.dispatchEvent(
PointerEvent.obtain(
PointerEventHelper.POINTER_CANCEL, surfaceId, targetTag, motionEvent));

eventDispatcher.dispatchEvent(
PointerEvent.obtain(PointerEventHelper.POINTER_LEAVE, surfaceId, targetTag, motionEvent));
for (int tag : hitPath) {
eventDispatcher.dispatchEvent(
PointerEvent.obtain(PointerEventHelper.POINTER_LEAVE, surfaceId, tag, motionEvent));
}

mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
mDownStartTime = TouchEvent.UNSET;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ function EventfulView(props: {|
const listeners = listen
? {
onPointerUp: eventLog('up'),
onPointerUpCapture: eventLog('up capture'),
onPointerDown: eventLog('down'),
onPointerDownCapture: eventLog('down capture'),
onPointerLeave2: eventLog('leave'),
onPointerLeave2Capture: eventLog('leave capture'),
onPointerEnter2: eventLog('enter'),
onPointerEnter2Capture: eventLog('enter capture'),
}
: Object.freeze({});

Expand Down

0 comments on commit de09bd3

Please sign in to comment.