Skip to content

Commit

Permalink
Track observed pointer-events using a single bitset
Browse files Browse the repository at this point in the history
Summary:
Noticed that we were using a different tag for each pointer event observed (and that weren't clearing these properly when recyling views).

Each of these tags is a hashmap entry and a boxed boolean, so we can do much better by just using a single (boxed) integer.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D40077519

fbshipit-source-id: 130e78c3641eaedfa8787644b98763fd4367bc27
  • Loading branch information
javache authored and facebook-github-bot committed Oct 7, 2022
1 parent 7680bde commit 2afcea2
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.PointerEventHelper;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -68,9 +69,7 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
protected T prepareToRecycleView(@NonNull ThemedReactContext reactContext, T view) {
// Reset tags
view.setTag(null);
view.setTag(R.id.pointer_enter, null);
view.setTag(R.id.pointer_leave, null);
view.setTag(R.id.pointer_move, null);
view.setTag(R.id.pointer_events, null);
view.setTag(R.id.react_test_id, null);
view.setTag(R.id.view_tag_native_id, null);
view.setTag(R.id.labelled_by, null);
Expand Down Expand Up @@ -639,55 +638,63 @@ private void logUnsupportedPropertyWarning(String propName) {
FLog.w(ReactConstants.TAG, "%s doesn't support property '%s'", getName(), propName);
}

private static void setPointerEventsFlag(
@NonNull View view, PointerEventHelper.EVENT event, boolean isListening) {
Integer tag = (Integer) view.getTag(R.id.pointer_events);
int currentValue = tag != null ? tag.intValue() : 0;
int flag = 1 << event.ordinal();
view.setTag(R.id.pointer_events, isListening ? (currentValue | flag) : (currentValue & ~flag));
}

/* Experimental W3C Pointer events start */
@ReactProp(name = "onPointerEnter")
public void setPointerEnter(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_enter, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.ENTER, value);
}

@ReactProp(name = "onPointerEnterCapture")
public void setPointerEnterCapture(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_enter_capture, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.ENTER_CAPTURE, value);
}

@ReactProp(name = "onPointerOver")
public void setPointerOver(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_over, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.OVER, value);
}

@ReactProp(name = "onPointerOverCapture")
public void setPointerOverCapture(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_over_capture, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.OVER_CAPTURE, value);
}

@ReactProp(name = "onPointerOut")
public void setPointerOut(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_out, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.OUT, value);
}

@ReactProp(name = "onPointerOutCapture")
public void setPointerOutCapture(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_out_capture, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.OUT_CAPTURE, value);
}

@ReactProp(name = "onPointerLeave")
public void setPointerLeave(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_leave, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.LEAVE, value);
}

@ReactProp(name = "onPointerLeaveCapture")
public void setPointerLeaveCapture(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_leave_capture, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.LEAVE_CAPTURE, value);
}

@ReactProp(name = "onPointerMove")
public void setPointerMove(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_move, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.MOVE, value);
}

@ReactProp(name = "onPointerMoveCapture")
public void setPointerMoveCapture(@NonNull T view, boolean value) {
view.setTag(R.id.pointer_move_capture, value);
setPointerEventsFlag(view, PointerEventHelper.EVENT.MOVE_CAPTURE, value);
}

/* Experimental W3C Pointer events end */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ public static boolean isListening(@Nullable View view, EVENT event) {
return false;
}

Object value = null;
switch (event) {
case DOWN:
case DOWN_CAPTURE:
Expand All @@ -136,44 +135,11 @@ public static boolean isListening(@Nullable View view, EVENT event) {
case CANCEL:
case CANCEL_CAPTURE:
return true;
case ENTER:
value = view.getTag(R.id.pointer_enter);
break;
case ENTER_CAPTURE:
value = view.getTag(R.id.pointer_enter_capture);
break;
case LEAVE:
value = view.getTag(R.id.pointer_leave);
break;
case LEAVE_CAPTURE:
value = view.getTag(R.id.pointer_leave_capture);
break;
case MOVE:
value = view.getTag(R.id.pointer_move);
break;
case MOVE_CAPTURE:
value = view.getTag(R.id.pointer_move_capture);
break;
case OVER:
value = view.getTag(R.id.pointer_over);
break;
case OVER_CAPTURE:
value = view.getTag(R.id.pointer_over_capture);
break;
case OUT:
value = view.getTag(R.id.pointer_out);
break;
case OUT_CAPTURE:
value = view.getTag(R.id.pointer_out_capture);
break;
}

if (value == null) {
return false;
}

if (value instanceof Boolean) {
return (Boolean) value;
Integer pointerEvents = (Integer) view.getTag(R.id.pointer_events);
if (pointerEvents != null) {
return (pointerEvents.intValue() & (1 << event.ordinal())) != 0;
}
return false;
}
Expand Down
43 changes: 13 additions & 30 deletions ReactAndroid/src/main/res/views/uimanager/values/ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,36 @@
<!-- tag is used to store the nativeID tag -->
<item type="id" name="view_tag_instance_handle"/>

<!--tag is used to store accessibilityHint tag-->
<!-- tag is used to store accessibilityHint tag-->
<item type="id" name="accessibility_hint"/>

<!--tag is used to store accessibilityRole tag-->
<!-- tag is used to store accessibilityRole tag-->
<item type="id" name="accessibility_role"/>

<!--tag is used to store accessibilityCollection -->
<!-- tag is used to store accessibilityCollection -->
<item type="id" name="accessibility_collection"/>

<!--tag is used to store accessibilityCollectionItem -->
<!-- tag is used to store accessibilityCollectionItem -->
<item type="id" name="accessibility_collection_item"/>

<!--tag is used to store accessibilityState -->
<!-- tag is used to store accessibilityState -->
<item type="id" name="accessibility_state"/>

<!--tag is used to store accessibilityLabel tag-->
<!-- tag is used to store accessibilityLabel tag-->
<item type="id" name="accessibility_label"/>

<!--tag is used to store accessibilityActions tag-->
<!-- tag is used to store accessibilityActions tag-->
<item type="id" name="accessibility_actions"/>

<!--tag is used to store accessibilityValue tag -->
<!-- tag is used to store accessibilityValue tag -->
<item type="id" name="accessibility_value"/>

<!--tag is used to store accessibilityLinks tag -->
<!-- tag is used to store accessibilityLinks tag -->
<item type="id" name="accessibility_links"/>
<!--tag is used to store accessibilityLabelledBy tag -->
<item type="id" name="labelled_by"/>

<!-- tag is used to store if a view is subscribed to the pointerenter event -->
<item type="id" name="pointer_enter"/>
<item type="id" name="pointer_enter_capture"/>

<!-- tag is used to store if a view is subscribed to the pointerleave event -->
<item type="id" name="pointer_leave"/>
<item type="id" name="pointer_leave_capture"/>

<!-- tag is used to store if a view is subscribed to the pointermove event -->
<item type="id" name="pointer_move"/>
<item type="id" name="pointer_move_capture"/>

<!-- tag is used to store if a view is subscribed to the pointerout event -->
<item type="id" name="pointer_out"/>
<item type="id" name="pointer_out_capture"/>

<!-- tag is used to store if a view is subscribed to the pointerover event -->
<item type="id" name="pointer_over"/>
<item type="id" name="pointer_over_capture"/>
<!-- tag is used to store accessibilityLabelledBy tag -->
<item type="id" name="labelled_by"/>

<!-- tag is used store bitset of pointer events observed -->
<item type="id" name="pointer_events"/>
</resources>

0 comments on commit 2afcea2

Please sign in to comment.