Skip to content

Commit 21a4c1f

Browse files
vincentriemerfacebook-github-bot
authored andcommitted
Add dispatch of pointerover/pointerout events
Summary: Changelog: [iOS][Internal] - Add dispatching of pointerover/pointerout events Reviewed By: lunaleaps Differential Revision: D36718156 fbshipit-source-id: c2fee2332ac52e617db938e321e3b38fd5c35ad3
1 parent 43f831b commit 21a4c1f

File tree

8 files changed

+82
-0
lines changed

8 files changed

+82
-0
lines changed

Libraries/NativeComponent/BaseViewConfig.ios.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,18 @@ const bubblingEventTypes = {
125125
skipBubbling: true,
126126
},
127127
},
128+
topPointerOver: {
129+
phasedRegistrationNames: {
130+
captured: 'onPointerOverCapture',
131+
bubbled: 'onPointerOver',
132+
},
133+
},
134+
topPointerOut: {
135+
phasedRegistrationNames: {
136+
captured: 'onPointerOutCapture',
137+
bubbled: 'onPointerOut',
138+
},
139+
},
128140
};
129141

130142
const directEventTypes = {
@@ -315,6 +327,8 @@ const validAttributesForEventProps = ConditionallyIgnoredEventHandlers({
315327
onPointerEnter2: true,
316328
onPointerMove2: true,
317329
onPointerLeave2: true,
330+
onPointerOver: true,
331+
onPointerOut: true,
318332
});
319333

320334
/**

React/Fabric/RCTSurfaceTouchHandler.mm

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,16 @@ static BOOL IsViewListeningToEvent(UIView *view, ViewEvents::Offset eventType)
232232
return NO;
233233
}
234234

235+
static BOOL IsAnyViewInPathListeningToEvent(NSOrderedSet<UIView *> *viewPath, ViewEvents::Offset eventType)
236+
{
237+
for (UIView *view in viewPath) {
238+
if (IsViewListeningToEvent(view, eventType)) {
239+
return YES;
240+
}
241+
}
242+
return NO;
243+
}
244+
235245
/**
236246
* Surprisingly, `__unsafe_unretained id` pointers are not regular pointers
237247
* and `std::hash<>` cannot hash them.
@@ -563,11 +573,22 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0))
563573

564574
UIView *targetView = [listenerView hitTest:clientLocation withEvent:nil];
565575
targetView = FindClosestFabricManagedTouchableView(targetView);
576+
UIView *prevTargetView = [_currentlyHoveredViews firstObject];
566577

567578
NSOrderedSet *eventPathViews = GetTouchableViewsInPathToRoot(targetView);
568579

569580
NSTimeInterval timestamp = CACurrentMediaTime();
570581

582+
// Over
583+
if (prevTargetView != targetView) {
584+
BOOL shouldEmitOverEvent = IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerOver);
585+
SharedTouchEventEmitter eventEmitter = GetTouchEmitterFromView(targetView, [recognizer locationInView:targetView]);
586+
if (shouldEmitOverEvent && eventEmitter != nil) {
587+
PointerEvent event = CreatePointerEventFromIncompleteHoverData(targetView, clientLocation, timestamp);
588+
eventEmitter->onPointerOver(event);
589+
}
590+
}
591+
571592
// Entering
572593

573594
// We only want to emit events to JS if there is a view that is currently listening to said event
@@ -595,6 +616,17 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0))
595616
}
596617
}
597618

619+
// Out
620+
if (prevTargetView != targetView) {
621+
BOOL shouldEmitOutEvent = IsAnyViewInPathListeningToEvent(_currentlyHoveredViews, ViewEvents::Offset::PointerOut);
622+
SharedTouchEventEmitter eventEmitter =
623+
GetTouchEmitterFromView(prevTargetView, [recognizer locationInView:prevTargetView]);
624+
if (shouldEmitOutEvent && eventEmitter != nil) {
625+
PointerEvent event = CreatePointerEventFromIncompleteHoverData(prevTargetView, clientLocation, timestamp);
626+
eventEmitter->onPointerOut(event);
627+
}
628+
}
629+
598630
// Leaving
599631

600632
// pointerleave events need to be emited from the deepest target to the root but

React/Views/RCTView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,7 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
112112
@property (nonatomic, assign) RCTBubblingEventBlock onPointerUp;
113113
@property (nonatomic, assign) RCTCapturingEventBlock onPointerEnter2;
114114
@property (nonatomic, assign) RCTCapturingEventBlock onPointerLeave2;
115+
@property (nonatomic, assign) RCTBubblingEventBlock onPointerOver;
116+
@property (nonatomic, assign) RCTBubblingEventBlock onPointerOut;
115117

116118
@end

React/Views/RCTViewManager.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,5 +452,7 @@ - (RCTShadowView *)shadowView
452452
RCT_EXPORT_VIEW_PROPERTY(onPointerUp, RCTBubblingEventBlock)
453453
RCT_EXPORT_VIEW_PROPERTY(onPointerEnter2, RCTCapturingEventBlock)
454454
RCT_EXPORT_VIEW_PROPERTY(onPointerLeave2, RCTCapturingEventBlock)
455+
RCT_EXPORT_VIEW_PROPERTY(onPointerOver, RCTBubblingEventBlock)
456+
RCT_EXPORT_VIEW_PROPERTY(onPointerOut, RCTBubblingEventBlock)
455457

456458
@end

ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,21 @@ void TouchEventEmitter::onPointerLeave2(const PointerEvent &event) const {
177177
RawEvent::Category::ContinuousEnd);
178178
}
179179

180+
void TouchEventEmitter::onPointerOver(const PointerEvent &event) const {
181+
dispatchPointerEvent(
182+
"pointerOver",
183+
event,
184+
EventPriority::AsynchronousBatched,
185+
RawEvent::Category::ContinuousStart);
186+
}
187+
188+
void TouchEventEmitter::onPointerOut(const PointerEvent &event) const {
189+
dispatchPointerEvent(
190+
"pointerOut",
191+
event,
192+
EventPriority::AsynchronousBatched,
193+
RawEvent::Category::ContinuousStart);
194+
}
195+
180196
} // namespace react
181197
} // namespace facebook

ReactCommon/react/renderer/components/view/TouchEventEmitter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class TouchEventEmitter : public EventEmitter {
3636
void onPointerUp(PointerEvent const &event) const;
3737
void onPointerEnter2(PointerEvent const &event) const;
3838
void onPointerLeave2(PointerEvent const &event) const;
39+
void onPointerOver(PointerEvent const &event) const;
40+
void onPointerOut(PointerEvent const &event) const;
3941

4042
private:
4143
void dispatchTouchEvent(

ReactCommon/react/renderer/components/view/primitives.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ struct ViewEvents {
5757
PointerEnter2Capture = 23,
5858
PointerLeave2Capture = 24,
5959
PointerMove2Capture = 25,
60+
PointerOver = 26,
61+
PointerOut = 27,
6062
};
6163

6264
constexpr bool operator[](const Offset offset) const {

ReactCommon/react/renderer/components/view/propsConversions.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,18 @@ static inline ViewEvents convertRawProp(
529529
"onPointerLeave2Capture",
530530
sourceValue[Offset::PointerLeave2Capture],
531531
defaultValue[Offset::PointerLeave2Capture]);
532+
result[Offset::PointerOver] = convertRawProp(
533+
context,
534+
rawProps,
535+
"onPointerOver",
536+
sourceValue[Offset::PointerOver],
537+
defaultValue[Offset::PointerOver]);
538+
result[Offset::PointerOut] = convertRawProp(
539+
context,
540+
rawProps,
541+
"onPointerOut",
542+
sourceValue[Offset::PointerOut],
543+
defaultValue[Offset::PointerOut]);
532544

533545
// PanResponder callbacks
534546
result[Offset::MoveShouldSetResponder] = convertRawProp(

0 commit comments

Comments
 (0)