mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
W3CPointerEvents: properly update hit path during native gestures (#37638)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37638 Changelog: [Android] [Fixed] - W3CPointerEvents: properly update hit path during native gestures Per [the W3C spec](https://www.w3.org/TR/pointerevents/#the-pointercancel-event), we need to fire pointerout and pointerleave after firing a pointercancel. However, in cases where the pointer doesn't physically leave the target after a cancel (e.g. scrolling by clicking and dragging), we would never re-fire a pointerenter event once the native gesture was completed. This change fixes the bug by clearing out the last hit path (and other relevant state) for the pointer when we start handling a native gesture. Then we'll re-fire a pointerenter as expected upon the next motion event (due to the logic in handleHitStateDivergence). Note: this bug only affected hovering pointers (e.g. mouse) because for non-hovering pointers the native gesture won't end unless the pointer is physically removed (i.e. finger is lifted). Reviewed By: javache Differential Revision: D46226021 fbshipit-source-id: de796217bac27b169ccf9ed5e0df85f7b63d8c5e
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3f0caedd4d
commit
396cdac629
+26
-8
@@ -70,6 +70,9 @@ public class JSPointerDispatcher {
|
||||
|
||||
dispatchCancelEventForTarget(childView, motionInRoot, eventDispatcher);
|
||||
mChildHandlingNativeGesture = childView.getId();
|
||||
|
||||
// clear "previous" state since interaction was canceled
|
||||
resetPreviousStateForMotionEvent(motionEvent);
|
||||
}
|
||||
|
||||
private MotionEvent convertMotionToRootFrame(View childView, MotionEvent childMotion) {
|
||||
@@ -84,6 +87,28 @@ public class JSPointerDispatcher {
|
||||
return motionInRoot;
|
||||
}
|
||||
|
||||
private void updatePreviousStateFromEvent(MotionEvent event, PointerEventState eventState) {
|
||||
// Caching the event state so we have a new "last"
|
||||
mLastHitPathByPointerId = eventState.getHitPathByPointerId();
|
||||
mLastEventCoordinatesByPointerId = eventState.getEventCoordinatesByPointerId();
|
||||
mLastButtonState = event.getButtonState();
|
||||
|
||||
// Clean up any stale pointerIds
|
||||
Set<Integer> allPointerIds = mLastEventCoordinatesByPointerId.keySet();
|
||||
mHoveringPointerIds.retainAll(allPointerIds);
|
||||
}
|
||||
|
||||
private void resetPreviousStateForMotionEvent(MotionEvent event) {
|
||||
int activePointerId = event.getPointerId(event.getActionIndex());
|
||||
if (mLastHitPathByPointerId != null) {
|
||||
mLastHitPathByPointerId.remove(activePointerId);
|
||||
}
|
||||
if (mLastEventCoordinatesByPointerId != null) {
|
||||
mLastEventCoordinatesByPointerId.remove(activePointerId);
|
||||
}
|
||||
mLastButtonState = 0;
|
||||
}
|
||||
|
||||
public void onChildEndedNativeGesture() {
|
||||
// There should be only one child gesture at any given time. We can safely turn off the flag.
|
||||
mChildHandlingNativeGesture = -1;
|
||||
@@ -368,14 +393,7 @@ public class JSPointerDispatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
// Caching the event state so we have a new "last"
|
||||
mLastHitPathByPointerId = eventState.getHitPathByPointerId();
|
||||
mLastEventCoordinatesByPointerId = eventState.getEventCoordinatesByPointerId();
|
||||
mLastButtonState = motionEvent.getButtonState();
|
||||
|
||||
// Clean up any stale pointerIds
|
||||
Set<Integer> allPointerIds = mLastEventCoordinatesByPointerId.keySet();
|
||||
mHoveringPointerIds.retainAll(allPointerIds);
|
||||
updatePreviousStateFromEvent(motionEvent, eventState);
|
||||
}
|
||||
|
||||
private static boolean isAnyoneListeningForBubblingEvent(
|
||||
|
||||
Reference in New Issue
Block a user