Files
react-native/ReactCommon/react/renderer/components/view/primitives.h
T
Andrei ShikovandFacebook GitHub Bot 980c52de41 Disable view flattening when the view has event handlers on Android
Summary:
The views with touch event props are currently flattened by Fabric core, as we don't take event listeners into account when calculating whether the view should be flattened. This results in a confusing situation when components with touch event listeners (e.g. `<View onTouchStart={() => {}} /> `) or ones using `PanResponder` are either ignored (iOS) or cause a crash (Android).

This change passes touch event props to C++ layer and uses them to calculate whether the view node should be flattened or not. It also refactors events to be kept as a singular bitset with 32 bit (~`uint32_t`).

Changelog: [Changed][General] Avoid flattening nodes with event props

Reviewed By: sammy-SC

Differential Revision: D34005536

fbshipit-source-id: 96255b389a7bfff4aa208a96fd0c173d9edf1512
2022-02-10 06:07:39 -08:00

224 lines
6.0 KiB
C++

/*
* 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 <butter/optional.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/Geometry.h>
#include <array>
#include <bitset>
#include <cmath>
namespace facebook {
namespace react {
enum class PointerEventsMode { Auto, None, BoxNone, BoxOnly };
struct ViewEvents {
std::bitset<32> bits{};
enum class Offset : std::size_t {
// Pointer events
PointerEnter = 0,
PointerMove = 1,
PointerLeave = 2,
// PanResponder callbacks
MoveShouldSetResponder = 3,
MoveShouldSetResponderCapture = 4,
StartShouldSetResponder = 5,
StartShouldSetResponderCapture = 6,
ResponderGrant = 7,
ResponderReject = 8,
ResponderStart = 9,
ResponderEnd = 10,
ResponderRelease = 11,
ResponderMove = 12,
ResponderTerminate = 13,
ResponderTerminationRequest = 14,
ShouldBlockNativeResponder = 15,
// Touch events
TouchStart = 16,
TouchMove = 17,
TouchEnd = 18,
TouchCancel = 19,
};
constexpr bool operator[](const Offset offset) const {
return bits[static_cast<std::size_t>(offset)];
}
std::bitset<32>::reference operator[](const Offset offset) {
return bits[static_cast<std::size_t>(offset)];
}
};
enum class BackfaceVisibility { Auto, Visible, Hidden };
enum class BorderStyle { Solid, Dotted, Dashed };
template <typename T>
struct CascadedRectangleEdges {
using Counterpart = RectangleEdges<T>;
using OptionalT = butter::optional<T>;
OptionalT left{};
OptionalT top{};
OptionalT right{};
OptionalT bottom{};
OptionalT start{};
OptionalT end{};
OptionalT horizontal{};
OptionalT vertical{};
OptionalT all{};
Counterpart resolve(bool isRTL, T defaults) const {
const auto leadingEdge = isRTL ? end : start;
const auto trailingEdge = isRTL ? start : end;
const auto horizontalOrAllOrDefault =
horizontal.value_or(all.value_or(defaults));
const auto verticalOrAllOrDefault =
vertical.value_or(all.value_or(defaults));
return {
/* .left = */
left.value_or(leadingEdge.value_or(horizontalOrAllOrDefault)),
/* .top = */ top.value_or(verticalOrAllOrDefault),
/* .right = */
right.value_or(trailingEdge.value_or(horizontalOrAllOrDefault)),
/* .bottom = */ bottom.value_or(verticalOrAllOrDefault),
};
}
bool operator==(const CascadedRectangleEdges<T> &rhs) const {
return std::tie(
this->left,
this->top,
this->right,
this->bottom,
this->start,
this->end,
this->horizontal,
this->vertical,
this->all) ==
std::tie(
rhs.left,
rhs.top,
rhs.right,
rhs.bottom,
rhs.start,
rhs.end,
rhs.horizontal,
rhs.vertical,
rhs.all);
}
bool operator!=(const CascadedRectangleEdges<T> &rhs) const {
return !(*this == rhs);
}
};
template <typename T>
struct CascadedRectangleCorners {
using Counterpart = RectangleCorners<T>;
using OptionalT = butter::optional<T>;
OptionalT topLeft{};
OptionalT topRight{};
OptionalT bottomLeft{};
OptionalT bottomRight{};
OptionalT topStart{};
OptionalT topEnd{};
OptionalT bottomStart{};
OptionalT bottomEnd{};
OptionalT all{};
Counterpart resolve(bool isRTL, T defaults) const {
const auto topLeading = isRTL ? topEnd : topStart;
const auto topTrailing = isRTL ? topStart : topEnd;
const auto bottomLeading = isRTL ? bottomEnd : bottomStart;
const auto bottomTrailing = isRTL ? bottomStart : bottomEnd;
return {
/* .topLeft = */ topLeft.value_or(
topLeading.value_or(all.value_or(defaults))),
/* .topRight = */
topRight.value_or(topTrailing.value_or(all.value_or(defaults))),
/* .bottomLeft = */
bottomLeft.value_or(bottomLeading.value_or(all.value_or(defaults))),
/* .bottomRight = */
bottomRight.value_or(bottomTrailing.value_or(all.value_or(defaults))),
};
}
bool operator==(const CascadedRectangleCorners<T> &rhs) const {
return std::tie(
this->topLeft,
this->topRight,
this->bottomLeft,
this->bottomRight,
this->topStart,
this->topEnd,
this->bottomStart,
this->bottomEnd,
this->all) ==
std::tie(
rhs.topLeft,
rhs.topRight,
rhs.bottomLeft,
rhs.bottomRight,
rhs.topStart,
rhs.topEnd,
rhs.bottomStart,
rhs.bottomEnd,
rhs.all);
}
bool operator!=(const CascadedRectangleCorners<T> &rhs) const {
return !(*this == rhs);
}
};
using BorderWidths = RectangleEdges<Float>;
using BorderStyles = RectangleEdges<BorderStyle>;
using BorderColors = RectangleEdges<SharedColor>;
using BorderRadii = RectangleCorners<Float>;
using CascadedBorderWidths = CascadedRectangleEdges<Float>;
using CascadedBorderStyles = CascadedRectangleEdges<BorderStyle>;
using CascadedBorderColors = CascadedRectangleEdges<SharedColor>;
using CascadedBorderRadii = CascadedRectangleCorners<Float>;
struct BorderMetrics {
BorderColors borderColors{};
BorderWidths borderWidths{};
BorderRadii borderRadii{};
BorderStyles borderStyles{};
bool operator==(const BorderMetrics &rhs) const {
return std::tie(
this->borderColors,
this->borderWidths,
this->borderRadii,
this->borderStyles) ==
std::tie(
rhs.borderColors,
rhs.borderWidths,
rhs.borderRadii,
rhs.borderStyles);
}
bool operator!=(const BorderMetrics &rhs) const {
return !(*this == rhs);
}
};
} // namespace react
} // namespace facebook