From 9244a8d4cff6a90c51095bd544ac9f59e32a2868 Mon Sep 17 00:00:00 2001 From: Alex Danoff Date: Thu, 6 Jul 2023 09:00:57 -0700 Subject: [PATCH 1/3] include modifier key properties in Android pointer events Differential Revision: D47162963 fbshipit-source-id: 83853238049dc64b699eabc0dc97da69d4c069ae --- .../facebook/react/uimanager/events/PointerEvent.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java index 02b99115070478..d0f221d127de7c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java @@ -7,6 +7,7 @@ package com.facebook.react.uimanager.events; +import android.view.KeyEvent; import android.view.MotionEvent; import androidx.annotation.Nullable; import androidx.core.util.Pools; @@ -183,6 +184,13 @@ private List createW3CPointerEvents() { return w3cPointerEvents; } + private void addModifierKeyData(WritableMap pointerEvent, int modifierKeyMask) { + pointerEvent.putBoolean("ctrlKey", (modifierKeyMask & KeyEvent.META_CTRL_ON) != 0); + pointerEvent.putBoolean("shiftKey", (modifierKeyMask & KeyEvent.META_SHIFT_ON) != 0); + pointerEvent.putBoolean("altKey", (modifierKeyMask & KeyEvent.META_ALT_ON) != 0); + pointerEvent.putBoolean("metaKey", (modifierKeyMask & KeyEvent.META_META_ON) != 0); + } + private WritableMap createW3CPointerEvent(int index) { WritableMap pointerEvent = Arguments.createMap(); int pointerId = mMotionEvent.getPointerId(index); @@ -254,6 +262,8 @@ private WritableMap createW3CPointerEvent(int index) { pointerEvent.putDouble("pressure", pressure); pointerEvent.putDouble("tangentialPressure", 0.0); + addModifierKeyData(pointerEvent, mMotionEvent.getMetaState()); + return pointerEvent; } From f5ffbb2583033932e1dd26af1f11b4e5b98dd378 Mon Sep 17 00:00:00 2001 From: Alex Danoff Date: Thu, 6 Jul 2023 09:00:57 -0700 Subject: [PATCH 2/3] W3CPointerEvents: fix unintentional shallow copy Differential Revision: D47230953 fbshipit-source-id: b6b3159b1520060a9aedb035c6605de3c93d83d1 --- .../facebook/react/uimanager/JSPointerDispatcher.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java index eb970dfbfb443f..4d61f51c56f358 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java @@ -674,16 +674,14 @@ private PointerEventState normalizeToRoot(PointerEventState original, float root Map newOffsets = new HashMap<>(original.getOffsetByPointerId()); Map newEventCoords = new HashMap<>(original.getEventCoordinatesByPointerId()); + float[] rootOffset = {rootX, rootY}; for (Map.Entry offsetEntry : newOffsets.entrySet()) { - float[] offsetValue = offsetEntry.getValue(); - offsetValue[0] = rootX; - offsetValue[1] = rootY; + offsetEntry.setValue(rootOffset); } + float[] zeroOffset = {0, 0}; for (Map.Entry eventCoordsEntry : newEventCoords.entrySet()) { - float[] eventCoordsValue = eventCoordsEntry.getValue(); - eventCoordsValue[0] = 0; - eventCoordsValue[1] = 0; + eventCoordsEntry.setValue(zeroOffset); } return new PointerEventState( From dd970d02c7ca88e1e866e72ed36830f15cd07d97 Mon Sep 17 00:00:00 2001 From: Alex Danoff Date: Thu, 6 Jul 2023 09:01:22 -0700 Subject: [PATCH 3/3] W3CPointerEvents: include screen coordinates in pointer events (#38222) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/38222 Changelog: [Android] [Fixed] - W3CPointerEvents: include screen coordinates in pointer events The [spec](https://www.w3.org/TR/uievents/#idl-mouseevent) says there should be properties on mouse events (and hence pointer events) indicating the screen coordinates of the event (i.e. coords relative to screen of the device). This change adds those properties. Reviewed By: javache Differential Revision: D47162962 fbshipit-source-id: 59361c4032b6aa0204b7f8d09309cb3f3a25616d --- .../react/uimanager/JSPointerDispatcher.java | 23 +++++++++++++++++++ .../react/uimanager/events/PointerEvent.java | 13 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java index 4d61f51c56f358..d65ecbea8bcfbc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java @@ -50,6 +50,8 @@ public class JSPointerDispatcher { private int mLastButtonState = 0; private final ViewGroup mRootViewGroup; + private static final int[] sRootScreenCoords = {0, 0}; + // Set globally for hover interactions, referenced for coalescing hover events public JSPointerDispatcher(ViewGroup viewGroup) { @@ -255,10 +257,21 @@ private void onDown( } } + private float[] eventCoordsToScreenCoords(float[] eventCoords) { + float[] screenCoords = new float[2]; + mRootViewGroup.getLocationOnScreen(sRootScreenCoords); + + screenCoords[0] = eventCoords[0] + sRootScreenCoords[0]; + screenCoords[1] = eventCoords[1] + sRootScreenCoords[1]; + + return screenCoords; + } + private PointerEventState createEventState(int activePointerId, MotionEvent motionEvent) { Map offsetByPointerId = new HashMap(); Map> hitPathByPointerId = new HashMap>(); Map eventCoordinatesByPointerId = new HashMap(); + Map screenCoordinatesByPointerId = new HashMap(); for (int index = 0; index < motionEvent.getPointerCount(); index++) { float[] offsetCoordinates = new float[2]; float[] eventCoordinates = new float[] {motionEvent.getX(index), motionEvent.getY(index)}; @@ -270,6 +283,7 @@ private PointerEventState createEventState(int activePointerId, MotionEvent moti offsetByPointerId.put(pointerId, offsetCoordinates); hitPathByPointerId.put(pointerId, hitPath); eventCoordinatesByPointerId.put(pointerId, eventCoordinates); + screenCoordinatesByPointerId.put(pointerId, eventCoordsToScreenCoords(eventCoordinates)); } int surfaceId = UIManagerHelper.getSurfaceId(mRootViewGroup); @@ -282,6 +296,7 @@ private PointerEventState createEventState(int activePointerId, MotionEvent moti offsetByPointerId, hitPathByPointerId, eventCoordinatesByPointerId, + screenCoordinatesByPointerId, mHoveringPointerIds); // Creates a copy of hovering pointer ids, as they may be updated } @@ -673,6 +688,8 @@ private int[] getChildOffsetRelativeToRoot(View childView) { private PointerEventState normalizeToRoot(PointerEventState original, float rootX, float rootY) { Map newOffsets = new HashMap<>(original.getOffsetByPointerId()); Map newEventCoords = new HashMap<>(original.getEventCoordinatesByPointerId()); + Map newScreenCoords = + new HashMap<>(original.getScreenCoordinatesByPointerId()); float[] rootOffset = {rootX, rootY}; for (Map.Entry offsetEntry : newOffsets.entrySet()) { @@ -684,6 +701,11 @@ private PointerEventState normalizeToRoot(PointerEventState original, float root eventCoordsEntry.setValue(zeroOffset); } + float[] screenCoords = eventCoordsToScreenCoords(rootOffset); + for (Map.Entry screenCoordsEntry : newScreenCoords.entrySet()) { + screenCoordsEntry.setValue(screenCoords); + } + return new PointerEventState( original.getPrimaryPointerId(), original.getActivePointerId(), @@ -692,6 +714,7 @@ private PointerEventState normalizeToRoot(PointerEventState original, float root newOffsets, new HashMap<>(original.getHitPathByPointerId()), newEventCoords, + newScreenCoords, new HashSet<>(original.getHoveringPointerIds())); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java index d0f221d127de7c..66f3c1bdcfc12c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java @@ -215,6 +215,12 @@ private WritableMap createW3CPointerEvent(int index) { pointerEvent.putDouble("clientX", clientX); pointerEvent.putDouble("clientY", clientY); + float[] screenCoords = mEventState.getScreenCoordinatesByPointerId().get(pointerId); + double screenX = PixelUtil.toDIPFromPixel(screenCoords[0]); + double screenY = PixelUtil.toDIPFromPixel(screenCoords[1]); + pointerEvent.putDouble("screenX", screenX); + pointerEvent.putDouble("screenY", screenY); + // x,y values are aliases of clientX, clientY pointerEvent.putDouble("x", clientX); pointerEvent.putDouble("y", clientY); @@ -338,6 +344,7 @@ public static class PointerEventState { private Map mOffsetByPointerId; private Map> mHitPathByPointerId; private Map mEventCoordinatesByPointerId; + private Map mScreenCoordinatesByPointerId; private Set mHoveringPointerIds; public PointerEventState( @@ -348,6 +355,7 @@ public PointerEventState( Map offsetByPointerId, Map> hitPathByPointerId, Map eventCoordinatesByPointerId, + Map screenCoordinatesByPointerId, Set hoveringPointerIds) { mPrimaryPointerId = primaryPointerId; mActivePointerId = activePointerId; @@ -356,6 +364,7 @@ public PointerEventState( mOffsetByPointerId = offsetByPointerId; mHitPathByPointerId = hitPathByPointerId; mEventCoordinatesByPointerId = eventCoordinatesByPointerId; + mScreenCoordinatesByPointerId = screenCoordinatesByPointerId; mHoveringPointerIds = new HashSet<>(hoveringPointerIds); } @@ -395,6 +404,10 @@ public final Map getEventCoordinatesByPointerId() { return mEventCoordinatesByPointerId; } + public final Map getScreenCoordinatesByPointerId() { + return mScreenCoordinatesByPointerId; + } + public final List getHitPathForActivePointer() { return mHitPathByPointerId.get(mActivePointerId); }