Skip to content

Commit 6c66bde

Browse files
authored
Pointer events (#14713)
* PointerEvent fixes * Change files * format
1 parent 02e7363 commit 6c66bde

File tree

6 files changed

+67
-57
lines changed

6 files changed

+67
-57
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "PointerEvent fixes",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -636,17 +636,6 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
636636

637637
auto eventPathViews = GetTouchableViewsInPathToRoot(targetView);
638638

639-
// Over
640-
if (targetView != nullptr && previousTargetTag != targetView.Tag()) {
641-
bool shouldEmitOverEvent =
642-
IsAnyViewInPathListeningToEvent(eventPathViews, facebook::react::ViewEvents::Offset::PointerOver);
643-
const auto eventEmitter = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
644-
->eventEmitterAtPoint(event.offsetPoint);
645-
if (shouldEmitOverEvent && eventEmitter != nullptr) {
646-
eventEmitter->onPointerOver(event);
647-
}
648-
}
649-
650639
// Entering
651640

652641
// We only want to emit events to JS if there is a view that is currently listening to said event
@@ -663,7 +652,6 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
663652
auto componentView = *itComponentView;
664653
bool shouldEmitEvent = componentView != nullptr &&
665654
(hasParentEnterListener ||
666-
IsViewListeningToEvent(componentView, facebook::react::ViewEvents::Offset::PointerEnter) ||
667655
IsViewListeningToEvent(componentView, facebook::react::WindowsViewEvents::Offset::MouseEnter));
668656

669657
if (std::find(currentlyHoveredViews.begin(), currentlyHoveredViews.end(), componentView) ==
@@ -673,16 +661,12 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
673661
m_context, componentView.Tag(), pointerPoint, keyModifiers);
674662
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(componentView)
675663
->OnPointerEntered(args);
676-
677664
if (shouldEmitEvent) {
678665
const auto eventEmitter =
679666
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(componentView)
680667
->eventEmitter();
681-
if (eventEmitter) {
682-
eventEmitter->onPointerEnter(event);
683-
if (IsMousePointerEvent(event)) {
684-
eventEmitter->onMouseEnter(event);
685-
}
668+
if (eventEmitter && IsMousePointerEvent(event)) {
669+
eventEmitter->onMouseEnter(event);
686670
}
687671
}
688672
}
@@ -695,41 +679,25 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
695679
// Call the underlaying pointer handler
696680
handler(eventPathViews);
697681

698-
// Out
699-
if (previousTargetTag != -1 && previousTargetTag != (targetView ? targetView.Tag() : -1)) {
700-
bool shouldEmitOutEvent =
701-
IsAnyViewInPathListeningToEvent(currentlyHoveredViews, facebook::react::ViewEvents::Offset::PointerOut);
702-
const auto eventEmitter =
703-
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(prevTargetView)->eventEmitter();
704-
if (shouldEmitOutEvent && eventEmitter != nullptr) {
705-
eventEmitter->onPointerOut(event);
706-
}
707-
}
708-
709682
// Leaving
710683

711684
// pointerleave events need to be emitted from the deepest target to the root but
712685
// we also need to efficiently keep track of if a view has a parent which is listening to the leave events,
713686
// so we first iterate from the root to the target, collecting the views which need events fired for, of which
714687
// we reverse iterate (now from target to root), actually emitting the events.
715-
std::vector<winrt::Microsoft::ReactNative::ComponentView>
716-
viewsToEmitJSLeaveEventsTo; // NSMutableOrderedSet<UIView *> *viewsToEmitLeaveEventsTo =
717-
// [NSMutableOrderedSet orderedSet];
688+
std::vector<winrt::Microsoft::ReactNative::ComponentView> viewsToEmitJSLeaveEventsTo;
718689

719690
std::vector<winrt::Microsoft::ReactNative::ComponentView> viewsToEmitLeaveEventsTo;
720691

721692
winrt::Microsoft::ReactNative::ComponentView viewToEmitNativeExitedEvent{nullptr};
722693

723694
bool hasParentLeaveListener = false;
724695
for (auto itComponentView = currentlyHoveredViews.rbegin(); itComponentView != currentlyHoveredViews.rend();
725-
itComponentView++) { // for (RCTReactTaggedView *taggedView in [currentlyHoveredViews
726-
// reverseObjectEnumerator])
727-
// {
696+
itComponentView++) {
728697
auto componentView = *itComponentView;
729698

730699
bool shouldEmitJSEvent = componentView != nullptr &&
731700
(hasParentLeaveListener ||
732-
IsViewListeningToEvent(componentView, facebook::react::ViewEvents::Offset::PointerLeave) ||
733701
IsViewListeningToEvent(componentView, facebook::react::WindowsViewEvents::Offset::MouseLeave));
734702

735703
if (std::find(eventPathViews.begin(), eventPathViews.end(), componentView) == eventPathViews.end()) {
@@ -754,17 +722,13 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
754722
}
755723

756724
for (auto itComponentView = viewsToEmitJSLeaveEventsTo.rbegin(); itComponentView != viewsToEmitJSLeaveEventsTo.rend();
757-
itComponentView++) { // for (UIView *componentView in [viewsToEmitJSLeaveEventsTo
758-
// reverseObjectEnumerator]) {
725+
itComponentView++) {
759726
auto componentView = *itComponentView;
760727

761728
const auto eventEmitter =
762729
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(componentView)->eventEmitter();
763-
if (eventEmitter) {
764-
eventEmitter->onPointerLeave(event);
765-
if (IsMousePointerEvent(event)) {
766-
eventEmitter->onMouseLeave(event);
767-
}
730+
if (eventEmitter && IsMousePointerEvent(event)) {
731+
eventEmitter->onMouseLeave(event);
768732
}
769733
}
770734

@@ -1063,19 +1027,17 @@ void CompositionEventHandler::onPointerMoved(
10631027
auto activeTouch = m_activeTouches.find(pointerId);
10641028
bool isActiveTouch = activeTouch != m_activeTouches.end() && activeTouch->second.eventEmitter != nullptr;
10651029

1066-
auto handler = [&targetView, &pointerEvent, isActiveTouch](
1030+
auto handler = [&, targetView, pointerEvent, isActiveTouch](
10671031
std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
10681032
const auto eventEmitter = targetView
10691033
? winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
10701034
->eventEmitterAtPoint(pointerEvent.offsetPoint)
1071-
: nullptr;
1072-
bool hasMoveEventListeners = isActiveTouch ||
1073-
IsAnyViewInPathListeningToEvent(eventPathViews, facebook::react::ViewEvents::Offset::PointerMove) ||
1074-
IsAnyViewInPathListeningToEvent(eventPathViews, facebook::react::ViewEvents::Offset::PointerMoveCapture);
1035+
: RootComponentView().eventEmitterAtPoint(pointerEvent.offsetPoint);
10751036

1076-
if (eventEmitter != nullptr && hasMoveEventListeners) {
1077-
// Add logging before dispatching the event
1037+
if (eventEmitter != nullptr) {
10781038
eventEmitter->onPointerMove(pointerEvent);
1039+
} else {
1040+
ClearAllHoveredForPointer(pointerEvent);
10791041
}
10801042
};
10811043

@@ -1089,6 +1051,23 @@ void CompositionEventHandler::onPointerMoved(
10891051
}
10901052
}
10911053

1054+
void CompositionEventHandler::ClearAllHoveredForPointer(const facebook::react::PointerEvent &pointerEvent) noexcept {
1055+
// special case if we have no target
1056+
// PointerEventsProcessor requires move events to keep track of the hovered components in core.
1057+
// It also treats a onPointerLeave event as a special case that removes the hover state of all currently hovered
1058+
// events. If we get null for the targetView, that means that the mouse is no over any components, so we have no
1059+
// element to send the move event to. However we need to send something so that any previously hovered elements
1060+
// are no longer hovered.
1061+
auto children = RootComponentView().Children();
1062+
if (auto size = children.Size()) {
1063+
auto firstChild = children.GetAt(0);
1064+
if (auto childEventEmitter =
1065+
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(firstChild)->eventEmitter()) {
1066+
childEventEmitter->onPointerLeave(pointerEvent);
1067+
}
1068+
}
1069+
}
1070+
10921071
void CompositionEventHandler::onPointerExited(
10931072
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
10941073
winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
@@ -1111,7 +1090,9 @@ void CompositionEventHandler::onPointerExited(
11111090

11121091
facebook::react::PointerEvent pointerEvent = CreatePointerEventFromIncompleteHoverData(ptScaled, ptLocal);
11131092

1114-
auto handler = [](std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {};
1093+
auto handler = [&](std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
1094+
ClearAllHoveredForPointer(pointerEvent);
1095+
};
11151096

11161097
HandleIncomingPointerEvent(pointerEvent, nullptr, pointerPoint, keyModifiers, handler);
11171098
}

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
9898
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
9999
winrt::Windows::System::VirtualKeyModifiers keyModifiers,
100100
std::function<void(std::vector<winrt::Microsoft::ReactNative::ComponentView> &)> handler);
101+
void ClearAllHoveredForPointer(const facebook::react::PointerEvent &pointerEvent) noexcept;
101102

102103
struct ActiveTouch {
103104
facebook::react::Touch touch;

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewEventEmitter.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ void HostPlatformViewEventEmitter::onBlur() const {
3838

3939
#pragma mark - Mouse Events
4040

41-
void HostPlatformViewEventEmitter::onMouseEnter(PointerEvent const &pointerEvent) const {
42-
dispatchEvent("mouseEnter", std::make_shared<PointerEvent>(pointerEvent), RawEvent::Category::ContinuousStart);
41+
void HostPlatformViewEventEmitter::onMouseEnter(MouseEvent const &pointerEvent) const {
42+
dispatchEvent("mouseEnter", std::make_shared<MouseEvent>(pointerEvent), RawEvent::Category::ContinuousStart);
4343
}
4444

45-
void HostPlatformViewEventEmitter::onMouseLeave(PointerEvent const &pointerEvent) const {
46-
dispatchEvent("mouseLeave", std::make_shared<PointerEvent>(pointerEvent), RawEvent::Category::ContinuousStart);
45+
void HostPlatformViewEventEmitter::onMouseLeave(MouseEvent const &pointerEvent) const {
46+
dispatchEvent("mouseLeave", std::make_shared<MouseEvent>(pointerEvent), RawEvent::Category::ContinuousStart);
4747
}
4848

4949
#pragma mark - Touch Events

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewEventEmitter.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <react/renderer/components/view/BaseViewEventEmitter.h>
77
#include "KeyEvent.h"
8+
#include "MouseEvent.h"
89

910
namespace facebook::react {
1011

@@ -32,8 +33,8 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
3233

3334
#pragma mark - Mouse Events
3435

35-
void onMouseEnter(PointerEvent const &pointerEvent) const;
36-
void onMouseLeave(PointerEvent const &pointerEvent) const;
36+
void onMouseEnter(MouseEvent const &pointerEvent) const;
37+
void onMouseLeave(MouseEvent const &pointerEvent) const;
3738

3839
#pragma mark - Touch Events
3940

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#pragma once
5+
6+
#include <react/renderer/components/view/PointerEvent.h>
7+
8+
namespace facebook::react {
9+
10+
struct MouseEvent : public PointerEvent {
11+
MouseEvent(PointerEvent &event) : PointerEvent(event){};
12+
13+
// We override the type so that it is not recorded as a PointerType,
14+
// otherwise PointerEventsProcessor gets confused by the Windows specific MouseEvents
15+
EventPayloadType getType() const override {
16+
return static_cast<EventPayloadType>(-1);
17+
};
18+
};
19+
20+
} // namespace facebook::react

0 commit comments

Comments
 (0)