mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
committed by
Facebook GitHub Bot
parent
e85f2e2547
commit
179c24e255
@@ -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: {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -942,6 +942,6 @@ SPEC CHECKSUMS:
|
||||
Yoga: cdc7a2377fc03a2d44322b7784a2461b4c17da09
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 0655d9d79b9837da383b58df5ece7f5c0c4f45b0
|
||||
PODFILE CHECKSUM: 22e00978a86d653e611654e52ad29190769af232
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
||||
Reference in New Issue
Block a user