Emit touch-equivalent W3C pointer events on iOS

Summary: Changelog: [Internal] Emit touch-equivalent W3C pointer events on iOS

Reviewed By: lunaleaps

Differential Revision: D35295104

fbshipit-source-id: 1c1d5a4159bbfed92df151f7e12a4973ec44e970
This commit is contained in:
Vincent Riemer
2022-04-07 14:07:58 -07:00
committed by Facebook GitHub Bot
parent e85f2e2547
commit 179c24e255
8 changed files with 251 additions and 1 deletions
@@ -363,6 +363,32 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
captured: 'onTouchEndCapture',
},
},
// Pointer Events
topPointerCancel: {
phasedRegistrationNames: {
captured: 'onPointerCancelCapture',
bubbled: 'onPointerCancel',
},
},
topPointerDown: {
phasedRegistrationNames: {
captured: 'onPointerDownCapture',
bubbled: 'onPointerDown',
},
},
topPointerMove2: {
phasedRegistrationNames: {
captured: 'onPointerMove2Capture',
bubbled: 'onPointerMove2',
},
},
topPointerUp: {
phasedRegistrationNames: {
captured: 'onPointerUpCapture',
bubbled: 'onPointerUp',
},
},
},
directEventTypes: {
topAccessibilityAction: {
+55
View File
@@ -58,6 +58,11 @@ struct ActiveTouch {
Touch touch;
SharedTouchEventEmitter eventEmitter;
/*
* The type of touch received.
*/
UITouchType touchType;
/*
* A component view on which the touch was begun.
*/
@@ -98,6 +103,8 @@ static void UpdateActiveTouchWithUITouch(
if (RCTForceTouchAvailable()) {
activeTouch.touch.force = RCTZeroIfNaN(uiTouch.force / uiTouch.maximumPossibleForce);
}
activeTouch.touchType = uiTouch.type;
}
static ActiveTouch CreateTouchWithUITouch(UITouch *uiTouch, UIView *rootComponentView, CGPoint rootViewOriginOffset)
@@ -121,6 +128,35 @@ static ActiveTouch CreateTouchWithUITouch(UITouch *uiTouch, UIView *rootComponen
return activeTouch;
}
static const char *PointerTypeCStringFromUITouchType(UITouchType type)
{
switch (type) {
case UITouchTypeDirect:
return "touch";
case UITouchTypePencil:
return "pen";
case UITouchTypeIndirectPointer:
return "mouse";
case UITouchTypeIndirect:
default:
return "";
}
}
static PointerEvent CreatePointerEventFromActiveTouch(ActiveTouch activeTouch)
{
Touch touch = activeTouch.touch;
PointerEvent event = {};
event.pointerId = touch.identifier;
event.pressure = touch.force;
event.pointerType = PointerTypeCStringFromUITouchType(activeTouch.touchType);
event.clientPoint = touch.pagePoint;
event.target = touch.target;
event.timestamp = touch.timestamp;
return event;
}
static BOOL AllTouchesAreCancelledOrEnded(NSSet<UITouch *> *touches)
{
for (UITouch *touch in touches) {
@@ -273,6 +309,25 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
changedActiveTouches.insert(activeTouch);
event.changedTouches.insert(activeTouch.touch);
uniqueEventEmitters.insert(activeTouch.eventEmitter);
// emit w3c pointer events
if (RCTGetDispatchW3CPointerEvents()) {
PointerEvent pointerEvent = CreatePointerEventFromActiveTouch(activeTouch);
switch (eventType) {
case RCTTouchEventTypeTouchStart:
activeTouch.eventEmitter->onPointerDown(pointerEvent);
break;
case RCTTouchEventTypeTouchMove:
activeTouch.eventEmitter->onPointerMove2(pointerEvent);
break;
case RCTTouchEventTypeTouchEnd:
activeTouch.eventEmitter->onPointerUp(pointerEvent);
break;
case RCTTouchEventTypeTouchCancel:
activeTouch.eventEmitter->onPointerCancel(pointerEvent);
break;
}
}
}
for (const auto &pair : _activeTouches) {
+6
View File
@@ -445,4 +445,10 @@ RCT_CUSTOM_VIEW_PROPERTY(onTouchMove, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onTouchEnd, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onTouchCancel, BOOL, RCTView) {}
// Pointer Events
RCT_EXPORT_VIEW_PROPERTY(onPointerCancel, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerDown, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerMove2, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerUp, RCTBubblingEventBlock)
@end
@@ -0,0 +1,35 @@
/*
* 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 "PointerEvent.h"
namespace facebook {
namespace react {
#if RN_DEBUG_STRING_CONVERTIBLE
std::string getDebugName(PointerEvent const &pointerEvent) {
return "PointerEvent";
}
std::vector<DebugStringConvertibleObject> getDebugProps(
PointerEvent const &pointerEvent,
DebugStringConvertibleOptions options) {
return {
{"pointerId", getDebugDescription(pointerEvent.pointerId, options)},
{"pressure", getDebugDescription(pointerEvent.pressure, options)},
{"pointerType", getDebugDescription(pointerEvent.pointerType, options)},
{"clientPoint", getDebugDescription(pointerEvent.clientPoint, options)},
{"target", getDebugDescription(pointerEvent.target, options)},
{"timestamp", getDebugDescription(pointerEvent.timestamp, options)},
};
}
#endif
} // namespace react
} // namespace facebook
@@ -0,0 +1,59 @@
/*
* 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 <react/renderer/core/ReactPrimitives.h>
#include <react/renderer/debug/DebugStringConvertible.h>
#include <react/renderer/graphics/Geometry.h>
namespace facebook {
namespace react {
struct PointerEvent {
/*
* A unique identifier for the pointer causing the event.
*/
int pointerId;
/*
* The normalized pressure of the pointer input in the range 0 to 1, where 0
* and 1 represent the minimum and maximum pressure the hardware is capable of
* detecting, respectively.
*/
Float pressure;
/*
* Indicates the device type that caused the event (mouse, pen, touch, etc.)
*/
std::string pointerType;
/*
* Point within the application's viewport at which the event occurred (as
* opposed to the coordinate within the page).
*/
Point clientPoint;
/*
* A reference to the view to which the event was originally dispatched.
*/
Tag target;
/*
* The time at which the event was created (in milliseconds). By
* specification, this value is time since epoch—but in reality, browsers'
* definitions vary.
*/
Float timestamp;
};
#if RN_DEBUG_STRING_CONVERTIBLE
std::string getDebugName(PointerEvent const &pointerEvent);
std::vector<DebugStringConvertibleObject> getDebugProps(
PointerEvent const &pointerEvent,
DebugStringConvertibleOptions options);
#endif
} // namespace react
} // namespace facebook
@@ -59,6 +59,20 @@ static jsi::Value touchEventPayload(
return object;
}
static jsi::Value pointerEventPayload(
jsi::Runtime &runtime,
PointerEvent const &event) {
auto object = jsi::Object(runtime);
object.setProperty(runtime, "pointerId", event.pointerId);
object.setProperty(runtime, "pressure", event.pressure);
object.setProperty(runtime, "pointerType", event.pointerType);
object.setProperty(runtime, "clientX", event.clientPoint.x);
object.setProperty(runtime, "clientY", event.clientPoint.y);
object.setProperty(runtime, "target", event.target);
object.setProperty(runtime, "timestamp", event.timestamp * 1000);
return object;
}
void TouchEventEmitter::dispatchTouchEvent(
std::string type,
TouchEvent const &event,
@@ -73,6 +87,20 @@ void TouchEventEmitter::dispatchTouchEvent(
category);
}
void TouchEventEmitter::dispatchPointerEvent(
std::string type,
PointerEvent const &event,
EventPriority priority,
RawEvent::Category category) const {
dispatchEvent(
std::move(type),
[event](jsi::Runtime &runtime) {
return pointerEventPayload(runtime, event);
},
priority,
category);
}
void TouchEventEmitter::onTouchStart(TouchEvent const &event) const {
dispatchTouchEvent(
"touchStart",
@@ -103,5 +131,35 @@ void TouchEventEmitter::onTouchCancel(TouchEvent const &event) const {
RawEvent::Category::ContinuousEnd);
}
void TouchEventEmitter::onPointerCancel(const PointerEvent &event) const {
dispatchPointerEvent(
"pointerCancel",
event,
EventPriority::AsynchronousBatched,
RawEvent::Category::ContinuousEnd);
}
void TouchEventEmitter::onPointerDown(const PointerEvent &event) const {
dispatchPointerEvent(
"pointerDown",
event,
EventPriority::AsynchronousBatched,
RawEvent::Category::ContinuousStart);
}
void TouchEventEmitter::onPointerMove2(const PointerEvent &event) const {
dispatchUniqueEvent("pointerMove2", [event](jsi::Runtime &runtime) {
return pointerEventPayload(runtime, event);
});
}
void TouchEventEmitter::onPointerUp(const PointerEvent &event) const {
dispatchPointerEvent(
"pointerUp",
event,
EventPriority::AsynchronousBatched,
RawEvent::Category::ContinuousEnd);
}
} // namespace react
} // namespace facebook
@@ -7,6 +7,7 @@
#pragma once
#include <react/renderer/components/view/PointerEvent.h>
#include <react/renderer/components/view/TouchEvent.h>
#include <react/renderer/core/EventEmitter.h>
#include <react/renderer/core/LayoutMetrics.h>
@@ -29,12 +30,22 @@ class TouchEventEmitter : public EventEmitter {
void onTouchEnd(TouchEvent const &event) const;
void onTouchCancel(TouchEvent const &event) const;
void onPointerCancel(PointerEvent const &event) const;
void onPointerDown(PointerEvent const &event) const;
void onPointerMove2(PointerEvent const &event) const;
void onPointerUp(PointerEvent const &event) const;
private:
void dispatchTouchEvent(
std::string type,
TouchEvent const &event,
EventPriority priority,
RawEvent::Category category) const;
void dispatchPointerEvent(
std::string type,
PointerEvent const &event,
EventPriority priority,
RawEvent::Category category) const;
};
} // namespace react
+1 -1
View File
@@ -942,6 +942,6 @@ SPEC CHECKSUMS:
Yoga: cdc7a2377fc03a2d44322b7784a2461b4c17da09
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 0655d9d79b9837da383b58df5ece7f5c0c4f45b0
PODFILE CHECKSUM: 22e00978a86d653e611654e52ad29190769af232
COCOAPODS: 1.11.2