Skip to content

Commit

Permalink
Add basic active pointer tracking to the pointer event processor (#38865
Browse files Browse the repository at this point in the history
)

Summary:
Pull Request resolved: #38865

Changelog: [Internal] - Add basic active pointer tracking to the pointer event processor

This diff lays the groundwork for keeping track of all the "active" pointers. The ActivePointer struct right now only contains a copy of the PointerEvent data but in future diffs this will be extended to include additional stateful metadata.

It's important to note that the code as written in this diff only "tracks" pointers when they are down and won't track pointers that are only hovering. I do want to include this in future diffs but I couldn't include that handling here as it requires changes to the iOS native pointer handling to stop deriving its own hover events which ends up snowballing even more changes. I will be making this change in (hopefully) the next diff where I refactor the hover event derivation to the fabric C++ layer.

Reviewed By: rozele

Differential Revision: D48167438

fbshipit-source-id: d1c7146e7b8d46b2f8defd4785618ee1cd54f155
  • Loading branch information
vincentriemer authored and facebook-github-bot committed Aug 22, 2023
1 parent 6043960 commit 269594b
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@ void PointerEventsProcessor::interceptPointerEvent(
eventTarget = retargeted.target.get();
}

if (type == "topPointerDown") {
registerActivePointer(pointerEvent);
} else if (type == "topPointerMove") {
// TODO: Remove the need for this check by properly handling
// pointerenter/pointerleave events emitted from the native platform
if (getActivePointer(pointerEvent.pointerId) != nullptr) {
updateActivePointer(pointerEvent);
}
}

eventTarget->retain(runtime);
auto shadowNode = getShadowNodeFromEventTarget(runtime, *eventTarget);
if (shadowNode != nullptr &&
Expand All @@ -237,30 +247,45 @@ void PointerEventsProcessor::interceptPointerEvent(
processPendingPointerCapture(
pointerEvent, runtime, eventDispatcher, uiManager);
}

if (type == "topPointerUp" || type == "topPointerCancel") {
unregisterActivePointer(pointerEvent);
}
}

void PointerEventsProcessor::setPointerCapture(
PointerIdentifier pointerId,
ShadowNode::Shared const &shadowNode) {
// TODO: Throw DOMException with name "NotFoundError" when pointerId does not
// match any of the active pointers
pendingPointerCaptureTargetOverrides_[pointerId] = shadowNode;
if (auto activePointer = getActivePointer(pointerId)) {
// As per the spec this method should silently fail if the pointer in
// question does not have any active buttons
if (activePointer->event.buttons == 0) {
return;
}

pendingPointerCaptureTargetOverrides_[pointerId] = shadowNode;
} else {
// TODO: Throw DOMException with name "NotFoundError" when pointerId does
// not match any of the active pointers
}
}

void PointerEventsProcessor::releasePointerCapture(
PointerIdentifier pointerId,
ShadowNode const *shadowNode) {
// TODO: Throw DOMException with name "NotFoundError" when pointerId does not
// match any of the active pointers

// We only clear the pointer's capture target override if release was called
// on the shadowNode which has the capture override, otherwise the result
// should no-op
auto pendingTarget = getCaptureTargetOverride(
pointerId, pendingPointerCaptureTargetOverrides_);
if (pendingTarget != nullptr &&
pendingTarget->getTag() == shadowNode->getTag()) {
pendingPointerCaptureTargetOverrides_.erase(pointerId);
if (getActivePointer(pointerId) != nullptr) {
// We only clear the pointer's capture target override if release was called
// on the shadowNode which has the capture override, otherwise the result
// should no-op
auto pendingTarget = getCaptureTargetOverride(
pointerId, pendingPointerCaptureTargetOverrides_);
if (pendingTarget != nullptr &&
pendingTarget->getTag() == shadowNode->getTag()) {
pendingPointerCaptureTargetOverrides_.erase(pointerId);
}
} else {
// TODO: Throw DOMException with name "NotFoundError" when pointerId does
// not match any of the active pointers
}
}

Expand All @@ -275,6 +300,38 @@ bool PointerEventsProcessor::hasPointerCapture(
return false;
}

ActivePointer *PointerEventsProcessor::getActivePointer(
PointerIdentifier pointerId) {
auto it = activePointers_.find(pointerId);
return (it == activePointers_.end()) ? nullptr : &it->second;
}

void PointerEventsProcessor::registerActivePointer(PointerEvent const &event) {
ActivePointer activePointer = {};
activePointer.event = event;

activePointers_[event.pointerId] = activePointer;
}

void PointerEventsProcessor::updateActivePointer(PointerEvent const &event) {
if (auto activePointer = getActivePointer(event.pointerId)) {
activePointer->event = event;
} else {
LOG(WARNING)
<< "Inconsistency between local and platform pointer registries: attempting to update an active pointer which has never been registered.";
}
}

void PointerEventsProcessor::unregisterActivePointer(
PointerEvent const &event) {
if (getActivePointer(event.pointerId) != nullptr) {
activePointers_.erase(event.pointerId);
} else {
LOG(WARNING)
<< "Inconsistency between local and platform pointer registries: attempting to unregister an active pointer which has never been registered.";
}
}

void PointerEventsProcessor::processPendingPointerCapture(
PointerEvent const &event,
jsi::Runtime &runtime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@

namespace facebook::react {

// Helper struct to package a PointerEvent and SharedEventTarget together
struct PointerEventTarget {
PointerEvent event;
SharedEventTarget target;
};

// Helper struct to contain an active pointer's event data along with additional
// metadata
struct ActivePointer {
PointerEvent event;
};

using DispatchEvent = std::function<void(
jsi::Runtime &runtime,
const EventTarget *eventTarget,
Expand All @@ -26,11 +38,8 @@ using PointerIdentifier = int32_t;
using CaptureTargetOverrideRegistry =
std::unordered_map<PointerIdentifier, ShadowNode::Weak>;

// Helper struct to package a PointerEvent and SharedEventTarget together
struct PointerEventTarget {
PointerEvent event;
SharedEventTarget target;
};
using ActivePointerRegistry =
std::unordered_map<PointerIdentifier, ActivePointer>;

class PointerEventsProcessor final {
public:
Expand All @@ -54,12 +63,20 @@ class PointerEventsProcessor final {
ShadowNode const *shadowNode);

private:
ActivePointer *getActivePointer(PointerIdentifier pointerId);

void registerActivePointer(PointerEvent const &event);
void updateActivePointer(PointerEvent const &event);
void unregisterActivePointer(PointerEvent const &event);

void processPendingPointerCapture(
PointerEvent const &event,
jsi::Runtime &runtime,
DispatchEvent const &eventDispatcher,
UIManager const &uiManager);

ActivePointerRegistry activePointers_;

CaptureTargetOverrideRegistry pendingPointerCaptureTargetOverrides_;
CaptureTargetOverrideRegistry activePointerCaptureTargetOverrides_;
};
Expand Down

0 comments on commit 269594b

Please sign in to comment.