first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_image_SRC CONFIGURE_DEPENDS *.cpp)
add_library(rrc_image OBJECT ${rrc_image_SRC})
target_include_directories(rrc_image PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(rrc_image
glog
folly_runtime
glog_init
jsi
react_debug
react_utils
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_imagemanager
react_renderer_mapbuffer
rrc_view
yoga
)
target_compile_reactnative_options(rrc_image PRIVATE)
target_compile_options(rrc_image PRIVATE -Wpedantic)

View File

@@ -0,0 +1,31 @@
/*
* 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 "ImageComponentDescriptor.h"
#include <react/renderer/imagemanager/ImageManager.h>
namespace facebook::react {
extern const char ImageManagerKey[] = "ImageManager";
ImageComponentDescriptor::ImageComponentDescriptor(
const ComponentDescriptorParameters& parameters)
: ConcreteComponentDescriptor(parameters),
imageManager_(
getManagerByName<ImageManager>(contextContainer_, ImageManagerKey)) {
};
void ImageComponentDescriptor::adopt(ShadowNode& shadowNode) const {
ConcreteComponentDescriptor::adopt(shadowNode);
auto& imageShadowNode = static_cast<ImageShadowNode&>(shadowNode);
// `ImageShadowNode` uses `ImageManager` to initiate image loading and
// communicate the loading state and results to mounting layer.
imageShadowNode.setImageManager(imageManager_);
}
} // namespace facebook::react

View File

@@ -0,0 +1,32 @@
/*
* 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/components/image/ImageShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
class ImageManager;
extern const char ImageManagerKey[];
/*
* Descriptor for <Image> component.
*/
class ImageComponentDescriptor final : public ConcreteComponentDescriptor<ImageShadowNode> {
public:
explicit ImageComponentDescriptor(const ComponentDescriptorParameters &parameters);
void adopt(ShadowNode &shadowNode) const override;
private:
const std::shared_ptr<ImageManager> imageManager_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,69 @@
/*
* 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 "ImageEventEmitter.h"
namespace facebook::react {
void ImageEventEmitter::onLoadStart() const {
dispatchEvent("loadStart");
}
void ImageEventEmitter::onLoad(const ImageSource& source) const {
dispatchEvent("load", [source](jsi::Runtime& runtime) {
auto src = jsi::Object(runtime);
src.setProperty(runtime, "uri", source.uri);
src.setProperty(runtime, "width", source.size.width * source.scale);
src.setProperty(runtime, "height", source.size.height * source.scale);
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "source", src);
return payload;
});
}
void ImageEventEmitter::onLoadEnd() const {
dispatchEvent("loadEnd");
}
void ImageEventEmitter::onProgress(
double progress,
int64_t loaded,
int64_t total) const {
dispatchEvent("progress", [progress, loaded, total](jsi::Runtime& runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "progress", progress);
payload.setProperty(runtime, "loaded", (double)loaded);
payload.setProperty(runtime, "total", (double)total);
return payload;
});
}
void ImageEventEmitter::onError(const ImageErrorInfo& error) const {
dispatchEvent("error", [error](jsi::Runtime& runtime) {
auto payload = jsi::Object(runtime);
if (!error.error.empty()) {
payload.setProperty(runtime, "error", error.error);
}
if (error.responseCode != 0) {
payload.setProperty(runtime, "responseCode", error.responseCode);
}
if (!error.httpResponseHeaders.empty()) {
auto headers = jsi::Object(runtime);
for (const auto& x : error.httpResponseHeaders) {
headers.setProperty(runtime, x.first.c_str(), x.second);
}
payload.setProperty(runtime, "httpResponseHeaders", headers);
}
return payload;
});
}
void ImageEventEmitter::onPartialLoad() const {
dispatchEvent("partialLoad");
}
} // namespace facebook::react

View File

@@ -0,0 +1,27 @@
/*
* 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/components/view/ViewEventEmitter.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
class ImageEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
void onLoadStart() const;
void onLoad(const ImageSource &source) const;
void onLoadEnd() const;
void onProgress(double progress, int64_t loaded, int64_t total) const;
void onError(const ImageErrorInfo &error) const;
void onPartialLoad() const;
};
} // namespace facebook::react

View File

@@ -0,0 +1,335 @@
/*
* 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 <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/image/ImageProps.h>
#include <react/renderer/components/image/conversions.h>
#include <react/renderer/core/propsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
namespace facebook::react {
ImageProps::ImageProps(
const PropsParserContext& context,
const ImageProps& sourceProps,
const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
sources(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.sources
: convertRawProp(
context,
rawProps,
"source",
sourceProps.sources,
{})),
defaultSource(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.defaultSource
: convertRawProp(
context,
rawProps,
"defaultSource",
sourceProps.defaultSource,
{})),
loadingIndicatorSource(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.loadingIndicatorSource
: convertRawProp(
context,
rawProps,
"loadingIndicatorSource",
sourceProps.loadingIndicatorSource,
{})),
resizeMode(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.resizeMode
: convertRawProp(
context,
rawProps,
"resizeMode",
sourceProps.resizeMode,
ImageResizeMode::Stretch)),
blurRadius(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.blurRadius
: convertRawProp(
context,
rawProps,
"blurRadius",
sourceProps.blurRadius,
{})),
capInsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.capInsets
: convertRawProp(
context,
rawProps,
"capInsets",
sourceProps.capInsets,
{})),
tintColor(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.tintColor
: convertRawProp(
context,
rawProps,
"tintColor",
sourceProps.tintColor,
{})),
internal_analyticTag(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.internal_analyticTag
: convertRawProp(
context,
rawProps,
"internal_analyticTag",
sourceProps.internal_analyticTag,
{})),
resizeMethod(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.resizeMethod
: convertRawProp(
context,
rawProps,
"resizeMethod",
sourceProps.resizeMethod,
{})),
resizeMultiplier(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.resizeMultiplier
: convertRawProp(
context,
rawProps,
"resizeMultiplier",
sourceProps.resizeMultiplier,
{})),
shouldNotifyLoadEvents(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.shouldNotifyLoadEvents
: convertRawProp(
context,
rawProps,
"shouldNotifyLoadEvents",
sourceProps.shouldNotifyLoadEvents,
{})),
overlayColor(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.overlayColor
: convertRawProp(
context,
rawProps,
"overlayColor",
sourceProps.overlayColor,
{})),
fadeDuration(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.fadeDuration
: convertRawProp(
context,
rawProps,
"fadeDuration",
sourceProps.fadeDuration,
{})),
progressiveRenderingEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.progressiveRenderingEnabled
: convertRawProp(
context,
rawProps,
"progressiveRenderingEnabled",
sourceProps.progressiveRenderingEnabled,
{})) {}
void ImageProps::setProp(
const PropsParserContext& context,
RawPropsPropNameHash hash,
const char* propName,
const RawValue& value) {
// All Props structs setProp methods must always, unconditionally,
// call all super::setProp methods, since multiple structs may
// reuse the same values.
ViewProps::setProp(context, hash, propName, value);
static auto defaults = ImageProps{};
switch (hash) {
RAW_SET_PROP_SWITCH_CASE(sources, "source");
RAW_SET_PROP_SWITCH_CASE(defaultSource, "defaultSource");
RAW_SET_PROP_SWITCH_CASE(loadingIndicatorSource, "loadingIndicatorSource");
RAW_SET_PROP_SWITCH_CASE_BASIC(resizeMode);
RAW_SET_PROP_SWITCH_CASE_BASIC(blurRadius);
RAW_SET_PROP_SWITCH_CASE_BASIC(capInsets);
RAW_SET_PROP_SWITCH_CASE_BASIC(tintColor);
RAW_SET_PROP_SWITCH_CASE_BASIC(internal_analyticTag);
RAW_SET_PROP_SWITCH_CASE_BASIC(resizeMethod);
RAW_SET_PROP_SWITCH_CASE_BASIC(resizeMultiplier);
RAW_SET_PROP_SWITCH_CASE_BASIC(shouldNotifyLoadEvents);
RAW_SET_PROP_SWITCH_CASE_BASIC(overlayColor);
RAW_SET_PROP_SWITCH_CASE_BASIC(fadeDuration);
RAW_SET_PROP_SWITCH_CASE_BASIC(progressiveRenderingEnabled);
}
}
#ifdef RN_SERIALIZABLE_STATE
static folly::dynamic convertEdgeInsets(const EdgeInsets& edgeInsets) {
folly::dynamic edgeInsetsResult = folly::dynamic::object();
edgeInsetsResult["left"] = edgeInsets.left;
edgeInsetsResult["top"] = edgeInsets.top;
edgeInsetsResult["right"] = edgeInsets.right;
edgeInsetsResult["bottom"] = edgeInsets.bottom;
return edgeInsetsResult;
}
ComponentName ImageProps::getDiffPropsImplementationTarget() const {
return "Image";
}
folly::dynamic ImageProps::getDiffProps(const Props* prevProps) const {
static const auto defaultProps = ImageProps();
const ImageProps* oldProps = prevProps == nullptr
? &defaultProps
: static_cast<const ImageProps*>(prevProps);
folly::dynamic result = ViewProps::getDiffProps(oldProps);
if (sources != oldProps->sources) {
auto sourcesArray = folly::dynamic::array();
for (const auto& source : sources) {
sourcesArray.push_back(toDynamic(source));
}
result["source"] = sourcesArray;
}
if (defaultSource != oldProps->defaultSource) {
result["defaultSource"] = toDynamic(defaultSource);
}
if (loadingIndicatorSource != oldProps->loadingIndicatorSource) {
result["loadingIndicatorSource"] = toDynamic(loadingIndicatorSource);
}
if (resizeMode != oldProps->resizeMode) {
switch (resizeMode) {
case ImageResizeMode::Cover:
result["resizeMode"] = "cover";
break;
case ImageResizeMode::Contain:
result["resizeMode"] = "contain";
break;
case ImageResizeMode::Stretch:
result["resizeMode"] = "stretch";
break;
case ImageResizeMode::Center:
result["resizeMode"] = "center";
break;
case ImageResizeMode::Repeat:
result["resizeMode"] = "repeat";
break;
case ImageResizeMode::None:
result["resizeMode"] = "none";
break;
}
}
if (blurRadius != oldProps->blurRadius) {
result["blurRadius"] = blurRadius;
}
if (capInsets != oldProps->capInsets) {
result["capInsets"] = convertEdgeInsets(capInsets);
}
if (tintColor != oldProps->tintColor) {
result["tintColor"] = *tintColor;
}
if (internal_analyticTag != oldProps->internal_analyticTag) {
result["internal_analyticTag"] = internal_analyticTag;
}
if (resizeMethod != oldProps->resizeMethod) {
result["resizeMethod"] = resizeMethod;
}
if (resizeMultiplier != oldProps->resizeMultiplier) {
result["resizeMultiplier"] = resizeMultiplier;
}
if (shouldNotifyLoadEvents != oldProps->shouldNotifyLoadEvents) {
result["shouldNotifyLoadEvents"] = shouldNotifyLoadEvents;
}
if (overlayColor != oldProps->overlayColor) {
result["overlayColor"] = *overlayColor;
}
if (fadeDuration != oldProps->fadeDuration) {
result["fadeDuration"] = fadeDuration;
}
if (progressiveRenderingEnabled != oldProps->progressiveRenderingEnabled) {
result["progressiveRenderingEnabled"] = progressiveRenderingEnabled;
}
return result;
}
#endif
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList ImageProps::getDebugProps() const {
const auto& imageProps = ImageProps();
auto sourcesList = SharedDebugStringConvertibleList{};
if (sources.size() == 1) {
sourcesList = sources[0].getDebugProps("source");
} else if (sources.size() > 1) {
for (const auto& source : sources) {
std::string sourceName = "source-" + react::toString(source.scale) + "x";
auto debugProps = source.getDebugProps(sourceName);
sourcesList.insert(
sourcesList.end(), debugProps.begin(), debugProps.end());
}
}
return ViewProps::getDebugProps() +
defaultSource.getDebugProps("defaultSource") + sourcesList +
SharedDebugStringConvertibleList{
debugStringConvertibleItem(
"blurRadius", blurRadius, imageProps.blurRadius),
debugStringConvertibleItem(
"resizeMode",
toString(resizeMode),
toString(imageProps.resizeMode)),
debugStringConvertibleItem(
"tintColor", toString(tintColor), toString(imageProps.tintColor)),
};
}
inline std::string toString(ImageResizeMode resizeMode) {
switch (resizeMode) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
case ImageResizeMode::None:
return "none";
}
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,53 @@
/*
* 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/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
// TODO (T28334063): Consider for codegen.
class ImageProps final : public ViewProps {
public:
ImageProps() = default;
ImageProps(const PropsParserContext &context, const ImageProps &sourceProps, const RawProps &rawProps);
void
setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, const RawValue &value);
#pragma mark - Props
ImageSources sources{};
ImageSource defaultSource{};
ImageSource loadingIndicatorSource{};
ImageResizeMode resizeMode{ImageResizeMode::Stretch};
Float blurRadius{};
EdgeInsets capInsets{};
SharedColor tintColor{};
std::string internal_analyticTag{};
std::string resizeMethod{};
Float resizeMultiplier{};
bool shouldNotifyLoadEvents{};
SharedColor overlayColor{};
Float fadeDuration{};
bool progressiveRenderingEnabled{};
#ifdef RN_SERIALIZABLE_STATE
ComponentName getDiffPropsImplementationTarget() const override;
folly::dynamic getDiffProps(const Props *prevProps) const override;
#endif
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace facebook::react

View File

@@ -0,0 +1,155 @@
/*
* 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 "ImageState.h"
#include <cstdlib>
#include <limits>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/image/ImageShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
namespace facebook::react {
const char ImageComponentName[] = "Image";
void ImageShadowNode::setImageManager(
const std::shared_ptr<ImageManager>& imageManager) {
ensureUnsealed();
imageManager_ = imageManager;
// TODO: T226624691 Improve image request creation to avoid double requests
// The image manager is set when the component descriptor adopts the shadow
// node. For instances where the shadow node was cloned without dirtying the
// layout, if the image source was changed we have to initiate the image
// request now since there is no guarantee that layout will run for the shadow
// node at a later time.
if (getIsLayoutClean() ||
ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
auto sources = getConcreteProps().sources;
auto layoutMetric = getLayoutMetrics();
if (sources.size() <= 1 ||
(layoutMetric.frame.size.width > 0 &&
layoutMetric.frame.size.height > 0)) {
updateStateIfNeeded();
}
}
}
void ImageShadowNode::updateStateIfNeeded() {
ensureUnsealed();
const auto& savedState = getStateData();
const auto& oldImageSource = savedState.getImageSource();
auto newImageSource = getImageSource();
const auto& oldImageRequestParams = savedState.getImageRequestParams();
const auto& imageProps = getConcreteProps();
const auto& newImageRequestParams = ImageRequestParams(
imageProps.blurRadius
#ifdef ANDROID
,
imageProps.defaultSource,
imageProps.resizeMode,
imageProps.resizeMethod,
// TODO: should we resizeMultiplier * imageSource.scale ?
imageProps.resizeMultiplier,
imageProps.shouldNotifyLoadEvents,
imageProps.overlayColor,
imageProps.tintColor,
imageProps.fadeDuration,
imageProps.progressiveRenderingEnabled,
imageProps.loadingIndicatorSource,
imageProps.internal_analyticTag
#endif
);
if (oldImageSource == newImageSource &&
oldImageRequestParams == newImageRequestParams) {
return;
}
#ifdef ANDROID
// Check if we should skip prefetching based on shouldResize logic
if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
const auto& resizeMethod = imageProps.resizeMethod;
const auto& uri = newImageSource.uri;
bool shouldResize = (resizeMethod == "resize") ||
// Only resize for local content/file URIs
(resizeMethod == "auto" &&
(uri.starts_with("content://") || uri.starts_with("file://")));
// If we would resize but have no dimensions, skip creating the request
if (shouldResize &&
(newImageSource.size.width == 0 || newImageSource.size.height == 0)) {
// Keep the old state - don't create a new image request
return;
}
}
#endif
ImageState state{
newImageSource,
imageManager_->requestImage(
newImageSource, getSurfaceId(), newImageRequestParams, getTag()),
newImageRequestParams};
setStateData(std::move(state));
}
ImageSource ImageShadowNode::getImageSource() const {
auto sources = getConcreteProps().sources;
if (sources.empty()) {
return {
/* .type = */ ImageSource::Type::Invalid,
};
}
auto layoutMetrics = getLayoutMetrics();
auto size = layoutMetrics.getContentFrame().size;
auto scale = layoutMetrics.pointScaleFactor;
if (sources.size() == 1) {
auto source = sources[0];
source.size = size;
source.scale = scale;
return source;
}
auto targetImageArea = size.width * size.height * scale * scale;
auto bestFit = std::numeric_limits<Float>::infinity();
auto bestSource = ImageSource{};
for (const auto& source : sources) {
auto sourceSize = source.size;
auto sourceScale = source.scale == 0 ? scale : source.scale;
auto sourceArea =
sourceSize.width * sourceSize.height * sourceScale * sourceScale;
auto fit = std::abs(1 - (sourceArea / targetImageArea));
if (fit < bestFit) {
bestFit = fit;
bestSource = source;
}
}
bestSource.size = size;
bestSource.scale = scale;
return bestSource;
}
#pragma mark - LayoutableShadowNode
void ImageShadowNode::layout(LayoutContext layoutContext) {
updateStateIfNeeded();
ConcreteViewShadowNode::layout(layoutContext);
}
} // namespace facebook::react

View File

@@ -0,0 +1,63 @@
/*
* 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/components/image/ImageEventEmitter.h>
#include <react/renderer/components/image/ImageProps.h>
#include <react/renderer/components/image/ImageState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/ShadowNodeFamily.h>
#include <react/renderer/imagemanager/ImageManager.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
extern const char ImageComponentName[];
/*
* `ShadowNode` for <Image> component.
*/
class ImageShadowNode final
: public ConcreteViewShadowNode<ImageComponentName, ImageProps, ImageEventEmitter, ImageState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
return traits;
}
/*
* Associates a shared `ImageManager` with the node.
*/
void setImageManager(const std::shared_ptr<ImageManager> &imageManager);
static ImageState initialStateData(
const Props::Shared &props,
const ShadowNodeFamily::Shared & /*family*/,
const ComponentDescriptor &componentDescriptor)
{
auto imageSource = ImageSource{ImageSource::Type::Invalid};
return {imageSource, {imageSource, nullptr}, {}};
}
#pragma mark - LayoutableShadowNode
void layout(LayoutContext layoutContext) override;
private:
ImageSource getImageSource() const;
std::shared_ptr<ImageManager> imageManager_;
void updateStateIfNeeded();
};
} // namespace facebook::react

View File

@@ -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 "ImageState.h"
namespace facebook::react {
ImageSource ImageState::getImageSource() const {
return imageSource_;
}
const ImageRequest& ImageState::getImageRequest() const {
return *imageRequest_;
}
const ImageRequestParams& ImageState::getImageRequestParams() const {
return imageRequestParams_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,67 @@
/*
* 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/imagemanager/ImageRequest.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
#include <react/renderer/imagemanager/primitives.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
#endif
namespace facebook::react {
/*
* State for <Image> component.
*/
class ImageState final {
public:
ImageState(const ImageSource &imageSource, ImageRequest imageRequest, const ImageRequestParams &imageRequestParams)
: imageSource_(imageSource),
imageRequest_(std::make_shared<ImageRequest>(std::move(imageRequest))),
imageRequestParams_(imageRequestParams)
{
}
/*
* Returns stored ImageSource object.
*/
ImageSource getImageSource() const;
/*
* Exposes for reading stored `ImageRequest` object.
* `ImageRequest` object cannot be copied or moved from `ImageLocalData`.
*/
const ImageRequest &getImageRequest() const;
/*
* Returns stored ImageRequestParams object.
*/
const ImageRequestParams &getImageRequestParams() const;
#ifdef ANDROID
ImageState(const ImageState &previousState, folly::dynamic data) : imageRequestParams_{} {};
/*
* Empty implementation for Android because it doesn't use this class.
*/
folly::dynamic getDynamic() const
{
return {};
};
#endif
private:
ImageSource imageSource_;
std::shared_ptr<ImageRequest> imageRequest_;
ImageRequestParams imageRequestParams_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,169 @@
/*
* 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 <unordered_map>
#include <folly/dynamic.h>
#include <glog/logging.h>
#include <react/debug/react_native_expect.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
inline void fromRawValue(const PropsParserContext &context, const RawValue &value, ImageSource &result)
{
if (value.hasType<std::string>()) {
result = {
/* .type = */ ImageSource::Type::Remote,
/* .uri = */ (std::string)value,
};
return;
}
if (value.hasType<std::unordered_map<std::string, RawValue>>()) {
auto items = (std::unordered_map<std::string, RawValue>)value;
result = {};
result.type = ImageSource::Type::Remote;
if (items.find("__packager_asset") != items.end()) {
result.type = ImageSource::Type::Local;
}
if (items.find("width") != items.end() && items.find("height") != items.end() &&
// The following checks have to be removed after codegen is shipped.
// See T45151459.
items.at("width").hasType<Float>() && items.at("height").hasType<Float>()) {
result.size = {(Float)items.at("width"), (Float)items.at("height")};
}
if (items.find("scale") != items.end() &&
// The following checks have to be removed after codegen is shipped.
// See T45151459.
items.at("scale").hasType<Float>()) {
result.scale = (Float)items.at("scale");
} else {
result.scale = items.find("deprecated") != items.end() ? 0.0f : 1.0f;
}
if (items.find("url") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("url").hasType<std::string>()) {
result.uri = (std::string)items.at("url");
}
if (items.find("uri") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("uri").hasType<std::string>()) {
result.uri = (std::string)items.at("uri");
}
if (items.find("bundle") != items.end() &&
// The following should be removed after codegen is shipped.
// See T45151459.
items.at("bundle").hasType<std::string>()) {
result.bundle = (std::string)items.at("bundle");
result.type = ImageSource::Type::Local;
}
if (items.find("headers") != items.end() &&
items.at("headers").hasType<std::unordered_map<std::string, std::string>>()) {
auto headers = (std::unordered_map<std::string, std::string>)items.at("headers");
for (const auto &header : headers) {
result.headers.push_back(header);
}
}
if (items.find("body") != items.end() && items.at("body").hasType<std::string>()) {
result.body = (std::string)items.at("body");
}
if (items.find("method") != items.end() && items.at("method").hasType<std::string>()) {
result.method = (std::string)items.at("method");
}
if (items.find("cache") != items.end() && items.at("cache").hasType<std::string>()) {
auto cache = (std::string)items.at("cache");
if (cache == "reload") {
result.cache = ImageSource::CacheStategy::Reload;
} else if (cache == "force-cache") {
result.cache = ImageSource::CacheStategy::ForceCache;
} else if (cache == "only-if-cached") {
result.cache = ImageSource::CacheStategy::OnlyIfCached;
}
}
return;
}
// The following should be removed after codegen is shipped.
// See T45151459.
result = {};
result.type = ImageSource::Type::Invalid;
}
inline std::string toString(const ImageSource &value)
{
return "{uri: " + value.uri + "}";
}
inline void fromRawValue(const PropsParserContext &context, const RawValue &value, ImageResizeMode &result)
{
react_native_expect(value.hasType<std::string>());
if (!value.hasType<std::string>()) {
LOG(ERROR) << "Unsupported ImageResizeMode type";
// "cover" is default in non-Fabric web and iOS
result = ImageResizeMode::Cover;
return;
}
auto stringValue = (std::string)value;
if (stringValue == "cover") {
result = ImageResizeMode::Cover;
} else if (stringValue == "contain") {
result = ImageResizeMode::Contain;
} else if (stringValue == "stretch") {
result = ImageResizeMode::Stretch;
} else if (stringValue == "center") {
result = ImageResizeMode::Center;
} else if (stringValue == "repeat") {
result = ImageResizeMode::Repeat;
} else if (stringValue == "none") {
result = ImageResizeMode::None;
} else {
LOG(ERROR) << "Unsupported ImageResizeMode value: " << stringValue;
react_native_expect(false);
// "cover" is default in non-Fabric web and iOS
result = ImageResizeMode::Cover;
}
}
inline std::string toString(const ImageResizeMode &value)
{
switch (value) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
case ImageResizeMode::None:
return "none";
}
}
} // namespace facebook::react

View File

@@ -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.
*/
#include <memory>
#include <gtest/gtest.h>
TEST(ImageTest, testSomething) {
// TODO
}

View File

@@ -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 <react/debug/react_native_assert.h>
#include <react/renderer/components/inputaccessory/InputAccessoryShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
/*
* Descriptor for <InputAccessoryView> component.
*/
class InputAccessoryComponentDescriptor final : public ConcreteComponentDescriptor<InputAccessoryShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
void adopt(ShadowNode &shadowNode) const override
{
auto &layoutableShadowNode = static_cast<YogaLayoutableShadowNode &>(shadowNode);
auto &stateData = static_cast<const InputAccessoryShadowNode::ConcreteState &>(*shadowNode.getState()).getData();
layoutableShadowNode.setSize(Size{.width = stateData.viewportSize.width, .height = stateData.viewportSize.height});
layoutableShadowNode.setPositionType(YGPositionTypeAbsolute);
ConcreteComponentDescriptor::adopt(shadowNode);
}
};
} // namespace facebook::react

View File

@@ -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.
*/
#include "InputAccessoryShadowNode.h"
namespace facebook::react {
extern const char InputAccessoryComponentName[] = "InputAccessoryView";
} // namespace facebook::react

View File

@@ -0,0 +1,38 @@
/*
* 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/components/FBReactNativeSpec/EventEmitters.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/components/inputaccessory/InputAccessoryState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
extern const char InputAccessoryComponentName[];
/*
* `ShadowNode` for <InputAccessory> component.
*/
class InputAccessoryShadowNode final : public ConcreteViewShadowNode<
InputAccessoryComponentName,
InputAccessoryProps,
InputAccessoryEventEmitter,
InputAccessoryState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::RootNodeKind);
return traits;
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,26 @@
/*
* 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/graphicsConversions.h>
#include <react/renderer/graphics/Float.h>
namespace facebook::react {
/*
* State for <InputAccessoryView> component.
*/
class InputAccessoryState final {
public:
InputAccessoryState() {};
InputAccessoryState(Size viewportSize_) : viewportSize(viewportSize_) {};
const Size viewportSize{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,26 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_legacyviewmanagerinterop_SRC CONFIGURE_DEPENDS *.cpp)
add_library(rrc_legacyviewmanagerinterop OBJECT ${rrc_legacyviewmanagerinterop_SRC})
target_include_directories(rrc_legacyviewmanagerinterop PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(rrc_legacyviewmanagerinterop
glog
glog_init
folly_runtime
jsi
react_renderer_core
rrc_view
yoga
)
target_compile_reactnative_options(rrc_legacyviewmanagerinterop PRIVATE)
target_compile_options(rrc_legacyviewmanagerinterop PRIVATE -Wpedantic)

View File

@@ -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.
*/
#pragma once
#include <react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
class LegacyViewManagerInteropComponentDescriptor final
: public ConcreteComponentDescriptor<LegacyViewManagerInteropShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
LegacyViewManagerInteropComponentDescriptor(const ComponentDescriptorParameters &parameters);
/*
* Returns `name` and `handle` based on a `flavor`, not on static data from
* `LegacyViewManagerInteropShadowNode`.
*/
ComponentHandle getComponentHandle() const override;
ComponentName getComponentName() const override;
protected:
void adopt(ShadowNode &shadowNode) const override;
private:
const std::shared_ptr<void> _coordinator;
};
} // namespace facebook::react

View File

@@ -0,0 +1,177 @@
/*
* 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 "LegacyViewManagerInteropComponentDescriptor.h"
#include <React/RCTBridge+Private.h>
#include <React/RCTBridge.h>
#include <React/RCTBridgeModuleDecorator.h>
#include <React/RCTBridgeProxy.h>
#include <React/RCTComponentData.h>
#include <React/RCTEventDispatcher.h>
#include <React/RCTModuleData.h>
#import <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/utils/ContextContainer.h>
#include <react/utils/ManagedObjectWrapper.h>
#include "LegacyViewManagerInteropState.h"
#include "RCTLegacyViewManagerInteropCoordinator.h"
namespace facebook::react {
static std::string moduleNameFromComponentNameNoRCTPrefix(const std::string &componentName)
{
// TODO: remove FB specific code (T56174424)
if (componentName == "StickerInputView") {
return "FBStickerInputViewManager";
}
if (componentName == "FDSTooltipView") {
return "FBReactFDSTooltipViewManager";
}
std::string fbPrefix("FB");
if (std::mismatch(fbPrefix.begin(), fbPrefix.end(), componentName.begin()).first == fbPrefix.end()) {
// If `moduleName` has "FB" prefix.
return componentName + "Manager";
}
std::string artPrefix("ART");
if (std::mismatch(artPrefix.begin(), artPrefix.end(), componentName.begin()).first == artPrefix.end()) {
return componentName + "Manager";
}
std::string rnPrefix("RN");
if (std::mismatch(rnPrefix.begin(), rnPrefix.end(), componentName.begin()).first == rnPrefix.end()) {
return componentName + "Manager";
}
return componentName + "Manager";
}
inline NSString *RCTNSStringFromString(const std::string &string)
{
return [NSString stringWithUTF8String:string.c_str()];
}
static Class getViewManagerFromComponentName(const std::string &componentName)
{
auto viewManagerName = moduleNameFromComponentNameNoRCTPrefix(componentName);
// 1. Try to get the manager with the RCT prefix.
auto rctViewManagerName = "RCT" + viewManagerName;
Class viewManagerClass = NSClassFromString(RCTNSStringFromString(rctViewManagerName));
if (viewManagerClass != nullptr) {
return viewManagerClass;
}
// 2. Try to get the manager without the prefix.
viewManagerClass = NSClassFromString(RCTNSStringFromString(viewManagerName));
if (viewManagerClass != nullptr) {
return viewManagerClass;
}
return nil;
}
static Class getViewManagerClass(const std::string &componentName, RCTBridge *bridge, RCTBridgeProxy *bridgeProxy)
{
Class viewManager = getViewManagerFromComponentName(componentName);
if (viewManager != nil) {
return viewManager;
}
if (ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS()) {
NSArray<Class> *modulesClasses = RCTGetModuleClasses();
for (Class moduleClass in modulesClasses) {
if ([RCTBridgeModuleNameForClass(moduleClass) isEqualToString:RCTNSStringFromString(componentName)]) {
return moduleClass;
}
}
} else {
// If all the heuristics fail, let's try to retrieve the view manager from the bridge/bridgeProxy
if (bridge != nil) {
return [[bridge moduleForName:RCTNSStringFromString(componentName)] class];
}
if (bridgeProxy != nil) {
return [[bridgeProxy moduleForName:RCTNSStringFromString(componentName) lazilyLoadIfNecessary:YES] class];
}
}
return nil;
}
static std::shared_ptr<void> constructCoordinator(
const std::shared_ptr<const ContextContainer> &contextContainer,
const ComponentDescriptor::Flavor &flavor)
{
auto optionalBridge = contextContainer->find<std::shared_ptr<void>>("Bridge");
RCTBridge *bridge;
if (optionalBridge) {
bridge = unwrapManagedObjectWeakly(optionalBridge.value());
}
RCTBridgeProxy *bridgeProxy;
auto optionalBridgeProxy = contextContainer->find<std::shared_ptr<void>>("RCTBridgeProxy");
if (optionalBridgeProxy) {
bridgeProxy = unwrapManagedObjectWeakly(optionalBridgeProxy.value());
}
auto componentName = *std::static_pointer_cast<const std::string>(flavor);
Class viewManagerClass = getViewManagerClass(componentName, bridge, bridgeProxy);
assert(viewManagerClass);
auto optionalEventDispatcher = contextContainer->find<std::shared_ptr<void>>("RCTEventDispatcher");
RCTEventDispatcher *eventDispatcher;
if (optionalEventDispatcher) {
eventDispatcher = unwrapManagedObject(optionalEventDispatcher.value());
}
auto optionalModuleDecorator = contextContainer->find<std::shared_ptr<void>>("RCTBridgeModuleDecorator");
RCTBridgeModuleDecorator *bridgeModuleDecorator;
if (optionalModuleDecorator) {
bridgeModuleDecorator = unwrapManagedObject(optionalModuleDecorator.value());
}
RCTComponentData *componentData =
[[RCTComponentData alloc] initWithManagerClass:viewManagerClass
bridge:bridge != nil ? bridge : (RCTBridge *)bridgeProxy
eventDispatcher:eventDispatcher];
return wrapManagedObject([[RCTLegacyViewManagerInteropCoordinator alloc]
initWithComponentData:componentData
bridge:bridge
bridgeProxy:bridgeProxy
bridgelessInteropData:bridgeModuleDecorator]);
}
LegacyViewManagerInteropComponentDescriptor::LegacyViewManagerInteropComponentDescriptor(
const ComponentDescriptorParameters &parameters)
: ConcreteComponentDescriptor(parameters), _coordinator(constructCoordinator(contextContainer_, flavor_))
{
}
ComponentHandle LegacyViewManagerInteropComponentDescriptor::getComponentHandle() const
{
return reinterpret_cast<ComponentHandle>(getComponentName());
}
ComponentName LegacyViewManagerInteropComponentDescriptor::getComponentName() const
{
return static_cast<const std::string *>(flavor_.get())->c_str();
}
void LegacyViewManagerInteropComponentDescriptor::adopt(ShadowNode &shadowNode) const
{
ConcreteComponentDescriptor::adopt(shadowNode);
auto &legacyViewManagerInteropShadowNode = static_cast<LegacyViewManagerInteropShadowNode &>(shadowNode);
auto state = LegacyViewManagerInteropState{};
state.coordinator = _coordinator;
legacyViewManagerInteropShadowNode.setStateData(std::move(state));
}
} // namespace facebook::react

View File

@@ -0,0 +1,13 @@
/*
* 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.
*/
namespace facebook::react {
extern const char LegacyViewManagerInteropComponentName[] =
"LegacyViewManagerInterop";
} // namespace facebook::react

View File

@@ -0,0 +1,24 @@
/*
* 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/components/legacyviewmanagerinterop/LegacyViewManagerInteropState.h>
#include <react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
extern const char LegacyViewManagerInteropComponentName[];
using LegacyViewManagerInteropShadowNode = ConcreteViewShadowNode<
LegacyViewManagerInteropComponentName,
LegacyViewManagerInteropViewProps,
ViewEventEmitter,
LegacyViewManagerInteropState>;
} // namespace facebook::react

View File

@@ -0,0 +1,22 @@
/*
* 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
#import <memory>
namespace facebook::react {
/*
* State for <LegacyViewManagerInterop> component.
*/
class LegacyViewManagerInteropState final {
public:
std::shared_ptr<void> coordinator;
};
} // namespace facebook::react

View File

@@ -0,0 +1,11 @@
/*
* 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 "LegacyViewManagerInteropState.h"
namespace facebook::react {
} // namespace facebook::react

View File

@@ -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 "LegacyViewManagerInteropViewProps.h"
#include <react/renderer/core/DynamicPropsUtilities.h>
namespace facebook::react {
LegacyViewManagerInteropViewProps::LegacyViewManagerInteropViewProps(
const PropsParserContext& context,
const LegacyViewManagerInteropViewProps& sourceProps,
const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
otherProps(mergeDynamicProps(
sourceProps.otherProps,
(folly::dynamic)rawProps,
NullValueStrategy::Override)) {}
} // namespace facebook::react

View File

@@ -0,0 +1,28 @@
/*
* 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 <folly/dynamic.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
#include <unordered_map>
namespace facebook::react {
class LegacyViewManagerInteropViewProps final : public ViewProps {
public:
LegacyViewManagerInteropViewProps() = default;
LegacyViewManagerInteropViewProps(
const PropsParserContext &context,
const LegacyViewManagerInteropViewProps &sourceProps,
const RawProps &rawProps);
#pragma mark - Props
const folly::dynamic otherProps;
};
} // namespace facebook::react

View File

@@ -0,0 +1,44 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModuleDecorator.h>
#import <UIKit/UIKit.h>
#include <folly/dynamic.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTComponentData;
@class RCTBridge;
@class RCTBridgeProxy;
typedef void (^InterceptorBlock)(std::string eventName, folly::dynamic &&event);
@interface RCTLegacyViewManagerInteropCoordinator : NSObject
- (instancetype)initWithComponentData:(RCTComponentData *)componentData
bridge:(nullable RCTBridge *)bridge
bridgeProxy:(nullable RCTBridgeProxy *)bridgeProxy
bridgelessInteropData:(RCTBridgeModuleDecorator *)bridgelessInteropData;
- (UIView *)createPaperViewWithTag:(NSInteger)tag;
- (void)addObserveForTag:(NSInteger)tag usingBlock:(InterceptorBlock)block;
- (void)removeObserveForTag:(NSInteger)tag;
- (void)setProps:(NSDictionary<NSString *, id> *)props forView:(UIView *)view;
- (NSString *)componentViewName;
- (void)handleCommand:(NSString *)commandName
args:(NSArray *)args
reactTag:(NSInteger)tag
paperView:(UIView *)paperView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,222 @@
/*
* 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 "RCTLegacyViewManagerInteropCoordinator.h"
#include <React/RCTBridge+Private.h>
#include <React/RCTBridgeMethod.h>
#include <React/RCTBridgeProxy.h>
#include <React/RCTComponentData.h>
#include <React/RCTEventDispatcherProtocol.h>
#include <React/RCTModuleData.h>
#include <React/RCTModuleMethod.h>
#include <React/RCTUIManager.h>
#include <React/RCTUIManagerUtils.h>
#include <React/RCTUtils.h>
#include <React/RCTViewManager.h>
#include <folly/json.h>
#include <objc/runtime.h>
#include <react/utils/FollyConvert.h>
using namespace facebook::react;
@implementation RCTLegacyViewManagerInteropCoordinator {
RCTComponentData *_componentData;
__weak RCTBridge *_bridge;
__weak RCTBridgeModuleDecorator *_bridgelessInteropData;
__weak RCTBridgeProxy *_bridgeProxy;
/*
Each instance of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched.
This is the container that maps unretained UIView pointer to a block to which the event is dispatched.
*/
NSMutableDictionary<NSNumber *, InterceptorBlock> *_eventInterceptors;
/*
* In bridgeless mode, instead of using the bridge to look up RCTModuleData,
* store that information locally.
*/
NSMutableArray<id<RCTBridgeMethod>> *_moduleMethods;
NSMutableDictionary<NSString *, id<RCTBridgeMethod>> *_moduleMethodsByName;
}
- (instancetype)initWithComponentData:(RCTComponentData *)componentData
bridge:(nullable RCTBridge *)bridge
bridgeProxy:(nullable RCTBridgeProxy *)bridgeProxy
bridgelessInteropData:(RCTBridgeModuleDecorator *)bridgelessInteropData
{
if (self = [super init]) {
_componentData = componentData;
_bridge = bridge;
_bridgelessInteropData = bridgelessInteropData;
_bridgeProxy = bridgeProxy;
if (bridgelessInteropData) {
// During bridge mode, RCTBridgeModules will be decorated with these APIs by the bridge.
RCTAssert(
_bridge == nil,
@"RCTLegacyViewManagerInteropCoordinator should not be initialized with RCTBridgeModuleDecorator in bridge mode.");
}
_eventInterceptors = [NSMutableDictionary new];
__weak __typeof(self) weakSelf = self;
_componentData.eventInterceptor = ^(NSString *eventName, NSDictionary *event, NSNumber *reactTag) {
__typeof(self) strongSelf = weakSelf;
if (strongSelf) {
InterceptorBlock block = [strongSelf->_eventInterceptors objectForKey:reactTag];
if (block) {
block(
std::string([RCTNormalizeInputEventName(eventName) UTF8String]),
convertIdToFollyDynamic(event ? event : @{}));
}
}
};
}
return self;
}
- (void)addObserveForTag:(NSInteger)tag usingBlock:(InterceptorBlock)block
{
[_eventInterceptors setObject:block forKey:[NSNumber numberWithInteger:tag]];
}
- (void)removeObserveForTag:(NSInteger)tag
{
[_eventInterceptors removeObjectForKey:[NSNumber numberWithInteger:tag]];
}
- (UIView *)createPaperViewWithTag:(NSInteger)tag
{
[_bridgelessInteropData attachInteropAPIsToModule:(id<RCTBridgeModule>)_componentData.manager];
return [_componentData createViewWithTag:[NSNumber numberWithInteger:tag] rootTag:NULL];
}
- (void)setProps:(NSDictionary<NSString *, id> *)props forView:(UIView *)view
{
[_componentData setProps:props forView:view];
if ([view respondsToSelector:@selector(didSetProps:)]) {
[view performSelector:@selector(didSetProps:) withObject:[props allKeys]];
}
}
- (NSString *)componentViewName
{
return RCTDropReactPrefixes(_componentData.name);
}
- (void)handleCommand:(NSString *)commandName
args:(NSArray *)args
reactTag:(NSInteger)tag
paperView:(nonnull UIView *)paperView
{
Class managerClass = _componentData.managerClass;
[self _lookupModuleMethodsIfNecessary];
RCTModuleData *moduleData = [_bridge.batchedBridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)];
id<RCTBridgeMethod> method;
// We can't use `[NSString intValue]` as "0" is a valid command,
// but also a falsy value. [NSNumberFormatter numberFromString] returns a
// `NSNumber *` which is NULL when it's to be NULL
// and it points to 0 when the string is @"0" (not a falsy value).
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
if ([commandName isKindOfClass:[NSNumber class]] || [formatter numberFromString:commandName] != NULL) {
method = moduleData ? moduleData.methods[[commandName intValue]] : _moduleMethods[[commandName intValue]];
} else if ([commandName isKindOfClass:[NSString class]]) {
method = moduleData ? moduleData.methodsByName[commandName] : _moduleMethodsByName[commandName];
if (method == nil) {
RCTLogError(@"No command found with name \"%@\"", commandName);
}
} else {
RCTLogError(@"dispatchViewManagerCommand must be called with a string or integer command");
return;
}
NSArray *newArgs = [@[ [NSNumber numberWithInteger:tag] ] arrayByAddingObjectsFromArray:args];
if (_bridge) {
[self _handleCommandsOnBridge:method withArgs:newArgs];
} else {
[self _handleCommandsOnBridgeless:method withArgs:newArgs];
}
}
#pragma mark - Private
- (void)_handleCommandsOnBridge:(id<RCTBridgeMethod>)method withArgs:(NSArray *)newArgs
{
[_bridge.batchedBridge
dispatchBlock:^{
[method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs];
[self->_bridge.uiManager setNeedsLayout];
}
queue:RCTGetUIManagerQueue()];
}
- (void)_handleCommandsOnBridgeless:(id<RCTBridgeMethod>)method withArgs:(NSArray *)newArgs
{
RCTViewManager *componentViewManager = self->_componentData.manager;
[componentViewManager setValue:_bridgeProxy forKey:@"bridge"];
[self->_bridgeProxy.uiManager
addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
[method invokeWithBridge:nil module:componentViewManager arguments:newArgs];
}];
}
- (void)_addUIBlock:(RCTViewManagerUIBlock)block
{
if (_bridge) {
[self _addUIBlockOnBridge:block];
} else {
[self->_bridgeProxy.uiManager addUIBlock:block];
}
}
- (void)_addUIBlockOnBridge:(RCTViewManagerUIBlock)block
{
__weak __typeof__(self) weakSelf = self;
[_bridge.batchedBridge
dispatchBlock:^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf->_bridge.uiManager addUIBlock:block];
}
queue:RCTGetUIManagerQueue()];
}
// This is copy-pasta from RCTModuleData.
- (void)_lookupModuleMethodsIfNecessary
{
if (!_bridge && !_moduleMethods) {
_moduleMethods = [NSMutableArray new];
_moduleMethodsByName = [NSMutableDictionary new];
unsigned int methodCount;
Class cls = _componentData.managerClass;
while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
IMP imp = method_getImplementation(method);
auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_componentData.managerClass, selector);
id<RCTBridgeMethod> moduleMethod =
[[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod moduleClass:_componentData.managerClass];
[_moduleMethodsByName setValue:moduleMethod forKey:[NSString stringWithUTF8String:moduleMethod.JSMethodName]];
[_moduleMethods addObject:moduleMethod];
}
}
free(methods);
cls = class_getSuperclass(cls);
}
}
}
@end

View File

@@ -0,0 +1,26 @@
/*
* 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 "UnstableLegacyViewManagerAutomaticComponentDescriptor.h"
#include <react/renderer/components/legacyviewmanagerinterop/UnstableLegacyViewManagerAutomaticShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/core/ReactPrimitives.h>
#include <string>
namespace facebook::react {
ComponentName
UnstableLegacyViewManagerAutomaticComponentDescriptor::getComponentName()
const {
return legacyComponentName_.c_str();
}
ComponentHandle
UnstableLegacyViewManagerAutomaticComponentDescriptor::getComponentHandle()
const {
return reinterpret_cast<ComponentHandle>(getComponentName());
}
} // namespace facebook::react

View File

@@ -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.
*/
#pragma once
#include <react/renderer/components/legacyviewmanagerinterop/UnstableLegacyViewManagerAutomaticShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/core/ReactPrimitives.h>
#include <string>
namespace facebook::react {
class UnstableLegacyViewManagerAutomaticComponentDescriptor final
: public ConcreteComponentDescriptor<LegacyViewManagerAndroidInteropShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
UnstableLegacyViewManagerAutomaticComponentDescriptor(
const ComponentDescriptorParameters &parameters,
std::string legacyComponentName)
: ConcreteComponentDescriptor(parameters), legacyComponentName_(std::move(legacyComponentName))
{
}
ComponentHandle getComponentHandle() const override;
ComponentName getComponentName() const override;
private:
std::string legacyComponentName_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,13 @@
/*
* 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.
*/
namespace facebook::react {
extern const char LegacyViewManagerAndroidInteropComponentName[] =
"LegacyViewManagerInterop";
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* 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/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
extern const char LegacyViewManagerAndroidInteropComponentName[];
using LegacyViewManagerAndroidInteropShadowNode =
ConcreteViewShadowNode<LegacyViewManagerAndroidInteropComponentName, LegacyViewManagerInteropViewProps>;
} // namespace facebook::react

View File

@@ -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.
*/
#pragma once
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
/*
* Descriptor for <UnstableReactLegacyComponent> component.
*
* This component is part of the Fabric Interop Layer and is subject to future
* changes (hence the "Unstable" prefix).
*/
template <const char *concreteComponentName>
class UnstableLegacyViewManagerInteropComponentDescriptor
: public ConcreteComponentDescriptor<ConcreteViewShadowNode<concreteComponentName, ViewProps>> {
public:
UnstableLegacyViewManagerInteropComponentDescriptor<concreteComponentName>(
const ComponentDescriptorParameters &parameters)
: ConcreteComponentDescriptor<ConcreteViewShadowNode<concreteComponentName, ViewProps>>(parameters)
{
}
private:
};
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/internal/react-native-platform-selector.cmake)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
react_native_android_selector(platform_SRC
platform/android/*.cpp
platform/cxx/*.cpp)
file(GLOB rrc_modal_SRC CONFIGURE_DEPENDS
*.cpp
${platform_SRC})
add_library(rrc_modal STATIC ${rrc_modal_SRC})
target_include_directories(rrc_modal PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(rrc_modal
glog
folly_runtime
glog_init
react_codegen_rncore
react_renderer_componentregistry
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_imagemanager
react_renderer_uimanager
rrc_image
rrc_view
yoga
)
target_compile_reactnative_options(rrc_modal PRIVATE)
target_compile_options(rrc_modal PRIVATE -Wpedantic)

View File

@@ -0,0 +1,48 @@
/*
* 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 "ModalHostViewComponentDescriptor.h"
namespace facebook::react {
#ifdef ANDROID
State::Shared ModalHostViewComponentDescriptor::createInitialState(
const Props::Shared& props,
const ShadowNodeFamily::Shared& family) const {
// For Android, we need to get the size of the screen without the vertical
// insets to correctly position the modal on the first rendering.
// For this reason we provide the `createInitialState` implementation
// that will query FabricUIManager for the size of the screen without
// vertical insets.
int surfaceId = family->getSurfaceId();
const jni::global_ref<jobject>& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto getEncodedScreenSizeWithoutVerticalInsets =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<jlong(jint)>("getEncodedScreenSizeWithoutVerticalInsets");
auto result =
getEncodedScreenSizeWithoutVerticalInsets(fabricUIManager, surfaceId);
// Inspired from yogaMeasureToSize from conversions.h
int32_t wBits = 0xFFFFFFFF & (result >> 32);
int32_t hBits = 0xFFFFFFFF & result;
auto* measuredWidth = reinterpret_cast<float*>(&wBits);
auto* measuredHeight = reinterpret_cast<float*>(&hBits);
return std::make_shared<ModalHostViewShadowNode::ConcreteState>(
std::make_shared<const ModalHostViewState>(ModalHostViewState(
Size{.width = *measuredWidth, .height = *measuredHeight})),
family);
}
#endif // ANDROID
} // namespace facebook::react

View File

@@ -0,0 +1,43 @@
/*
* 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 <glog/logging.h>
#include <react/renderer/components/modal/ModalHostViewShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
/*
* Descriptor for <ModalHostView> component.
*/
class ModalHostViewComponentDescriptor final : public ConcreteComponentDescriptor<ModalHostViewShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
void adopt(ShadowNode &shadowNode) const override
{
auto &layoutableShadowNode = static_cast<YogaLayoutableShadowNode &>(shadowNode);
auto &stateData = static_cast<const ModalHostViewShadowNode::ConcreteState &>(*shadowNode.getState()).getData();
layoutableShadowNode.setSize(Size{.width = stateData.screenSize.width, .height = stateData.screenSize.height});
layoutableShadowNode.setPositionType(YGPositionTypeAbsolute);
ConcreteComponentDescriptor::adopt(shadowNode);
}
#ifdef ANDROID
State::Shared createInitialState(const Props::Shared &props, const ShadowNodeFamily::Shared &family) const override;
#endif // ANDROID
private:
constexpr static auto UIManagerJavaDescriptor = "com/facebook/react/fabric/FabricUIManager";
};
} // namespace facebook::react

View File

@@ -0,0 +1,17 @@
/*
* 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 "ModalHostViewShadowNode.h"
#include <react/renderer/components/modal/ModalHostViewShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
namespace facebook::react {
extern const char ModalHostViewComponentName[] = "ModalHostView";
} // namespace facebook::react

View File

@@ -0,0 +1,41 @@
/*
* 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/components/FBReactNativeSpec/EventEmitters.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/components/modal/ModalHostViewState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
extern const char ModalHostViewComponentName[];
/*
* `ShadowNode` for <ModalHostView> component.
*/
class ModalHostViewShadowNode final : public ConcreteViewShadowNode<
ModalHostViewComponentName,
ModalHostViewProps,
ModalHostViewEventEmitter,
ModalHostViewState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::RootNodeKind);
// <Modal> has a side effect of showing the modal overlay and
// must not be culled. Otherwise, the modal overlay will not be shown.
traits.set(ShadowNodeTraits::Trait::Unstable_uncullableView);
return traits;
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,19 @@
/*
* 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 "ModalHostViewState.h"
namespace facebook::react {
#ifdef RN_SERIALIZABLE_STATE
folly::dynamic ModalHostViewState::getDynamic() const {
return folly::dynamic::object("screenWidth", screenSize.width)(
"screenHeight", screenSize.height);
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,46 @@
/*
* 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/graphicsConversions.h>
#include <react/renderer/graphics/Float.h>
#include "ModalHostViewUtils.h"
#ifdef RN_SERIALIZABLE_STATE
#include <folly/dynamic.h>
#endif
namespace facebook::react {
/*
* State for <ModalHostView> component.
*/
class ModalHostViewState final {
public:
using Shared = std::shared_ptr<const ModalHostViewState>;
ModalHostViewState() : screenSize(ModalHostViewScreenSize()) {}
ModalHostViewState(Size screenSize_) : screenSize(screenSize_) {};
#ifdef RN_SERIALIZABLE_STATE
ModalHostViewState(const ModalHostViewState &previousState, folly::dynamic data)
: screenSize(
Size{.width = (Float)data["screenWidth"].getDouble(), .height = (Float)data["screenHeight"].getDouble()}) {
};
#endif
const Size screenSize{};
#ifdef RN_SERIALIZABLE_STATE
folly::dynamic getDynamic() const;
#endif
#pragma mark - Getters
};
} // namespace facebook::react

View File

@@ -0,0 +1,16 @@
/*
* 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/graphics/Size.h>
namespace facebook::react {
Size ModalHostViewScreenSize(void);
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
#import "ModalHostViewUtils.h"
#import <Foundation/Foundation.h>
#import <React/RCTUtils.h>
namespace facebook::react {
Size ModalHostViewScreenSize(void)
{
CGSize screenSize = RCTScreenSize();
return {.width = screenSize.width, .height = screenSize.height};
}
} // namespace facebook::react

View File

@@ -0,0 +1,17 @@
/*
* 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 <react/renderer/components/modal/ModalHostViewUtils.h>
#include <react/renderer/graphics/Size.h>
namespace facebook::react {
Size ModalHostViewScreenSize() {
return Size{.width = 0, .height = 0};
}
} // namespace facebook::react

View File

@@ -0,0 +1,17 @@
/*
* 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 <react/renderer/components/modal/ModalHostViewUtils.h>
#include <react/renderer/graphics/Size.h>
namespace facebook::react {
Size ModalHostViewScreenSize() {
return Size{0, 0};
}
} // namespace facebook::react

View File

@@ -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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_progressbar_SRC CONFIGURE_DEPENDS android/react/renderer/components/progressbar/*.cpp)
add_library(rrc_progressbar OBJECT ${rrc_progressbar_SRC})
target_include_directories(rrc_progressbar
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/android/
)
target_link_libraries(rrc_progressbar
glog
fbjni
folly_runtime
glog_init
react_codegen_rncore
react_debug
react_renderer_componentregistry
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_uimanager
reactnativejni
rrc_view
yoga
)
target_compile_reactnative_options(rrc_progressbar PRIVATE)
target_compile_options(rrc_progressbar PRIVATE -Wpedantic)

View File

@@ -0,0 +1,42 @@
/*
* 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/ConcreteComponentDescriptor.h>
#include "AndroidProgressBarMeasurementsManager.h"
#include "AndroidProgressBarShadowNode.h"
namespace facebook::react {
/*
* Descriptor for <AndroidProgressBar> component.
*/
class AndroidProgressBarComponentDescriptor final : public ConcreteComponentDescriptor<AndroidProgressBarShadowNode> {
public:
AndroidProgressBarComponentDescriptor(const ComponentDescriptorParameters &parameters)
: ConcreteComponentDescriptor(parameters),
measurementsManager_(std::make_shared<AndroidProgressBarMeasurementsManager>(contextContainer_))
{
}
void adopt(ShadowNode &shadowNode) const override
{
ConcreteComponentDescriptor::adopt(shadowNode);
auto &androidProgressBarShadowNode = static_cast<AndroidProgressBarShadowNode &>(shadowNode);
// `AndroidProgressBarShadowNode` uses
// `AndroidProgressBarMeasurementsManager` to provide measurements to Yoga.
androidProgressBarShadowNode.setAndroidProgressBarMeasurementsManager(measurementsManager_);
}
private:
const std::shared_ptr<AndroidProgressBarMeasurementsManager> measurementsManager_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,63 @@
/*
* 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 "AndroidProgressBarMeasurementsManager.h"
#include <fbjni/fbjni.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/components/progressbar/conversions.h>
#include <react/renderer/core/conversions.h>
using namespace facebook::jni;
namespace facebook::react {
Size AndroidProgressBarMeasurementsManager::measure(
SurfaceId surfaceId,
const AndroidProgressBarProps& props,
LayoutConstraints layoutConstraints) const {
const jni::global_ref<jobject>& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto measure = facebook::jni::findClassStatic(
"com/facebook/react/fabric/FabricUIManager")
->getMethod<jlong(
jint,
jstring,
ReadableMap::javaobject,
ReadableMap::javaobject,
ReadableMap::javaobject,
jfloat,
jfloat,
jfloat,
jfloat)>("measure");
auto minimumSize = layoutConstraints.minimumSize;
auto maximumSize = layoutConstraints.maximumSize;
local_ref<JString> componentName = make_jstring("AndroidProgressBar");
auto serialiazedProps = toDynamic(props);
local_ref<ReadableNativeMap::javaobject> propsRNM =
ReadableNativeMap::newObjectCxxArgs(serialiazedProps);
local_ref<ReadableMap::javaobject> propsRM =
make_local(reinterpret_cast<ReadableMap::javaobject>(propsRNM.get()));
return yogaMeassureToSize(measure(
fabricUIManager,
surfaceId,
componentName.get(),
nullptr,
propsRM.get(),
nullptr,
minimumSize.width,
maximumSize.width,
minimumSize.height,
maximumSize.height));
}
} // namespace facebook::react

View File

@@ -0,0 +1,30 @@
/*
* 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/components/FBReactNativeSpec/Props.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
class AndroidProgressBarMeasurementsManager {
public:
AndroidProgressBarMeasurementsManager(const std::shared_ptr<const ContextContainer> &contextContainer)
: contextContainer_(contextContainer)
{
}
Size measure(SurfaceId surfaceId, const AndroidProgressBarProps &props, LayoutConstraints layoutConstraints) const;
private:
const std::shared_ptr<const ContextContainer> contextContainer_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,34 @@
/*
* 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 "AndroidProgressBarShadowNode.h"
#include <react/renderer/components/progressbar/AndroidProgressBarShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
namespace facebook::react {
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
extern const char AndroidProgressBarComponentName[] = "AndroidProgressBar";
void AndroidProgressBarShadowNode::setAndroidProgressBarMeasurementsManager(
const std::shared_ptr<AndroidProgressBarMeasurementsManager>&
measurementsManager) {
ensureUnsealed();
measurementsManager_ = measurementsManager;
}
#pragma mark - LayoutableShadowNode
Size AndroidProgressBarShadowNode::measureContent(
const LayoutContext& /*layoutContext*/,
const LayoutConstraints& layoutConstraints) const {
return measurementsManager_->measure(
getSurfaceId(), getConcreteProps(), layoutConstraints);
}
} // namespace facebook::react

View File

@@ -0,0 +1,49 @@
/*
* 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/components/FBReactNativeSpec/EventEmitters.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
extern const char AndroidProgressBarComponentName[];
/*
* `ShadowNode` for <AndroidProgressBar> component.
*/
class AndroidProgressBarShadowNode final : public ConcreteViewShadowNode<
AndroidProgressBarComponentName,
AndroidProgressBarProps,
AndroidProgressBarEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
return traits;
}
// Associates a shared `AndroidProgressBarMeasurementsManager` with the node.
void setAndroidProgressBarMeasurementsManager(
const std::shared_ptr<AndroidProgressBarMeasurementsManager> &measurementsManager);
#pragma mark - LayoutableShadowNode
Size measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
private:
std::shared_ptr<AndroidProgressBarMeasurementsManager> measurementsManager_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,31 @@
/*
* 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 <folly/dynamic.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/core/propsConversions.h>
namespace facebook::react {
#ifdef RN_SERIALIZABLE_STATE
inline folly::dynamic toDynamic(const AndroidProgressBarProps &props)
{
folly::dynamic serializedProps = folly::dynamic::object();
serializedProps["styleAttr"] = props.styleAttr;
serializedProps["typeAttr"] = props.typeAttr;
serializedProps["indeterminate"] = props.indeterminate;
serializedProps["progress"] = props.progress;
serializedProps["animating"] = props.animating;
serializedProps["color"] = toAndroidRepr(props.color);
serializedProps["testID"] = props.testID;
return serializedProps;
}
#endif
} // namespace facebook::react

View File

@@ -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.
*/
#include <react/renderer/components/FBReactNativeSpec/States.h>
#warning \
"[DEPRECATION] `react/renderer/components/rncore/States.h` is deprecated and will be \
removed in the future. If this warning appears due to a library, please open an issue \
in that library, and ask for an update. Please, replace the `rncore` imports with \
`FBReactNativeSpec` or remove them entirely.

View File

@@ -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.
*/
#include <react/renderer/components/FBReactNativeSpec/EventEmitters.h>
#warning \
"[DEPRECATION] `react/renderer/components/rncore/EventEmitters.h` is deprecated \
and will be removed in the future. If this warning appears due to a library, \
please open an issue in that library, and ask for an update. Please, replace \
the `rncore` imports with `FBReactNativeSpec` or remove them entirely.

View File

@@ -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.
*/
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#warning \
"[DEPRECATION] `react/renderer/components/rncore/Props.h` is deprecated and will \
be removed in the future. If this warning appears due to a library, please open \
an issue in that library, and ask for an update. Please, replace the `rncore` \
imports with `FBReactNativeSpec` or remove them entirely.

View File

@@ -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.
*/
#include <react/renderer/components/FBReactNativeSpec/ShadowNodes.h>
#warning \
"[DEPRECATION] `react/renderer/components/rncore/ShadowNodes.h` is deprecated and \
will be removed in the future. If this warning appears due to a library, please \
open an issue in that library, and ask for an update. Please, replace the `rncore` \
imports with `FBReactNativeSpec` or remove them entirely.

View File

@@ -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.
*/
#include <react/renderer/components/FBReactNativeSpec/States.h>
#warning \
"[DEPRECATION] `react/renderer/components/rncore/States.h` is deprecated and will be \
removed in the future. If this warning appears due to a library, please open an \
issue in that library, and ask for an update. Please, replace the `rncore` imports \
with `FBReactNativeSpec` or remove them entirely.

View File

@@ -0,0 +1,28 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_root_SRC CONFIGURE_DEPENDS *.cpp)
add_library(rrc_root OBJECT ${rrc_root_SRC})
target_include_directories(rrc_root PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(rrc_root
folly_runtime
glog
glog_init
react_debug
react_renderer_core
react_renderer_debug
react_renderer_graphics
rrc_view
yoga
)
target_compile_reactnative_options(rrc_root PRIVATE)
target_compile_options(rrc_root PRIVATE -Wpedantic)

View File

@@ -0,0 +1,17 @@
/*
* 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/components/root/RootShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
using RootComponentDescriptor = ConcreteComponentDescriptor<RootShadowNode>;
} // namespace facebook::react

View File

@@ -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.
*/
#include "RootProps.h"
#include <react/renderer/components/view/YogaLayoutableShadowNode.h>
#include <react/renderer/components/view/conversions.h>
namespace facebook::react {
// Note that a default/empty context may be passed here from RootShadowNode.
// If that's a problem and the context is necessary here, refactor
// RootShadowNode first.
RootProps::RootProps(
const PropsParserContext& context,
const RootProps& sourceProps,
const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps) {}
// Note that a default/empty context may be passed here from RootShadowNode.
// If that's a problem and the context is necessary here, refactor
// RootShadowNode first.
RootProps::RootProps(
const PropsParserContext& /*context*/,
const RootProps& /*sourceProps*/,
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext)
: ViewProps(),
layoutConstraints(layoutConstraints),
layoutContext(layoutContext) {};
} // namespace facebook::react

View File

@@ -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.
*/
#pragma once
#include <memory>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/PropsParserContext.h>
namespace facebook::react {
class RootProps final : public ViewProps {
public:
RootProps() = default;
RootProps(const PropsParserContext &context, const RootProps &sourceProps, const RawProps &rawProps);
RootProps(
const PropsParserContext &context,
const RootProps &sourceProps,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext);
#pragma mark - Props
LayoutConstraints layoutConstraints{};
LayoutContext layoutContext{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,65 @@
/*
* 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 "RootShadowNode.h"
#include <cxxreact/TraceSection.h>
#include <react/renderer/components/view/conversions.h>
namespace facebook::react {
// NOLINTNEXTLINE(facebook-hte-CArray,modernize-avoid-c-arrays)
const char RootComponentName[] = "RootView";
bool RootShadowNode::layoutIfNeeded(
std::vector<const LayoutableShadowNode*>* affectedNodes) {
TraceSection s("RootShadowNode::layout");
if (getIsLayoutClean()) {
return false;
}
ensureUnsealed();
auto layoutContext = getConcreteProps().layoutContext;
layoutContext.affectedNodes = affectedNodes;
layoutTree(layoutContext, getConcreteProps().layoutConstraints);
return true;
}
Transform RootShadowNode::getTransform() const {
auto viewportOffset = getConcreteProps().layoutContext.viewportOffset;
return Transform::Translate(viewportOffset.x, viewportOffset.y, 0);
}
RootShadowNode::Unshared RootShadowNode::clone(
const PropsParserContext& propsParserContext,
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext) const {
auto props = std::make_shared<const RootProps>(
propsParserContext, getConcreteProps(), layoutConstraints, layoutContext);
auto newRootShadowNode = std::make_shared<RootShadowNode>(
*this,
ShadowNodeFragment{
/* .props = */ props,
});
if (layoutConstraints != getConcreteProps().layoutConstraints) {
newRootShadowNode->dirtyLayout();
}
return newRootShadowNode;
}
void RootShadowNode::setInstanceHandle(
InstanceHandle::Shared instanceHandle) const {
getFamily().setInstanceHandle(instanceHandle);
}
} // namespace facebook::react

View File

@@ -0,0 +1,62 @@
/*
* 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 <memory>
#include <react/renderer/components/root/RootProps.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/PropsParserContext.h>
namespace facebook::react {
class RootShadowNode;
extern const char RootComponentName[];
/*
* `ShadowNode` for the root component.
* Besides all functionality of the `View` component, `RootShadowNode` contains
* props which represent external layout constraints and context of the
* shadow tree.
*/
class RootShadowNode final : public ConcreteViewShadowNode<RootComponentName, RootProps> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
using Shared = std::shared_ptr<const RootShadowNode>;
using Unshared = std::shared_ptr<RootShadowNode>;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::RootNodeKind);
return traits;
}
/*
* Layouts the shadow tree if needed.
* Returns `false` if the three is already laid out.
*/
bool layoutIfNeeded(std::vector<const LayoutableShadowNode *> *affectedNodes = {});
/*
* Clones the node with given `layoutConstraints` and `layoutContext`.
*/
RootShadowNode::Unshared clone(
const PropsParserContext &propsParserContext,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const;
Transform getTransform() const override;
void setInstanceHandle(InstanceHandle::Shared instanceHandle) const;
};
} // namespace facebook::react

View File

@@ -0,0 +1,46 @@
/*
* 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 <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/element/ComponentBuilder.h>
#include <gtest/gtest.h>
#include <react/renderer/element/Element.h>
#include <react/renderer/element/testUtils.h>
namespace facebook::react {
TEST(RootShadowNodeTest, cloneWithLayoutConstraints) {
ContextContainer contextContainer{};
PropsParserContext parserContext{-1, contextContainer};
auto builder = simpleComponentBuilder();
std::shared_ptr<RootShadowNode> rootShadowNode;
LayoutConstraints defaultLayoutConstraints = {};
auto element =
Element<RootShadowNode>().reference(rootShadowNode).tag(1).props([&] {
auto sharedProps = std::make_shared<RootProps>();
sharedProps->layoutConstraints = defaultLayoutConstraints;
return sharedProps;
});
builder.build(element);
EXPECT_FALSE(rootShadowNode->getIsLayoutClean());
EXPECT_TRUE(rootShadowNode->layoutIfNeeded());
EXPECT_TRUE(rootShadowNode->getIsLayoutClean());
auto clonedWithDifferentLayoutConstraints = rootShadowNode->clone(
parserContext, LayoutConstraints{{0, 0}, {10, 10}}, {});
EXPECT_FALSE(clonedWithDifferentLayoutConstraints->getIsLayoutClean());
EXPECT_TRUE(clonedWithDifferentLayoutConstraints->layoutIfNeeded());
}
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_safeareaview_SRCS CONFIGURE_DEPENDS *.cpp)
add_library(
rrc_safeareaview
STATIC
${rrc_safeareaview_SRCS}
)
target_include_directories(rrc_safeareaview PUBLIC .)
target_link_libraries(
rrc_safeareaview
glog
fbjni
folly_runtime
glog_init
react_codegen_rncore
react_debug
react_renderer_componentregistry
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_uimanager
reactnativejni
rrc_view
yoga
)
target_compile_reactnative_options(rrc_safeareaview PRIVATE)
target_compile_options(rrc_safeareaview PRIVATE -Wpedantic)

View File

@@ -0,0 +1,30 @@
/*
* 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/components/safeareaview/SafeAreaViewShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
/*
* Descriptor for <SafeAreaView> component.
*/
class SafeAreaViewComponentDescriptor final : public ConcreteComponentDescriptor<SafeAreaViewShadowNode> {
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
void adopt(ShadowNode &shadowNode) const override
{
auto &layoutableShadowNode = static_cast<YogaLayoutableShadowNode &>(shadowNode);
auto &stateData = static_cast<const SafeAreaViewShadowNode::ConcreteState &>(*shadowNode.getState()).getData();
layoutableShadowNode.setPadding(stateData.padding);
ConcreteComponentDescriptor::adopt(shadowNode);
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,15 @@
/*
* 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 "SafeAreaViewShadowNode.h"
namespace facebook::react {
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
const char SafeAreaViewComponentName[] = "SafeAreaView";
} // namespace facebook::react

View File

@@ -0,0 +1,28 @@
/*
* 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/components/FBReactNativeSpec/EventEmitters.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/components/safeareaview/SafeAreaViewState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
extern const char SafeAreaViewComponentName[];
/*
* `ShadowNode` for <SafeAreaView> component.
*/
class SafeAreaViewShadowNode final
: public ConcreteViewShadowNode<SafeAreaViewComponentName, SafeAreaViewProps, ViewEventEmitter, SafeAreaViewState> {
using ConcreteViewShadowNode::ConcreteViewShadowNode;
};
} // namespace facebook::react

View File

@@ -0,0 +1,19 @@
/*
* 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 "SafeAreaViewState.h"
namespace facebook::react {
#ifdef ANDROID
folly::dynamic SafeAreaViewState::getDynamic() const {
return folly::dynamic::object("left", padding.left)("top", padding.top)(
"right", padding.right)("bottom", padding.bottom);
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,41 @@
/*
* 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/graphics/RectangleEdges.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#endif
namespace facebook::react {
/*
* State for <SafeAreaView> component.
*/
class SafeAreaViewState final {
public:
#ifdef ANDROID
SafeAreaViewState() = default;
SafeAreaViewState(const SafeAreaViewState & /*previousState*/, folly::dynamic data)
: padding(
EdgeInsets{
(Float)data["left"].getDouble(),
(Float)data["top"].getDouble(),
(Float)data["right"].getDouble(),
(Float)data["bottom"].getDouble(),
}) {};
folly::dynamic getDynamic() const;
#endif
EdgeInsets padding{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,566 @@
/*
* 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 "BaseScrollViewProps.h"
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/scrollview/conversions.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
#include <react/renderer/core/propsConversions.h>
namespace facebook::react {
// TODO (T28334063): Consider for codegen.
BaseScrollViewProps::BaseScrollViewProps(
const PropsParserContext& context,
const BaseScrollViewProps& sourceProps,
const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
alwaysBounceHorizontal(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.alwaysBounceHorizontal
: convertRawProp(
context,
rawProps,
"alwaysBounceHorizontal",
sourceProps.alwaysBounceHorizontal,
{})),
alwaysBounceVertical(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.alwaysBounceVertical
: convertRawProp(
context,
rawProps,
"alwaysBounceVertical",
sourceProps.alwaysBounceVertical,
{})),
bounces(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.bounces
: convertRawProp(
context,
rawProps,
"bounces",
sourceProps.bounces,
true)),
bouncesZoom(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.bouncesZoom
: convertRawProp(
context,
rawProps,
"bouncesZoom",
sourceProps.bouncesZoom,
true)),
canCancelContentTouches(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.canCancelContentTouches
: convertRawProp(
context,
rawProps,
"canCancelContentTouches",
sourceProps.canCancelContentTouches,
true)),
centerContent(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.centerContent
: convertRawProp(
context,
rawProps,
"centerContent",
sourceProps.centerContent,
{})),
automaticallyAdjustContentInsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.automaticallyAdjustContentInsets
: convertRawProp(
context,
rawProps,
"automaticallyAdjustContentInsets",
sourceProps.automaticallyAdjustContentInsets,
{})),
automaticallyAdjustsScrollIndicatorInsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.automaticallyAdjustsScrollIndicatorInsets
: convertRawProp(
context,
rawProps,
"automaticallyAdjustsScrollIndicatorInsets",
sourceProps.automaticallyAdjustsScrollIndicatorInsets,
true)),
automaticallyAdjustKeyboardInsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.automaticallyAdjustKeyboardInsets
: convertRawProp(
context,
rawProps,
"automaticallyAdjustKeyboardInsets",
sourceProps.automaticallyAdjustKeyboardInsets,
false)),
decelerationRate(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.decelerationRate
: convertRawProp(
context,
rawProps,
"decelerationRate",
sourceProps.decelerationRate,
(Float)0.998)),
endDraggingSensitivityMultiplier(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.endDraggingSensitivityMultiplier
: convertRawProp(
context,
rawProps,
"endDraggingSensitivityMultiplier",
sourceProps.endDraggingSensitivityMultiplier,
(Float)1)),
directionalLockEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.directionalLockEnabled
: convertRawProp(
context,
rawProps,
"directionalLockEnabled",
sourceProps.directionalLockEnabled,
{})),
indicatorStyle(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.indicatorStyle
: convertRawProp(
context,
rawProps,
"indicatorStyle",
sourceProps.indicatorStyle,
{})),
keyboardDismissMode(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.keyboardDismissMode
: convertRawProp(
context,
rawProps,
"keyboardDismissMode",
sourceProps.keyboardDismissMode,
{})),
maintainVisibleContentPosition(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.maintainVisibleContentPosition
: convertRawProp(
context,
rawProps,
"maintainVisibleContentPosition",
sourceProps.maintainVisibleContentPosition,
{})),
maximumZoomScale(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.maximumZoomScale
: convertRawProp(
context,
rawProps,
"maximumZoomScale",
sourceProps.maximumZoomScale,
(Float)1.0)),
minimumZoomScale(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.minimumZoomScale
: convertRawProp(
context,
rawProps,
"minimumZoomScale",
sourceProps.minimumZoomScale,
(Float)1.0)),
scrollEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.scrollEnabled
: convertRawProp(
context,
rawProps,
"scrollEnabled",
sourceProps.scrollEnabled,
true)),
pagingEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.pagingEnabled
: convertRawProp(
context,
rawProps,
"pagingEnabled",
sourceProps.pagingEnabled,
{})),
pinchGestureEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.pinchGestureEnabled
: convertRawProp(
context,
rawProps,
"pinchGestureEnabled",
sourceProps.pinchGestureEnabled,
true)),
scrollsToTop(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.scrollsToTop
: convertRawProp(
context,
rawProps,
"scrollsToTop",
sourceProps.scrollsToTop,
true)),
showsHorizontalScrollIndicator(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.showsHorizontalScrollIndicator
: convertRawProp(
context,
rawProps,
"showsHorizontalScrollIndicator",
sourceProps.showsHorizontalScrollIndicator,
true)),
showsVerticalScrollIndicator(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.showsVerticalScrollIndicator
: convertRawProp(
context,
rawProps,
"showsVerticalScrollIndicator",
sourceProps.showsVerticalScrollIndicator,
true)),
persistentScrollbar(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.persistentScrollbar
: convertRawProp(
context,
rawProps,
"persistentScrollbar",
sourceProps.persistentScrollbar,
true)),
horizontal(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.horizontal
: convertRawProp(
context,
rawProps,
"horizontal",
sourceProps.horizontal,
true)),
scrollEventThrottle(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.scrollEventThrottle
: convertRawProp(
context,
rawProps,
"scrollEventThrottle",
sourceProps.scrollEventThrottle,
{})),
zoomScale(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.zoomScale
: convertRawProp(
context,
rawProps,
"zoomScale",
sourceProps.zoomScale,
(Float)1.0)),
contentInset(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.contentInset
: convertRawProp(
context,
rawProps,
"contentInset",
sourceProps.contentInset,
{})),
contentOffset(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.contentOffset
: convertRawProp(
context,
rawProps,
"contentOffset",
sourceProps.contentOffset,
{})),
scrollIndicatorInsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.scrollIndicatorInsets
: convertRawProp(
context,
rawProps,
"scrollIndicatorInsets",
sourceProps.scrollIndicatorInsets,
{})),
snapToInterval(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.snapToInterval
: convertRawProp(
context,
rawProps,
"snapToInterval",
sourceProps.snapToInterval,
{})),
snapToAlignment(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.snapToAlignment
: convertRawProp(
context,
rawProps,
"snapToAlignment",
sourceProps.snapToAlignment,
{})),
disableIntervalMomentum(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.disableIntervalMomentum
: convertRawProp(
context,
rawProps,
"disableIntervalMomentum",
sourceProps.disableIntervalMomentum,
{})),
snapToOffsets(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.snapToOffsets
: convertRawProp(
context,
rawProps,
"snapToOffsets",
sourceProps.snapToOffsets,
{})),
snapToStart(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.snapToStart
: convertRawProp(
context,
rawProps,
"snapToStart",
sourceProps.snapToStart,
true)),
snapToEnd(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.snapToEnd
: convertRawProp(
context,
rawProps,
"snapToEnd",
sourceProps.snapToEnd,
true)),
contentInsetAdjustmentBehavior(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.contentInsetAdjustmentBehavior
: convertRawProp(
context,
rawProps,
"contentInsetAdjustmentBehavior",
sourceProps.contentInsetAdjustmentBehavior,
{ContentInsetAdjustmentBehavior::Never})),
scrollToOverflowEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.scrollToOverflowEnabled
: convertRawProp(
context,
rawProps,
"scrollToOverflowEnabled",
sourceProps.scrollToOverflowEnabled,
{})),
isInvertedVirtualizedList(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.isInvertedVirtualizedList
: convertRawProp(
context,
rawProps,
"isInvertedVirtualizedList",
sourceProps.isInvertedVirtualizedList,
{})) {}
void BaseScrollViewProps::setProp(
const PropsParserContext& context,
RawPropsPropNameHash hash,
const char* propName,
const RawValue& value) {
// All Props structs setProp methods must always, unconditionally,
// call all super::setProp methods, since multiple structs may
// reuse the same values.
ViewProps::setProp(context, hash, propName, value);
static auto defaults = BaseScrollViewProps{};
switch (hash) {
RAW_SET_PROP_SWITCH_CASE_BASIC(alwaysBounceHorizontal);
RAW_SET_PROP_SWITCH_CASE_BASIC(alwaysBounceVertical);
RAW_SET_PROP_SWITCH_CASE_BASIC(bounces);
RAW_SET_PROP_SWITCH_CASE_BASIC(bouncesZoom);
RAW_SET_PROP_SWITCH_CASE_BASIC(canCancelContentTouches);
RAW_SET_PROP_SWITCH_CASE_BASIC(centerContent);
RAW_SET_PROP_SWITCH_CASE_BASIC(automaticallyAdjustContentInsets);
RAW_SET_PROP_SWITCH_CASE_BASIC(automaticallyAdjustsScrollIndicatorInsets);
RAW_SET_PROP_SWITCH_CASE_BASIC(decelerationRate);
RAW_SET_PROP_SWITCH_CASE_BASIC(directionalLockEnabled);
RAW_SET_PROP_SWITCH_CASE_BASIC(indicatorStyle);
RAW_SET_PROP_SWITCH_CASE_BASIC(keyboardDismissMode);
RAW_SET_PROP_SWITCH_CASE_BASIC(maintainVisibleContentPosition);
RAW_SET_PROP_SWITCH_CASE_BASIC(maximumZoomScale);
RAW_SET_PROP_SWITCH_CASE_BASIC(minimumZoomScale);
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollEnabled);
RAW_SET_PROP_SWITCH_CASE_BASIC(endDraggingSensitivityMultiplier);
RAW_SET_PROP_SWITCH_CASE_BASIC(pagingEnabled);
RAW_SET_PROP_SWITCH_CASE_BASIC(pinchGestureEnabled);
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollsToTop);
RAW_SET_PROP_SWITCH_CASE_BASIC(showsHorizontalScrollIndicator);
RAW_SET_PROP_SWITCH_CASE_BASIC(showsVerticalScrollIndicator);
RAW_SET_PROP_SWITCH_CASE_BASIC(persistentScrollbar);
RAW_SET_PROP_SWITCH_CASE_BASIC(horizontal);
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollEventThrottle);
RAW_SET_PROP_SWITCH_CASE_BASIC(zoomScale);
RAW_SET_PROP_SWITCH_CASE_BASIC(contentInset);
RAW_SET_PROP_SWITCH_CASE_BASIC(contentOffset);
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollIndicatorInsets);
RAW_SET_PROP_SWITCH_CASE_BASIC(snapToInterval);
RAW_SET_PROP_SWITCH_CASE_BASIC(snapToAlignment);
RAW_SET_PROP_SWITCH_CASE_BASIC(disableIntervalMomentum);
RAW_SET_PROP_SWITCH_CASE_BASIC(snapToOffsets);
RAW_SET_PROP_SWITCH_CASE_BASIC(snapToStart);
RAW_SET_PROP_SWITCH_CASE_BASIC(snapToEnd);
RAW_SET_PROP_SWITCH_CASE_BASIC(contentInsetAdjustmentBehavior);
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollToOverflowEnabled);
RAW_SET_PROP_SWITCH_CASE_BASIC(isInvertedVirtualizedList);
}
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList BaseScrollViewProps::getDebugProps() const {
auto defaultScrollViewProps = BaseScrollViewProps{};
return ViewProps::getDebugProps() +
SharedDebugStringConvertibleList{
debugStringConvertibleItem(
"alwaysBounceHorizontal",
alwaysBounceHorizontal,
defaultScrollViewProps.alwaysBounceHorizontal),
debugStringConvertibleItem(
"alwaysBounceVertical",
alwaysBounceVertical,
defaultScrollViewProps.alwaysBounceVertical),
debugStringConvertibleItem(
"bounces", bounces, defaultScrollViewProps.bounces),
debugStringConvertibleItem(
"bouncesZoom", bouncesZoom, defaultScrollViewProps.bouncesZoom),
debugStringConvertibleItem(
"canCancelContentTouches",
canCancelContentTouches,
defaultScrollViewProps.canCancelContentTouches),
debugStringConvertibleItem(
"centerContent",
centerContent,
defaultScrollViewProps.centerContent),
debugStringConvertibleItem(
"automaticallyAdjustContentInsets",
automaticallyAdjustContentInsets,
defaultScrollViewProps.automaticallyAdjustContentInsets),
debugStringConvertibleItem(
"automaticallyAdjustsScrollIndicatorInsets",
automaticallyAdjustsScrollIndicatorInsets,
defaultScrollViewProps.automaticallyAdjustsScrollIndicatorInsets),
debugStringConvertibleItem(
"decelerationRate",
decelerationRate,
defaultScrollViewProps.decelerationRate),
debugStringConvertibleItem(
"directionalLockEnabled",
directionalLockEnabled,
defaultScrollViewProps.directionalLockEnabled),
debugStringConvertibleItem(
"indicatorStyle",
indicatorStyle,
defaultScrollViewProps.indicatorStyle),
debugStringConvertibleItem(
"keyboardDismissMode",
keyboardDismissMode,
defaultScrollViewProps.keyboardDismissMode),
debugStringConvertibleItem(
"maintainVisibleContentPosition",
maintainVisibleContentPosition,
defaultScrollViewProps.maintainVisibleContentPosition),
debugStringConvertibleItem(
"maximumZoomScale",
maximumZoomScale,
defaultScrollViewProps.maximumZoomScale),
debugStringConvertibleItem(
"minimumZoomScale",
minimumZoomScale,
defaultScrollViewProps.minimumZoomScale),
debugStringConvertibleItem(
"scrollEnabled",
scrollEnabled,
defaultScrollViewProps.scrollEnabled),
debugStringConvertibleItem(
"pagingEnabled",
pagingEnabled,
defaultScrollViewProps.pagingEnabled),
debugStringConvertibleItem(
"pinchGestureEnabled",
pinchGestureEnabled,
defaultScrollViewProps.pinchGestureEnabled),
debugStringConvertibleItem(
"scrollsToTop",
scrollsToTop,
defaultScrollViewProps.scrollsToTop),
debugStringConvertibleItem(
"showsHorizontalScrollIndicator",
showsHorizontalScrollIndicator,
defaultScrollViewProps.showsHorizontalScrollIndicator),
debugStringConvertibleItem(
"showsVerticalScrollIndicator",
showsVerticalScrollIndicator,
defaultScrollViewProps.showsVerticalScrollIndicator),
debugStringConvertibleItem(
"persistentScrollbar",
persistentScrollbar,
defaultScrollViewProps.persistentScrollbar),
debugStringConvertibleItem(
"horizontal", horizontal, defaultScrollViewProps.horizontal),
debugStringConvertibleItem(
"scrollEventThrottle",
scrollEventThrottle,
defaultScrollViewProps.scrollEventThrottle),
debugStringConvertibleItem(
"zoomScale", zoomScale, defaultScrollViewProps.zoomScale),
debugStringConvertibleItem(
"contentInset",
contentInset,
defaultScrollViewProps.contentInset),
debugStringConvertibleItem(
"contentOffset",
contentOffset,
defaultScrollViewProps.contentOffset),
debugStringConvertibleItem(
"scrollIndicatorInsets",
scrollIndicatorInsets,
defaultScrollViewProps.scrollIndicatorInsets),
debugStringConvertibleItem(
"snapToInterval",
snapToInterval,
defaultScrollViewProps.snapToInterval),
debugStringConvertibleItem(
"snapToAlignment",
snapToAlignment,
defaultScrollViewProps.snapToAlignment),
debugStringConvertibleItem(
"disableIntervalMomentum",
disableIntervalMomentum,
defaultScrollViewProps.disableIntervalMomentum),
debugStringConvertibleItem(
"snapToStart", snapToStart, defaultScrollViewProps.snapToStart),
debugStringConvertibleItem(
"snapToEnd", snapToEnd, defaultScrollViewProps.snapToEnd),
debugStringConvertibleItem(
"isInvertedVirtualizedList",
isInvertedVirtualizedList,
defaultScrollViewProps.isInvertedVirtualizedList)};
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,79 @@
/*
* 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/components/scrollview/primitives.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
#include <optional>
namespace facebook::react {
// TODO (T28334063): Consider for codegen.
class BaseScrollViewProps : public ViewProps {
public:
BaseScrollViewProps() = default;
BaseScrollViewProps(
const PropsParserContext &context,
const BaseScrollViewProps &sourceProps,
const RawProps &rawProps);
void
setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, const RawValue &value);
#pragma mark - Props
bool alwaysBounceHorizontal{};
bool alwaysBounceVertical{};
bool bounces{true};
bool bouncesZoom{true};
bool canCancelContentTouches{true};
bool centerContent{};
bool automaticallyAdjustContentInsets{};
bool automaticallyAdjustsScrollIndicatorInsets{true};
bool automaticallyAdjustKeyboardInsets{false};
Float decelerationRate{0.998f};
Float endDraggingSensitivityMultiplier{1};
bool directionalLockEnabled{};
ScrollViewIndicatorStyle indicatorStyle{};
ScrollViewKeyboardDismissMode keyboardDismissMode{};
std::optional<ScrollViewMaintainVisibleContentPosition> maintainVisibleContentPosition{};
Float maximumZoomScale{1.0f};
Float minimumZoomScale{1.0f};
bool scrollEnabled{true};
bool pagingEnabled{};
bool pinchGestureEnabled{true};
bool scrollsToTop{true};
bool showsHorizontalScrollIndicator{true};
bool showsVerticalScrollIndicator{true};
bool persistentScrollbar{false};
bool horizontal{false};
Float scrollEventThrottle{};
Float zoomScale{1.0f};
EdgeInsets contentInset{};
Point contentOffset{};
EdgeInsets scrollIndicatorInsets{};
Float snapToInterval{};
ScrollViewSnapToAlignment snapToAlignment{};
bool disableIntervalMomentum{false};
std::vector<Float> snapToOffsets{};
bool snapToStart{true};
bool snapToEnd{true};
ContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior{ContentInsetAdjustmentBehavior::Never};
bool scrollToOverflowEnabled{false};
bool isInvertedVirtualizedList{false};
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};
} // namespace facebook::react

View File

@@ -0,0 +1,43 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/internal/react-native-platform-selector.cmake)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
react_native_android_selector(platform_SRC
platform/android/react/renderer/components/scrollview/*.cpp
platform/cxx/react/renderer/components/scrollview/*.cpp
)
file(GLOB rrc_scrollview_SRC CONFIGURE_DEPENDS *.cpp ${platform_SRC})
add_library(rrc_scrollview STATIC ${rrc_scrollview_SRC})
react_native_android_selector(platform_DIR
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/
${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/)
target_include_directories(rrc_scrollview PUBLIC ${REACT_COMMON_DIR} . ${platform_DIR})
react_native_android_selector(platform_DIR_PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/react/renderer/components/scrollview/
${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/react/renderer/components/scrollview/)
target_include_directories(rrc_scrollview PRIVATE ${platform_DIR_PRIVATE})
target_link_libraries(rrc_scrollview
glog
folly_runtime
glog_init
jsi
react_debug
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_mapbuffer
rrc_view
yoga
)
target_compile_reactnative_options(rrc_scrollview PRIVATE)
target_compile_options(rrc_scrollview PRIVATE -Wpedantic)

View File

@@ -0,0 +1,133 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
NS_ASSUME_NONNULL_BEGIN
@protocol RCTScrollViewProtocol <NSObject>
- (void)flashScrollIndicators;
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated;
- (void)scrollToEnd:(BOOL)animated;
- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated;
@end
RCT_EXTERN inline void
RCTScrollViewHandleCommand(id<RCTScrollViewProtocol> componentView, const NSString *commandName, const NSArray *args)
{
if ([commandName isEqualToString:@"flashScrollIndicators"]) {
#if RCT_DEBUG
if ([args count] != 0) {
RCTLogError(
@"%@ command %@ received %d arguments, expected %d.", @"ScrollView", commandName, (int)[args count], 1);
return;
}
#endif
[componentView flashScrollIndicators];
return;
}
if ([commandName isEqualToString:@"scrollTo"]) {
#if RCT_DEBUG
if ([args count] != 3) {
RCTLogError(
@"%@ command %@ received %d arguments, expected %d.", @"ScrollView", commandName, (int)[args count], 3);
return;
}
#endif
NSObject *arg0 = args[0];
#if RCT_DEBUG
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"float", @"ScrollView", commandName, @"1st")) {
return;
}
#endif
NSObject *arg1 = args[1];
#if RCT_DEBUG
if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @"float", @"ScrollView", commandName, @"2nd")) {
return;
}
#endif
NSObject *arg2 = args[2];
#if RCT_DEBUG
if (!RCTValidateTypeOfViewCommandArgument(arg2, [NSNumber class], @"boolean", @"ScrollView", commandName, @"3rd")) {
return;
}
#endif
double x = [(NSNumber *)arg0 doubleValue];
double y = [(NSNumber *)arg1 doubleValue];
BOOL animated = [(NSNumber *)arg2 boolValue];
[componentView scrollTo:x y:y animated:animated];
return;
}
if ([commandName isEqualToString:@"scrollToEnd"]) {
#if RCT_DEBUG
if ([args count] != 1) {
RCTLogError(
@"%@ command %@ received %d arguments, expected %d.", @"ScrollView", commandName, (int)[args count], 1);
return;
}
#endif
NSObject *arg0 = args[0];
#if RCT_DEBUG
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"boolean", @"ScrollView", commandName, @"1st")) {
return;
}
#endif
BOOL animated = [(NSNumber *)arg0 boolValue];
[componentView scrollToEnd:animated];
return;
}
if ([commandName isEqualToString:@"zoomToRect"]) {
#if RCT_DEBUG
if ([args count] != 2) {
RCTLogError(
@"%@ command %@ received %d arguments, expected %d.", @"ScrollView", commandName, (int)[args count], 2);
return;
}
#endif
NSObject *arg0 = args[0];
#if RCT_DEBUG
if (!RCTValidateTypeOfViewCommandArgument(
arg0, [NSDictionary class], @"dictionary", @"ScrollView", commandName, @"1st")) {
return;
}
#endif
NSDictionary *rectDict = (NSDictionary *)arg0;
NSNumber *x = rectDict[@"x"];
NSNumber *y = rectDict[@"y"];
NSNumber *width = rectDict[@"width"];
NSNumber *height = rectDict[@"height"];
CGRect rect = CGRectMake(x.doubleValue, y.doubleValue, width.doubleValue, height.doubleValue);
NSObject *arg1 = args[1];
#if RCT_DEBUG
if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @"boolean", @"ScrollView", commandName, @"2nd")) {
return;
}
#endif
BOOL animated = [(NSNumber *)arg1 boolValue];
[componentView zoomToRect:rect animated:animated];
return;
}
}
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,149 @@
/*
* 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 "ScrollEvent.h"
namespace facebook::react {
jsi::Value ScrollEvent::asJSIValue(jsi::Runtime& runtime) const {
auto payload = jsi::Object(runtime);
{
auto contentOffsetObj = jsi::Object(runtime);
contentOffsetObj.setProperty(runtime, "x", contentOffset.x);
contentOffsetObj.setProperty(runtime, "y", contentOffset.y);
payload.setProperty(runtime, "contentOffset", contentOffsetObj);
}
{
auto contentInsetObj = jsi::Object(runtime);
contentInsetObj.setProperty(runtime, "top", contentInset.top);
contentInsetObj.setProperty(runtime, "left", contentInset.left);
contentInsetObj.setProperty(runtime, "bottom", contentInset.bottom);
contentInsetObj.setProperty(runtime, "right", contentInset.right);
payload.setProperty(runtime, "contentInset", contentInsetObj);
}
{
auto contentSizeObj = jsi::Object(runtime);
contentSizeObj.setProperty(runtime, "width", contentSize.width);
contentSizeObj.setProperty(runtime, "height", contentSize.height);
payload.setProperty(runtime, "contentSize", contentSizeObj);
}
{
auto containerSizeObj = jsi::Object(runtime);
containerSizeObj.setProperty(runtime, "width", containerSize.width);
containerSizeObj.setProperty(runtime, "height", containerSize.height);
payload.setProperty(runtime, "layoutMeasurement", containerSizeObj);
}
payload.setProperty(runtime, "zoomScale", zoomScale);
payload.setProperty(runtime, "timestamp", timestamp * 1000);
return payload;
}
folly::dynamic ScrollEvent::asDynamic() const {
auto contentOffsetObj =
folly::dynamic::object("x", contentOffset.x)("y", contentOffset.y);
auto contentInsetObj = folly::dynamic::object("top", contentInset.top)(
"left", contentInset.left)("bottom", contentInset.bottom)(
"right", contentInset.right);
auto contentSizeObj = folly::dynamic::object("width", contentSize.width)(
"height", contentSize.height);
auto containerSizeObj = folly::dynamic::object("width", containerSize.width)(
"height", containerSize.height);
auto metrics =
folly::dynamic::object("contentOffset", std::move(contentOffsetObj))(
"contentInset", std::move(contentInsetObj))(
"contentSize", std::move(contentSizeObj))(
"layoutMeasurement", std::move(containerSizeObj))(
"zoomScale", zoomScale)("timestamp", timestamp * 1000);
return metrics;
};
std::optional<double> ScrollEvent::extractValue(
const std::vector<std::string>& path) const {
if (path.size() == 1 && path[0] == "zoomScale") {
return zoomScale;
} else if (path.size() == 2 && path[0] == "contentOffset") {
if (path[1] == "x") {
return contentOffset.x;
} else if (path[1] == "y") {
return contentOffset.y;
}
}
return EventPayload::extractValue(path);
}
EventPayloadType ScrollEvent::getType() const {
return EventPayloadType::ScrollEvent;
}
jsi::Value ScrollEndDragEvent::asJSIValue(jsi::Runtime& runtime) const {
auto payload = ScrollEvent::asJSIValue(runtime).asObject(runtime);
{
auto targetContentOffsetObj = jsi::Object(runtime);
targetContentOffsetObj.setProperty(runtime, "x", targetContentOffset.x);
targetContentOffsetObj.setProperty(runtime, "y", targetContentOffset.y);
payload.setProperty(runtime, "targetContentOffset", targetContentOffsetObj);
}
{
auto velocityObj = jsi::Object(runtime);
velocityObj.setProperty(runtime, "x", velocity.x);
velocityObj.setProperty(runtime, "y", velocity.y);
payload.setProperty(runtime, "velocity", velocityObj);
}
return payload;
}
folly::dynamic ScrollEndDragEvent::asDynamic() const {
auto metrics = ScrollEvent::asDynamic();
auto targetContentOffsetObj = folly::dynamic::object(
"x", targetContentOffset.x)("y", targetContentOffset.y);
metrics["targetContentOffset"] = std::move(targetContentOffsetObj);
auto velocityObj = folly::dynamic::object("x", velocity.x)("y", velocity.y);
metrics["velocity"] = std::move(velocityObj);
return metrics;
};
#if RN_DEBUG_STRING_CONVERTIBLE
std::string getDebugName(const ScrollEvent& /*scrollEvent*/) {
return "ScrollEvent";
}
std::vector<DebugStringConvertibleObject> getDebugProps(
const ScrollEvent& scrollEvent,
DebugStringConvertibleOptions options) {
return {
{"contentOffset",
getDebugDescription(scrollEvent.contentOffset, options)},
{"contentInset", getDebugDescription(scrollEvent.contentInset, options)},
{"contentSize", getDebugDescription(scrollEvent.contentSize, options)},
{"layoutMeasurement",
getDebugDescription(scrollEvent.containerSize, options)},
{"zoomScale", getDebugDescription(scrollEvent.zoomScale, options)},
{"timestamp", getDebugDescription(scrollEvent.timestamp, options)}};
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,70 @@
/*
* 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 <folly/dynamic.h>
#include <react/renderer/core/EventPayload.h>
#include <react/renderer/debug/DebugStringConvertible.h>
#include <react/renderer/graphics/RectangleEdges.h>
#include <react/renderer/graphics/Size.h>
namespace facebook::react {
struct ScrollEvent : public EventPayload {
Size contentSize;
Point contentOffset;
EdgeInsets contentInset;
Size containerSize;
Float zoomScale{};
/*
* The time in seconds when the touch occurred or when it was last mutated.
*/
Float timestamp{};
ScrollEvent() = default;
folly::dynamic asDynamic() const;
/*
* EventPayload implementations
*/
jsi::Value asJSIValue(jsi::Runtime &runtime) const override;
EventPayloadType getType() const override;
std::optional<double> extractValue(const std::vector<std::string> &path) const override;
};
struct ScrollEndDragEvent : public ScrollEvent {
Point targetContentOffset;
Point velocity;
ScrollEndDragEvent() = default;
ScrollEndDragEvent(const ScrollEvent &scrollEvent) : ScrollEvent(scrollEvent), targetContentOffset({}), velocity({})
{
}
folly::dynamic asDynamic() const;
/*
* EventPayload implementations
*/
jsi::Value asJSIValue(jsi::Runtime &runtime) const override;
};
#if RN_DEBUG_STRING_CONVERTIBLE
std::string getDebugName(const ScrollEvent &scrollEvent);
std::vector<DebugStringConvertibleObject> getDebugProps(
const ScrollEvent &scrollEvent,
DebugStringConvertibleOptions options);
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,17 @@
/*
* 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/components/scrollview/ScrollViewShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
using ScrollViewComponentDescriptor = ConcreteComponentDescriptor<ScrollViewShadowNode>;
} // namespace facebook::react

View File

@@ -0,0 +1,49 @@
/*
* 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 "ScrollViewEventEmitter.h"
namespace facebook::react {
void ScrollViewEventEmitter::onScroll(const ScrollEvent& scrollEvent) const {
dispatchUniqueEvent("scroll", std::make_shared<ScrollEvent>(scrollEvent));
}
void ScrollViewEventEmitter::onScrollToTop(
const ScrollEvent& scrollEvent) const {
dispatchUniqueEvent(
"scrollToTop", std::make_shared<ScrollEvent>(scrollEvent));
}
void ScrollViewEventEmitter::onScrollBeginDrag(
const ScrollEvent& scrollEvent) const {
dispatchScrollViewEvent("scrollBeginDrag", scrollEvent);
}
void ScrollViewEventEmitter::onScrollEndDrag(
const ScrollEndDragEvent& scrollEvent) const {
dispatchEvent(
"scrollEndDrag", std::make_shared<ScrollEndDragEvent>(scrollEvent));
}
void ScrollViewEventEmitter::onMomentumScrollBegin(
const ScrollEvent& scrollEvent) const {
dispatchScrollViewEvent("momentumScrollBegin", scrollEvent);
}
void ScrollViewEventEmitter::onMomentumScrollEnd(
const ScrollEvent& scrollEvent) const {
dispatchScrollViewEvent("momentumScrollEnd", scrollEvent);
}
void ScrollViewEventEmitter::dispatchScrollViewEvent(
std::string name,
const ScrollEvent& scrollEvent) const {
dispatchEvent(std::move(name), std::make_shared<ScrollEvent>(scrollEvent));
}
} // namespace facebook::react

View File

@@ -0,0 +1,37 @@
/*
* 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 <memory>
#include <folly/dynamic.h>
#include <react/renderer/components/scrollview/ScrollEvent.h>
#include <react/renderer/components/view/ViewEventEmitter.h>
#include <react/renderer/core/EventEmitter.h>
namespace facebook::react {
class ScrollViewEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
using Metrics = ScrollEvent;
using EndDragMetrics = ScrollEndDragEvent;
void onScroll(const ScrollEvent &scrollEvent) const;
void onScrollBeginDrag(const ScrollEvent &scrollEvent) const;
void onScrollEndDrag(const ScrollEndDragEvent &scrollEvent) const;
void onMomentumScrollBegin(const ScrollEvent &scrollEvent) const;
void onMomentumScrollEnd(const ScrollEvent &scrollEvent) const;
void onScrollToTop(const ScrollEvent &scrollEvent) const;
private:
void dispatchScrollViewEvent(std::string name, const ScrollEvent &scrollEvent) const;
};
} // namespace facebook::react

View File

@@ -0,0 +1,15 @@
/*
* 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/components/scrollview/HostPlatformScrollViewProps.h>
namespace facebook::react {
using ScrollViewProps = HostPlatformScrollViewProps;
using SharedScrollViewProps = std::shared_ptr<const ScrollViewProps>;
} // namespace facebook::react

View File

@@ -0,0 +1,80 @@
/*
* 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 "ScrollViewShadowNode.h"
#include <react/debug/react_native_assert.h>
#include <react/renderer/core/LayoutMetrics.h>
namespace facebook::react {
// NOLINTNEXTLINE(facebook-hte-CArray)
const char ScrollViewComponentName[] = "ScrollView";
void ScrollViewShadowNode::updateStateIfNeeded() {
ensureUnsealed();
auto contentBoundingRect = Rect{};
for (const auto& childNode : getLayoutableChildNodes()) {
contentBoundingRect.unionInPlace(childNode->getLayoutMetrics().frame);
}
auto state = getStateData();
if (state.contentBoundingRect != contentBoundingRect) {
state.contentBoundingRect = contentBoundingRect;
setStateData(std::move(state));
}
}
void ScrollViewShadowNode::updateScrollContentOffsetIfNeeded() {
#ifndef ANDROID
if (getLayoutMetrics().layoutDirection == LayoutDirection::RightToLeft) {
// Yoga places `contentView` on the right side of `scrollView` when RTL
// layout is enforced. To correct for this, in RTL setting, correct the
// frame's origin. React Native Classic does this as well in
// `RCTScrollContentShadowView.m`.
for (auto layoutableNode : getLayoutableChildNodes()) {
auto layoutMetrics = layoutableNode->getLayoutMetrics();
if (layoutMetrics.frame.origin.x != 0) {
layoutMetrics.frame.origin.x = 0;
layoutableNode->setLayoutMetrics(layoutMetrics);
}
}
}
#endif
}
ScrollViewState ScrollViewShadowNode::initialStateData(
const Props::Shared& props,
const ShadowNodeFamily::Shared& /*family*/,
const ComponentDescriptor& /*componentDescriptor*/) {
return {static_cast<const ScrollViewProps&>(*props).contentOffset, {}, 0};
}
#pragma mark - LayoutableShadowNode
void ScrollViewShadowNode::layout(LayoutContext layoutContext) {
ConcreteViewShadowNode::layout(layoutContext);
updateScrollContentOffsetIfNeeded();
updateStateIfNeeded();
}
Point ScrollViewShadowNode::getContentOriginOffset(
bool includeTransform) const {
auto stateData = getStateData();
auto contentOffset = stateData.contentOffset;
auto transform = includeTransform ? getTransform() : Transform::Identity();
auto result = transform *
Vector{
.x = -contentOffset.x, .y = -contentOffset.y, .z = 0.0f, .w = 1.0f};
return {
.x = result.x,
.y = result.y + static_cast<float>(stateData.scrollAwayPaddingTop)};
}
} // namespace facebook::react

View File

@@ -0,0 +1,44 @@
/*
* 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/components/scrollview/ScrollViewEventEmitter.h>
#include <react/renderer/components/scrollview/ScrollViewProps.h>
#include <react/renderer/components/scrollview/ScrollViewState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/ShadowNodeFamily.h>
namespace facebook::react {
extern const char ScrollViewComponentName[];
/*
* `ShadowNode` for <ScrollView> component.
*/
class ScrollViewShadowNode final
: public ConcreteViewShadowNode<ScrollViewComponentName, ScrollViewProps, ScrollViewEventEmitter, ScrollViewState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ScrollViewState initialStateData(
const Props::Shared &props,
const ShadowNodeFamily::Shared &family,
const ComponentDescriptor &componentDescriptor);
#pragma mark - LayoutableShadowNode
void layout(LayoutContext layoutContext) override;
Point getContentOriginOffset(bool includeTransform) const override;
private:
void updateStateIfNeeded();
void updateScrollContentOffsetIfNeeded();
};
} // namespace facebook::react

View File

@@ -0,0 +1,24 @@
/*
* 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 "ScrollViewState.h"
namespace facebook::react {
ScrollViewState::ScrollViewState(
Point contentOffset,
Rect contentBoundingRect,
int scrollAwayPaddingTop)
: contentOffset(contentOffset),
contentBoundingRect(contentBoundingRect),
scrollAwayPaddingTop(scrollAwayPaddingTop) {}
Size ScrollViewState::getContentSize() const {
return contentBoundingRect.size;
}
} // namespace facebook::react

View File

@@ -0,0 +1,60 @@
/*
* 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/graphics/Float.h>
#include <react/renderer/graphics/Point.h>
#include <react/renderer/graphics/Rect.h>
#include <react/renderer/graphics/Size.h>
#ifdef RN_SERIALIZABLE_STATE
#include <folly/dynamic.h>
#endif
namespace facebook::react {
/*
* State for <ScrollView> component.
*/
class ScrollViewState final {
public:
ScrollViewState(Point contentOffset, Rect contentBoundingRect, int scrollAwayPaddingTop);
ScrollViewState() = default;
Point contentOffset;
Rect contentBoundingRect;
int scrollAwayPaddingTop;
/*
* View Culling has to be disabled when accessibility features are used.
* View Culling removes views from view hierarchy and for example VoiceOver
* wouldn't recognise there is a view outside of the viewport that it can
* scroll to.
*/
bool disableViewCulling{false};
/*
* Returns size of scrollable area.
*/
Size getContentSize() const;
#ifdef RN_SERIALIZABLE_STATE
ScrollViewState(const ScrollViewState &previousState, folly::dynamic data)
: contentOffset({(Float)data["contentOffsetLeft"].getDouble(), (Float)data["contentOffsetTop"].getDouble()}),
contentBoundingRect({}),
scrollAwayPaddingTop((Float)data["scrollAwayPaddingTop"].getDouble()) {};
folly::dynamic getDynamic() const
{
return folly::dynamic::object("contentOffsetLeft", contentOffset.x)("contentOffsetTop", contentOffset.y)(
"scrollAwayPaddingTop", scrollAwayPaddingTop);
};
#endif
};
} // namespace facebook::react

View File

@@ -0,0 +1,174 @@
/*
* 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 <folly/dynamic.h>
#include <react/renderer/components/scrollview/primitives.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/core/propsConversions.h>
#include <unordered_map>
namespace facebook::react {
inline void fromRawValue(const PropsParserContext &context, const RawValue &value, ScrollViewSnapToAlignment &result)
{
auto string = (std::string)value;
if (string == "start") {
result = ScrollViewSnapToAlignment::Start;
return;
}
if (string == "center") {
result = ScrollViewSnapToAlignment::Center;
return;
}
if (string == "end") {
result = ScrollViewSnapToAlignment::End;
return;
}
abort();
}
inline void fromRawValue(const PropsParserContext &context, const RawValue &value, ScrollViewIndicatorStyle &result)
{
auto string = (std::string)value;
if (string == "default") {
result = ScrollViewIndicatorStyle::Default;
return;
}
if (string == "black") {
result = ScrollViewIndicatorStyle::Black;
return;
}
if (string == "white") {
result = ScrollViewIndicatorStyle::White;
return;
}
abort();
}
inline void
fromRawValue(const PropsParserContext &context, const RawValue &value, ScrollViewKeyboardDismissMode &result)
{
auto string = (std::string)value;
if (string == "none") {
result = ScrollViewKeyboardDismissMode::None;
return;
}
if (string == "on-drag") {
result = ScrollViewKeyboardDismissMode::OnDrag;
return;
}
if (string == "interactive") {
result = ScrollViewKeyboardDismissMode::Interactive;
return;
}
abort();
}
inline void
fromRawValue(const PropsParserContext &context, const RawValue &value, ContentInsetAdjustmentBehavior &result)
{
auto string = (std::string)value;
if (string == "never") {
result = ContentInsetAdjustmentBehavior::Never;
return;
}
if (string == "automatic") {
result = ContentInsetAdjustmentBehavior::Automatic;
return;
}
if (string == "scrollableAxes") {
result = ContentInsetAdjustmentBehavior::ScrollableAxes;
return;
}
if (string == "always") {
result = ContentInsetAdjustmentBehavior::Always;
return;
}
abort();
}
inline void
fromRawValue(const PropsParserContext &context, const RawValue &value, ScrollViewMaintainVisibleContentPosition &result)
{
auto map = (std::unordered_map<std::string, RawValue>)value;
auto minIndexForVisible = map.find("minIndexForVisible");
if (minIndexForVisible != map.end()) {
fromRawValue(context, minIndexForVisible->second, result.minIndexForVisible);
}
auto autoscrollToTopThreshold = map.find("autoscrollToTopThreshold");
if (autoscrollToTopThreshold != map.end()) {
fromRawValue(context, autoscrollToTopThreshold->second, result.autoscrollToTopThreshold);
}
}
inline std::string toString(const ScrollViewSnapToAlignment &value)
{
switch (value) {
case ScrollViewSnapToAlignment::Start:
return "start";
case ScrollViewSnapToAlignment::Center:
return "center";
case ScrollViewSnapToAlignment::End:
return "end";
}
}
#if RN_DEBUG_STRING_CONVERTIBLE
inline std::string toString(const ScrollViewIndicatorStyle &value)
{
switch (value) {
case ScrollViewIndicatorStyle::Default:
return "default";
case ScrollViewIndicatorStyle::Black:
return "black";
case ScrollViewIndicatorStyle::White:
return "white";
}
}
inline std::string toString(const ScrollViewKeyboardDismissMode &value)
{
switch (value) {
case ScrollViewKeyboardDismissMode::None:
return "none";
case ScrollViewKeyboardDismissMode::OnDrag:
return "on-drag";
case ScrollViewKeyboardDismissMode::Interactive:
return "interactive";
}
}
inline std::string toString(const ContentInsetAdjustmentBehavior &value)
{
switch (value) {
case ContentInsetAdjustmentBehavior::Never:
return "never";
case ContentInsetAdjustmentBehavior::Automatic:
return "automatic";
case ContentInsetAdjustmentBehavior::ScrollableAxes:
return "scrollableAxes";
case ContentInsetAdjustmentBehavior::Always:
return "always";
}
}
inline std::string toString(const std::optional<ScrollViewMaintainVisibleContentPosition> &value)
{
if (!value) {
return "null";
}
return "{minIndexForVisible: " + toString(value.value().minIndexForVisible) +
", autoscrollToTopThreshold: " + toString(value.value().autoscrollToTopThreshold) + "}";
}
#endif
} // namespace facebook::react

View File

@@ -0,0 +1,18 @@
/*
* 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/components/scrollview/AndroidHorizontalScrollContentViewShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
using AndroidHorizontalScrollContentViewComponentDescriptor =
ConcreteComponentDescriptor<AndroidHorizontalScrollContentViewShadowNode>;
} // namespace facebook::react

View File

@@ -0,0 +1,29 @@
/*
* 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 <react/renderer/components/scrollview/AndroidHorizontalScrollContentViewShadowNode.h>
namespace facebook::react {
const char AndroidHorizontalScrollContentViewShadowNodeComponentName[] =
"AndroidHorizontalScrollContentView";
void AndroidHorizontalScrollContentViewShadowNode::layout(
LayoutContext layoutContext) {
ConcreteViewShadowNode::layout(layoutContext);
// When the layout direction is RTL, we expect Yoga to give us a layout
// that extends off the screen to the left so we re-center it to be at most
// zero (where the scrolling offset will be adjusted to match if larger than
// parent width on the Android component side).
if (layoutMetrics_.layoutDirection == LayoutDirection::RightToLeft &&
layoutMetrics_.frame.origin.x < 0) {
layoutMetrics_.frame.origin.x = 0;
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,30 @@
/*
* 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/components/scrollview/ScrollViewEventEmitter.h>
#include <react/renderer/components/scrollview/ScrollViewState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
namespace facebook::react {
extern const char AndroidHorizontalScrollContentViewShadowNodeComponentName[];
class AndroidHorizontalScrollContentViewShadowNode final
: public ConcreteViewShadowNode<
AndroidHorizontalScrollContentViewShadowNodeComponentName,
ViewProps,
ScrollViewEventEmitter,
ScrollViewState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
void layout(LayoutContext layoutContext) override;
};
} // namespace facebook::react

View File

@@ -0,0 +1,402 @@
/*
* 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 "HostPlatformScrollViewProps.h"
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/scrollview/conversions.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>
#include <react/renderer/core/propsConversions.h>
namespace facebook::react {
HostPlatformScrollViewProps::HostPlatformScrollViewProps(
const PropsParserContext& context,
const HostPlatformScrollViewProps& sourceProps,
const RawProps& rawProps)
: BaseScrollViewProps(context, sourceProps, rawProps),
sendMomentumEvents(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.sendMomentumEvents
: convertRawProp(
context,
rawProps,
"sendMomentumEvents",
sourceProps.sendMomentumEvents,
true)),
nestedScrollEnabled(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.nestedScrollEnabled
: convertRawProp(
context,
rawProps,
"nestedScrollEnabled",
sourceProps.nestedScrollEnabled,
true)),
fadingEdgeLength(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.fadingEdgeLength
: convertRawProp(
context,
rawProps,
"fadingEdgeLength",
sourceProps.fadingEdgeLength,
nullptr)),
overScrollMode(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.overScrollMode
: convertRawProp(
context,
rawProps,
"overScrollMode",
sourceProps.overScrollMode,
"auto")),
endFillColor(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.endFillColor
: convertRawProp(
context,
rawProps,
"endFillColor",
sourceProps.endFillColor,
clearColor())) {}
void HostPlatformScrollViewProps::setProp(
const PropsParserContext& context,
RawPropsPropNameHash hash,
const char* propName,
const RawValue& value) {
// All Props structs setProp methods must always, unconditionally,
// call all super::setProp methods, since multiple structs may
// reuse the same values.
BaseScrollViewProps::setProp(context, hash, propName, value);
static auto defaults = HostPlatformScrollViewProps{};
switch (hash) {
RAW_SET_PROP_SWITCH_CASE_BASIC(sendMomentumEvents);
RAW_SET_PROP_SWITCH_CASE_BASIC(nestedScrollEnabled);
RAW_SET_PROP_SWITCH_CASE_BASIC(fadingEdgeLength);
RAW_SET_PROP_SWITCH_CASE_BASIC(overScrollMode);
RAW_SET_PROP_SWITCH_CASE_BASIC(endFillColor);
}
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList HostPlatformScrollViewProps::getDebugProps()
const {
auto defaultScrollViewProps = HostPlatformScrollViewProps{};
return BaseScrollViewProps::getDebugProps() +
SharedDebugStringConvertibleList{
debugStringConvertibleItem(
"sendMomentumEvents",
sendMomentumEvents,
defaultScrollViewProps.sendMomentumEvents),
debugStringConvertibleItem(
"nestedScrollEnabled",
nestedScrollEnabled,
defaultScrollViewProps.nestedScrollEnabled),
debugStringConvertibleItem(
"overScrollMode",
overScrollMode,
defaultScrollViewProps.overScrollMode),
debugStringConvertibleItem(
"endFillColor",
endFillColor,
defaultScrollViewProps.endFillColor)};
}
#endif
static folly::dynamic convertScrollViewMaintainVisibleContentPosition(
const ScrollViewMaintainVisibleContentPosition& value) {
folly::dynamic result = folly::dynamic::object();
result["minIndexForVisible"] = value.minIndexForVisible;
if (value.autoscrollToTopThreshold.has_value()) {
result["autoscrollToTopThreshold"] = value.autoscrollToTopThreshold.value();
}
return result;
}
static folly::dynamic convertEdgeInsets(const EdgeInsets& edgeInsets) {
folly::dynamic edgeInsetsResult = folly::dynamic::object();
edgeInsetsResult["left"] = edgeInsets.left;
edgeInsetsResult["top"] = edgeInsets.top;
edgeInsetsResult["right"] = edgeInsets.right;
edgeInsetsResult["bottom"] = edgeInsets.bottom;
return edgeInsetsResult;
}
static folly::dynamic convertPoint(const Point& point) {
folly::dynamic pointResult = folly::dynamic::object();
pointResult["y"] = point.y;
pointResult["x"] = point.x;
return pointResult;
}
ComponentName HostPlatformScrollViewProps::getDiffPropsImplementationTarget()
const {
return "ScrollView";
}
folly::dynamic HostPlatformScrollViewProps::getDiffProps(
const Props* prevProps) const {
static const auto defaultProps = HostPlatformScrollViewProps();
const HostPlatformScrollViewProps* oldProps = prevProps == nullptr
? &defaultProps
: static_cast<const HostPlatformScrollViewProps*>(prevProps);
folly::dynamic result = ViewProps::getDiffProps(oldProps);
if (alwaysBounceHorizontal != oldProps->alwaysBounceHorizontal) {
result["alwaysBounceHorizontal"] = alwaysBounceHorizontal;
}
if (alwaysBounceVertical != oldProps->alwaysBounceVertical) {
result["alwaysBounceVertical"] = alwaysBounceVertical;
}
if (bounces != oldProps->bounces) {
result["bounces"] = bounces;
}
if (bouncesZoom != oldProps->bouncesZoom) {
result["bouncesZoom"] = bouncesZoom;
}
if (canCancelContentTouches != oldProps->canCancelContentTouches) {
result["canCancelContentTouches"] = canCancelContentTouches;
}
if (centerContent != oldProps->centerContent) {
result["centerContent"] = centerContent;
}
if (automaticallyAdjustContentInsets !=
oldProps->automaticallyAdjustContentInsets) {
result["automaticallyAdjustContentInsets"] =
automaticallyAdjustContentInsets;
}
if (automaticallyAdjustsScrollIndicatorInsets !=
oldProps->automaticallyAdjustsScrollIndicatorInsets) {
result["automaticallyAdjustsScrollIndicatorInsets"] =
automaticallyAdjustsScrollIndicatorInsets;
}
if (automaticallyAdjustKeyboardInsets !=
oldProps->automaticallyAdjustKeyboardInsets) {
result["automaticallyAdjustKeyboardInsets"] =
automaticallyAdjustKeyboardInsets;
}
if (decelerationRate != oldProps->decelerationRate) {
result["decelerationRate"] = decelerationRate;
}
if (endDraggingSensitivityMultiplier !=
oldProps->endDraggingSensitivityMultiplier) {
result["endDraggingSensitivityMultiplier"] =
endDraggingSensitivityMultiplier;
}
if (directionalLockEnabled != oldProps->directionalLockEnabled) {
result["directionalLockEnabled"] = directionalLockEnabled;
}
if (indicatorStyle != oldProps->indicatorStyle) {
switch (indicatorStyle) {
case ScrollViewIndicatorStyle::Default:
result["indicatorStyle"] = "default";
break;
case ScrollViewIndicatorStyle::Black:
result["indicatorStyle"] = "black";
break;
case ScrollViewIndicatorStyle::White:
result["indicatorStyle"] = "white";
break;
}
}
if (keyboardDismissMode != oldProps->keyboardDismissMode) {
switch (keyboardDismissMode) {
case ScrollViewKeyboardDismissMode::None:
result["keyboardDismissMode"] = "none";
break;
case ScrollViewKeyboardDismissMode::OnDrag:
result["keyboardDismissMode"] = "on-drag";
break;
case ScrollViewKeyboardDismissMode::Interactive:
result["keyboardDismissMode"] = "interactive";
break;
}
}
if (maintainVisibleContentPosition !=
oldProps->maintainVisibleContentPosition) {
if (maintainVisibleContentPosition.has_value()) {
result["maintainVisibleContentPosition"] =
convertScrollViewMaintainVisibleContentPosition(
maintainVisibleContentPosition.value());
} else {
result["maintainVisibleContentPosition"] = folly::dynamic(nullptr);
}
}
if (maximumZoomScale != oldProps->maximumZoomScale) {
result["maximumZoomScale"] = maximumZoomScale;
}
if (minimumZoomScale != oldProps->minimumZoomScale) {
result["minimumZoomScale"] = minimumZoomScale;
}
if (scrollEnabled != oldProps->scrollEnabled) {
result["scrollEnabled"] = scrollEnabled;
}
if (pagingEnabled != oldProps->pagingEnabled) {
result["pagingEnabled"] = pagingEnabled;
}
if (pinchGestureEnabled != oldProps->pinchGestureEnabled) {
result["pinchGestureEnabled"] = pinchGestureEnabled;
}
if (scrollsToTop != oldProps->scrollsToTop) {
result["scrollsToTop"] = scrollsToTop;
}
if (showsHorizontalScrollIndicator !=
oldProps->showsHorizontalScrollIndicator) {
result["showsHorizontalScrollIndicator"] = showsHorizontalScrollIndicator;
}
if (showsVerticalScrollIndicator != oldProps->showsVerticalScrollIndicator) {
result["showsVerticalScrollIndicator"] = showsVerticalScrollIndicator;
}
if (persistentScrollbar != oldProps->persistentScrollbar) {
result["persistentScrollbar"] = persistentScrollbar;
}
if (horizontal != oldProps->horizontal) {
result["horizontal"] = horizontal;
}
if (scrollEventThrottle != oldProps->scrollEventThrottle) {
result["scrollEventThrottle"] = scrollEventThrottle;
}
if (zoomScale != oldProps->zoomScale) {
result["zoomScale"] = zoomScale;
}
if (contentInset != oldProps->contentInset) {
result["contentInset"] = convertEdgeInsets(contentInset);
}
if (contentOffset != oldProps->contentOffset) {
result["contentOffset"] = convertPoint(contentOffset);
}
if (scrollIndicatorInsets != oldProps->scrollIndicatorInsets) {
result["scrollIndicatorInsets"] = convertEdgeInsets(scrollIndicatorInsets);
}
if (snapToInterval != oldProps->snapToInterval) {
result["snapToInterval"] = snapToInterval;
}
if (snapToAlignment != oldProps->snapToAlignment) {
switch (snapToAlignment) {
case ScrollViewSnapToAlignment::Start:
result["snapToAlignment"] = "start";
break;
case ScrollViewSnapToAlignment::Center:
result["snapToAlignment"] = "center";
break;
case ScrollViewSnapToAlignment::End:
result["snapToAlignment"] = "end";
break;
}
}
if (disableIntervalMomentum != oldProps->disableIntervalMomentum) {
result["disableIntervalMomentum"] = disableIntervalMomentum;
}
if (snapToOffsets != oldProps->snapToOffsets) {
auto snapToOffsetsArray = folly::dynamic::array();
for (const auto& snapToOffset : snapToOffsets) {
snapToOffsetsArray.push_back(snapToOffset);
}
result["snapToOffsets"] = snapToOffsetsArray;
}
if (snapToStart != oldProps->snapToStart) {
result["snapToStart"] = snapToStart;
}
if (snapToEnd != oldProps->snapToEnd) {
result["snapToEnd"] = snapToEnd;
}
if (contentInsetAdjustmentBehavior !=
oldProps->contentInsetAdjustmentBehavior) {
switch (contentInsetAdjustmentBehavior) {
case ContentInsetAdjustmentBehavior::Never:
result["contentInsetAdjustmentBehavior"] = "never";
break;
case ContentInsetAdjustmentBehavior::Automatic:
result["contentInsetAdjustmentBehavior"] = "automatic";
break;
case ContentInsetAdjustmentBehavior::ScrollableAxes:
result["contentInsetAdjustmentBehavior"] = "scrollableAxes";
break;
case ContentInsetAdjustmentBehavior::Always:
result["contentInsetAdjustmentBehavior"] = "always";
break;
}
}
if (scrollToOverflowEnabled != oldProps->scrollToOverflowEnabled) {
result["scrollToOverflowEnabled"] = scrollToOverflowEnabled;
}
if (isInvertedVirtualizedList != oldProps->isInvertedVirtualizedList) {
result["isInvertedVirtualizedList"] = isInvertedVirtualizedList;
}
if (sendMomentumEvents != oldProps->sendMomentumEvents) {
result["sendMomentumEvents"] = sendMomentumEvents;
}
if (nestedScrollEnabled != oldProps->nestedScrollEnabled) {
result["nestedScrollEnabled"] = nestedScrollEnabled;
}
if (fadingEdgeLength != oldProps->fadingEdgeLength) {
result["fadingEdgeLength"] = fadingEdgeLength;
}
if (overScrollMode != oldProps->overScrollMode) {
result["overScrollMode"] = overScrollMode;
}
if (endFillColor != oldProps->endFillColor) {
result["endFillColor"] = *endFillColor;
}
return result;
}
} // namespace facebook::react

View File

@@ -0,0 +1,46 @@
/*
* 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/components/scrollview/BaseScrollViewProps.h>
#include <react/renderer/components/scrollview/primitives.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/PropsParserContext.h>
namespace facebook::react {
class HostPlatformScrollViewProps : public BaseScrollViewProps {
public:
HostPlatformScrollViewProps() = default;
HostPlatformScrollViewProps(
const PropsParserContext &context,
const HostPlatformScrollViewProps &sourceProps,
const RawProps &rawProps);
void
setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, const RawValue &value);
#pragma mark - Props
bool sendMomentumEvents{};
bool nestedScrollEnabled{};
folly::dynamic fadingEdgeLength{};
std::string overScrollMode{"auto"};
SharedColor endFillColor{clearColor()};
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
ComponentName getDiffPropsImplementationTarget() const override;
folly::dynamic getDiffProps(const Props *prevProps) const override;
};
} // namespace facebook::react

View File

@@ -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
#include <react/renderer/components/scrollview/BaseScrollViewProps.h>
namespace facebook::react {
using HostPlatformScrollViewProps = BaseScrollViewProps;
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
/*
* 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 <optional>
#include <tuple>
namespace facebook::react {
enum class ScrollViewSnapToAlignment { Start, Center, End };
enum class ScrollViewIndicatorStyle { Default, Black, White };
enum class ScrollViewKeyboardDismissMode { None, OnDrag, Interactive };
enum class ContentInsetAdjustmentBehavior { Never, Automatic, ScrollableAxes, Always };
class ScrollViewMaintainVisibleContentPosition final {
public:
int minIndexForVisible{0};
std::optional<int> autoscrollToTopThreshold{};
bool operator==(const ScrollViewMaintainVisibleContentPosition &rhs) const
{
return std::tie(this->minIndexForVisible, this->autoscrollToTopThreshold) ==
std::tie(rhs.minIndexForVisible, rhs.autoscrollToTopThreshold);
}
bool operator!=(const ScrollViewMaintainVisibleContentPosition &rhs) const
{
return !(*this == rhs);
}
};
} // namespace facebook::react

View File

@@ -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.
*/
#include <memory>
#include <gtest/gtest.h>
TEST(ScrollViewTest, testSomething) {
// TODO
}

View File

@@ -0,0 +1,39 @@
# 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.
cmake_minimum_required(VERSION 3.13)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB rrc_switch_SRCS CONFIGURE_DEPENDS androidswitch/react/renderer/components/androidswitch/*.cpp)
add_library(
rrc_switch
STATIC
${rrc_switch_SRCS}
)
target_include_directories(rrc_switch PUBLIC androidswitch/)
target_link_libraries(
rrc_switch
glog
fbjni
folly_runtime
glog_init
react_codegen_rncore
react_debug
react_renderer_componentregistry
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_uimanager
reactnativejni
rrc_view
yoga
)
target_compile_reactnative_options(rrc_switch PRIVATE)
target_compile_options(rrc_switch PRIVATE -Wpedantic)

View File

@@ -0,0 +1,43 @@
/*
* 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 "AndroidSwitchMeasurementsManager.h"
#include "AndroidSwitchShadowNode.h"
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
/*
* Descriptor for <AndroidSwitch> component.
*/
class AndroidSwitchComponentDescriptor final : public ConcreteComponentDescriptor<AndroidSwitchShadowNode> {
public:
AndroidSwitchComponentDescriptor(const ComponentDescriptorParameters &parameters)
: ConcreteComponentDescriptor(parameters),
measurementsManager_(std::make_shared<AndroidSwitchMeasurementsManager>(contextContainer_))
{
}
void adopt(ShadowNode &shadowNode) const override
{
ConcreteComponentDescriptor::adopt(shadowNode);
auto &androidSwitchShadowNode = static_cast<AndroidSwitchShadowNode &>(shadowNode);
// `AndroidSwitchShadowNode` uses `AndroidSwitchMeasurementsManager` to
// provide measurements to Yoga.
androidSwitchShadowNode.setAndroidSwitchMeasurementsManager(measurementsManager_);
}
private:
const std::shared_ptr<AndroidSwitchMeasurementsManager> measurementsManager_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,57 @@
/*
* 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 "AndroidSwitchMeasurementsManager.h"
#include <fbjni/fbjni.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/core/conversions.h>
using namespace facebook::jni;
namespace facebook::react {
Size AndroidSwitchMeasurementsManager::measure(
SurfaceId surfaceId,
LayoutConstraints layoutConstraints) const {
{
const jni::global_ref<jobject>& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto measure =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<jlong(
jint,
jstring,
ReadableMap::javaobject,
ReadableMap::javaobject,
ReadableMap::javaobject,
jfloat,
jfloat,
jfloat,
jfloat)>("measure");
auto minimumSize = layoutConstraints.minimumSize;
auto maximumSize = layoutConstraints.maximumSize;
local_ref<JString> componentName = make_jstring("AndroidSwitch");
return yogaMeassureToSize(measure(
fabricUIManager,
surfaceId,
componentName.get(),
nullptr,
nullptr,
nullptr,
minimumSize.width,
maximumSize.width,
minimumSize.height,
maximumSize.height));
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,29 @@
/*
* 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/ConcreteComponentDescriptor.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
class AndroidSwitchMeasurementsManager {
public:
AndroidSwitchMeasurementsManager(const std::shared_ptr<const ContextContainer> &contextContainer)
: contextContainer_(contextContainer)
{
}
Size measure(SurfaceId surfaceId, LayoutConstraints layoutConstraints) const;
private:
const std::shared_ptr<const ContextContainer> contextContainer_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,30 @@
/*
* 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 "AndroidSwitchShadowNode.h"
namespace facebook::react {
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
extern const char AndroidSwitchComponentName[] = "AndroidSwitch";
void AndroidSwitchShadowNode::setAndroidSwitchMeasurementsManager(
const std::shared_ptr<AndroidSwitchMeasurementsManager>&
measurementsManager) {
ensureUnsealed();
measurementsManager_ = measurementsManager;
}
#pragma mark - LayoutableShadowNode
Size AndroidSwitchShadowNode::measureContent(
const LayoutContext& /*layoutContext*/,
const LayoutConstraints& layoutConstraints) const {
return measurementsManager_->measure(getSurfaceId(), layoutConstraints);
}
} // namespace facebook::react

View File

@@ -0,0 +1,49 @@
/*
* 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 "AndroidSwitchMeasurementsManager.h"
#include <react/renderer/components/FBReactNativeSpec/EventEmitters.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
extern const char AndroidSwitchComponentName[];
/*
* `ShadowNode` for <AndroidSwitch> component.
*/
class AndroidSwitchShadowNode final
: public ConcreteViewShadowNode<AndroidSwitchComponentName, AndroidSwitchProps, AndroidSwitchEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
return traits;
}
// Associates a shared `AndroidSwitchMeasurementsManager` with the node.
void setAndroidSwitchMeasurementsManager(
const std::shared_ptr<AndroidSwitchMeasurementsManager> &measurementsManager);
#pragma mark - LayoutableShadowNode
Size measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
private:
std::shared_ptr<AndroidSwitchMeasurementsManager> measurementsManager_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,31 @@
/*
* 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 "AppleSwitchShadowNode.h"
#include <react/renderer/core/ConcreteComponentDescriptor.h>
namespace facebook::react {
/*
* Descriptor for <Switch> component.
*/
class SwitchComponentDescriptor final : public ConcreteComponentDescriptor<SwitchShadowNode> {
public:
SwitchComponentDescriptor(const ComponentDescriptorParameters &parameters) : ConcreteComponentDescriptor(parameters)
{
}
void adopt(ShadowNode &shadowNode) const override
{
ConcreteComponentDescriptor::adopt(shadowNode);
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
/*
* 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/components/FBReactNativeSpec/EventEmitters.h>
#include <react/renderer/components/FBReactNativeSpec/Props.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook::react {
extern const char AppleSwitchComponentName[];
/*
* `ShadowNode` for <IOSSwitch> component.
*/
class SwitchShadowNode final
: public ConcreteViewShadowNode<AppleSwitchComponentName, SwitchProps, SwitchEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
static ShadowNodeTraits BaseTraits()
{
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
return traits;
}
#pragma mark - LayoutableShadowNode
Size measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
};
} // namespace facebook::react

Some files were not shown because too many files have changed in this diff Show More