Skip to content

Commit

Permalink
Add basics of pointer event interception
Browse files Browse the repository at this point in the history
Summary:
Changelog: [Internal] - Add basics of pointer event interception

This diff adds the scaffolding for intercepting Pointer Events (without actually doing anything with them yet). Most notably introduces the logic of determining which events are of the PointerEvent type and downcasting to the type so the interceptor can properly work with the typed properties.

Originally my plan was to leverage `dynamic_cast` but sandcastle's signals let me know that there exists internal apps (particularly VR ones) which don't have RTTI enabled — so to avoid that usage I introduced an additional abstract method on EventPayload which lets subclasses identify themselves cheaply and then subsequently leverage `static_cast`.

Differential Revision: https://internalfb.com/D47443773

fbshipit-source-id: deb804a229db7c57f0d36cd6b34fbf39bd550859
  • Loading branch information
vincentriemer authored and facebook-github-bot committed Jul 18, 2023
1 parent 0cdb9e6 commit c9a714d
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ jsi::Value PointerEvent::asJSIValue(jsi::Runtime &runtime) const {
return object;
}

EventPayloadType PointerEvent::getType() const {
return EventPayloadType::PointerEvent;
}

#if RN_DEBUG_STRING_CONVERTIBLE

std::string getDebugName(PointerEvent const & /*pointerEvent*/) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ struct PointerEvent : public EventPayload {
* EventPayload implementations
*/
jsi::Value asJSIValue(jsi::Runtime &runtime) const override;
EventPayloadType getType() const override;
};

#if RN_DEBUG_STRING_CONVERTIBLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <jsi/jsi.h>

#include <react/renderer/core/EventPayloadType.h>

namespace facebook::react {

/**
Expand All @@ -24,6 +26,13 @@ struct EventPayload {
EventPayload &operator=(EventPayload &&) = default;

virtual jsi::Value asJSIValue(jsi::Runtime &runtime) const = 0;

/**
* Reports the type of the event payload for efficient downcasting.
* When adding a new EventPayload be sure to add a new type of it
* in `EventPayloadType` and return it from its overriden `getType()` method.
*/
virtual EventPayloadType getType() const = 0;
};

using SharedEventPayload = std::shared_ptr<const EventPayload>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

namespace facebook::react {

enum class EventPayloadType { ValueFactory, PointerEvent };

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ jsi::Value ValueFactoryEventPayload::asJSIValue(jsi::Runtime &runtime) const {
return valueFactory_(runtime);
}

EventPayloadType ValueFactoryEventPayload::getType() const {
return EventPayloadType::ValueFactory;
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ValueFactoryEventPayload : public EventPayload {
public:
explicit ValueFactoryEventPayload(ValueFactory factory);
jsi::Value asJSIValue(jsi::Runtime &runtime) const override;
EventPayloadType getType() const override;

private:
ValueFactory valueFactory_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "PointerEventsProcessor.h"

namespace facebook::react {

void PointerEventsProcessor::interceptPointerEvent(
jsi::Runtime &runtime,
EventTarget const *eventTarget,
std::string const &type,
ReactEventPriority priority,
PointerEvent const &event,
DispatchEvent const &eventDispatcher) {
// TODO: implement pointer capture redirection
eventDispatcher(runtime, eventTarget, type, priority, event);
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <functional>

#include <jsi/jsi.h>
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/uimanager/primitives.h>

namespace facebook::react {

using DispatchEvent = std::function<void(
jsi::Runtime &runtime,
const EventTarget *eventTarget,
const std::string &type,
ReactEventPriority priority,
const EventPayload &payload)>;

class PointerEventsProcessor final {
public:
void interceptPointerEvent(
jsi::Runtime &runtime,
EventTarget const *eventTarget,
std::string const &type,
ReactEventPriority priority,
PointerEvent const &event,
DispatchEvent const &eventDispatcher);
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <glog/logging.h>
#include <jsi/JSIDynamic.h>
#include <react/debug/react_native_assert.h>
#include <react/renderer/components/view/PointerEvent.h>
#include <react/renderer/core/LayoutableShadowNode.h>
#include <react/renderer/core/TraitCast.h>
#include <react/renderer/debug/SystraceSection.h>
Expand Down Expand Up @@ -96,6 +97,32 @@ void UIManagerBinding::dispatchEvent(
const EventPayload &eventPayload) const {
SystraceSection s("UIManagerBinding::dispatchEvent", "type", type);

if (eventPayload.getType() == EventPayloadType::PointerEvent) {
auto pointerEvent = static_cast<const PointerEvent &>(eventPayload);
auto dispatchCallback = [this](
jsi::Runtime &runtime,
EventTarget const *eventTarget,
std::string const &type,
ReactEventPriority priority,
const EventPayload &eventPayload) {
this->dispatchEventToJS(
runtime, eventTarget, type, priority, eventPayload);
};
pointerEventsProcessor_.interceptPointerEvent(
runtime, eventTarget, type, priority, pointerEvent, dispatchCallback);
} else {
dispatchEventToJS(runtime, eventTarget, type, priority, eventPayload);
}
}

void UIManagerBinding::dispatchEventToJS(
jsi::Runtime &runtime,
EventTarget const *eventTarget,
std::string const &type,
ReactEventPriority priority,
const EventPayload &eventPayload) const {
SystraceSection s("UIManagerBinding::dispatchEventToJS", "type", type);

auto payload = eventPayload.asJSIValue(runtime);

// If a payload is null, the factory has decided to cancel the event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <react/renderer/core/RawValue.h>
#include <react/renderer/uimanager/PointerEventsProcessor.h>
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/uimanager/primitives.h>

Expand Down Expand Up @@ -71,8 +72,20 @@ class UIManagerBinding : public jsi::HostObject {
UIManager &getUIManager();

private:
/*
* Internal method that sends the event to JS. Should only be called from
* UIManagerBinding::dispatchEvent.
*/
void dispatchEventToJS(
jsi::Runtime &runtime,
EventTarget const *eventTarget,
std::string const &type,
ReactEventPriority priority,
const EventPayload &payload) const;

std::shared_ptr<UIManager> uiManager_;
std::unique_ptr<EventHandler const> eventHandler_;
mutable PointerEventsProcessor pointerEventsProcessor_;
mutable ReactEventPriority currentEventPriority_;
};

Expand Down

0 comments on commit c9a714d

Please sign in to comment.