Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "PointerEvent fixes",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -636,17 +636,6 @@ void CompositionEventHandler::HandleIncomingPointerEvent(

auto eventPathViews = GetTouchableViewsInPathToRoot(targetView);

// Over
if (targetView != nullptr && previousTargetTag != targetView.Tag()) {
bool shouldEmitOverEvent =
IsAnyViewInPathListeningToEvent(eventPathViews, facebook::react::ViewEvents::Offset::PointerOver);
const auto eventEmitter = winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
->eventEmitterAtPoint(event.offsetPoint);
if (shouldEmitOverEvent && eventEmitter != nullptr) {
eventEmitter->onPointerOver(event);
}
}

// Entering

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

if (std::find(currentlyHoveredViews.begin(), currentlyHoveredViews.end(), componentView) ==
Expand All @@ -673,16 +661,12 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
m_context, componentView.Tag(), pointerPoint, keyModifiers);
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(componentView)
->OnPointerEntered(args);

if (shouldEmitEvent) {
const auto eventEmitter =
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(componentView)
->eventEmitter();
if (eventEmitter) {
eventEmitter->onPointerEnter(event);
if (IsMousePointerEvent(event)) {
eventEmitter->onMouseEnter(event);
}
if (eventEmitter && IsMousePointerEvent(event)) {
eventEmitter->onMouseEnter(event);
}
}
}
Expand All @@ -695,41 +679,25 @@ void CompositionEventHandler::HandleIncomingPointerEvent(
// Call the underlaying pointer handler
handler(eventPathViews);

// Out
if (previousTargetTag != -1 && previousTargetTag != (targetView ? targetView.Tag() : -1)) {
bool shouldEmitOutEvent =
IsAnyViewInPathListeningToEvent(currentlyHoveredViews, facebook::react::ViewEvents::Offset::PointerOut);
const auto eventEmitter =
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(prevTargetView)->eventEmitter();
if (shouldEmitOutEvent && eventEmitter != nullptr) {
eventEmitter->onPointerOut(event);
}
}

// Leaving

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

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

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

bool hasParentLeaveListener = false;
for (auto itComponentView = currentlyHoveredViews.rbegin(); itComponentView != currentlyHoveredViews.rend();
itComponentView++) { // for (RCTReactTaggedView *taggedView in [currentlyHoveredViews
// reverseObjectEnumerator])
// {
itComponentView++) {
auto componentView = *itComponentView;

bool shouldEmitJSEvent = componentView != nullptr &&
(hasParentLeaveListener ||
IsViewListeningToEvent(componentView, facebook::react::ViewEvents::Offset::PointerLeave) ||
IsViewListeningToEvent(componentView, facebook::react::WindowsViewEvents::Offset::MouseLeave));

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

for (auto itComponentView = viewsToEmitJSLeaveEventsTo.rbegin(); itComponentView != viewsToEmitJSLeaveEventsTo.rend();
itComponentView++) { // for (UIView *componentView in [viewsToEmitJSLeaveEventsTo
// reverseObjectEnumerator]) {
itComponentView++) {
auto componentView = *itComponentView;

const auto eventEmitter =
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(componentView)->eventEmitter();
if (eventEmitter) {
eventEmitter->onPointerLeave(event);
if (IsMousePointerEvent(event)) {
eventEmitter->onMouseLeave(event);
}
if (eventEmitter && IsMousePointerEvent(event)) {
eventEmitter->onMouseLeave(event);
}
}

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

auto handler = [&targetView, &pointerEvent, isActiveTouch](
auto handler = [&, targetView, pointerEvent, isActiveTouch](
std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
const auto eventEmitter = targetView
? winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
->eventEmitterAtPoint(pointerEvent.offsetPoint)
: nullptr;
bool hasMoveEventListeners = isActiveTouch ||
IsAnyViewInPathListeningToEvent(eventPathViews, facebook::react::ViewEvents::Offset::PointerMove) ||
IsAnyViewInPathListeningToEvent(eventPathViews, facebook::react::ViewEvents::Offset::PointerMoveCapture);
: RootComponentView().eventEmitterAtPoint(pointerEvent.offsetPoint);

if (eventEmitter != nullptr && hasMoveEventListeners) {
// Add logging before dispatching the event
if (eventEmitter != nullptr) {
eventEmitter->onPointerMove(pointerEvent);
} else {
ClearAllHoveredForPointer(pointerEvent);
}
};

Expand All @@ -1089,6 +1051,23 @@ void CompositionEventHandler::onPointerMoved(
}
}

void CompositionEventHandler::ClearAllHoveredForPointer(const facebook::react::PointerEvent &pointerEvent) noexcept {
// special case if we have no target
// PointerEventsProcessor requires move events to keep track of the hovered components in core.
// It also treats a onPointerLeave event as a special case that removes the hover state of all currently hovered
// events. If we get null for the targetView, that means that the mouse is no over any components, so we have no
// element to send the move event to. However we need to send something so that any previously hovered elements
// are no longer hovered.
auto children = RootComponentView().Children();
if (auto size = children.Size()) {
auto firstChild = children.GetAt(0);
if (auto childEventEmitter =
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(firstChild)->eventEmitter()) {
childEventEmitter->onPointerLeave(pointerEvent);
}
}
}

void CompositionEventHandler::onPointerExited(
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
Expand All @@ -1111,7 +1090,9 @@ void CompositionEventHandler::onPointerExited(

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

auto handler = [](std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {};
auto handler = [&](std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
ClearAllHoveredForPointer(pointerEvent);
};

HandleIncomingPointerEvent(pointerEvent, nullptr, pointerPoint, keyModifiers, handler);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
winrt::Windows::System::VirtualKeyModifiers keyModifiers,
std::function<void(std::vector<winrt::Microsoft::ReactNative::ComponentView> &)> handler);
void ClearAllHoveredForPointer(const facebook::react::PointerEvent &pointerEvent) noexcept;

struct ActiveTouch {
facebook::react::Touch touch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ void HostPlatformViewEventEmitter::onBlur() const {

#pragma mark - Mouse Events

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

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

#pragma mark - Touch Events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <react/renderer/components/view/BaseViewEventEmitter.h>
#include "KeyEvent.h"
#include "MouseEvent.h"

namespace facebook::react {

Expand Down Expand Up @@ -32,8 +33,8 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {

#pragma mark - Mouse Events

void onMouseEnter(PointerEvent const &pointerEvent) const;
void onMouseLeave(PointerEvent const &pointerEvent) const;
void onMouseEnter(MouseEvent const &pointerEvent) const;
void onMouseLeave(MouseEvent const &pointerEvent) const;

#pragma mark - Touch Events

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include <react/renderer/components/view/PointerEvent.h>

namespace facebook::react {

struct MouseEvent : public PointerEvent {
MouseEvent(PointerEvent &event) : PointerEvent(event){};

// We override the type so that it is not recorded as a PointerType,
// otherwise PointerEventsProcessor gets confused by the Windows specific MouseEvents
EventPayloadType getType() const override {
return static_cast<EventPayloadType>(-1);
};
};

} // namespace facebook::react