feat: custom shadow nodes (#2568)

# Summary

Implement custom shadow nodes for nearly all `Svg` components. While
it's a foundation for numerous upcoming changes, it currently addresses
and resolves #2544.

## Test Plan

There shouldn't be any noticeable changes, and everything should
function as before, except that `onLayout` will now be triggered only
once and with the correct dimensions.

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| iOS     |          |
| MacOS   |          |
| Android |          |

---------

Co-authored-by: Jakub Piasecki <jakubpiasecki67@gmail.com>
This commit is contained in:
Jakub Grzywacz
2024-12-12 11:48:46 +01:00
committed by GitHub
parent bf1c32d9a5
commit d3d61a5fc1
62 changed files with 560 additions and 55 deletions

View File

@@ -0,0 +1,64 @@
#pragma once
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include "RNSVGShadowNodes.h"
namespace facebook::react {
using RNSVGCircleComponentDescriptor =
ConcreteComponentDescriptor<RNSVGCircleShadowNode>;
using RNSVGClipPathComponentDescriptor =
ConcreteComponentDescriptor<RNSVGClipPathShadowNode>;
using RNSVGDefsComponentDescriptor =
ConcreteComponentDescriptor<RNSVGDefsShadowNode>;
using RNSVGEllipseComponentDescriptor =
ConcreteComponentDescriptor<RNSVGEllipseShadowNode>;
using RNSVGFeBlendComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeBlendShadowNode>;
using RNSVGFeColorMatrixComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeColorMatrixShadowNode>;
using RNSVGFeCompositeComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeCompositeShadowNode>;
using RNSVGFeFloodComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeFloodShadowNode>;
using RNSVGFeGaussianBlurComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeGaussianBlurShadowNode>;
using RNSVGFeMergeComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeMergeShadowNode>;
using RNSVGFeOffsetComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFeOffsetShadowNode>;
using RNSVGFilterComponentDescriptor =
ConcreteComponentDescriptor<RNSVGFilterShadowNode>;
using RNSVGForeignObjectComponentDescriptor =
ConcreteComponentDescriptor<RNSVGForeignObjectShadowNode>;
using RNSVGGroupComponentDescriptor =
ConcreteComponentDescriptor<RNSVGGroupShadowNode>;
using RNSVGLinearGradientComponentDescriptor =
ConcreteComponentDescriptor<RNSVGLinearGradientShadowNode>;
using RNSVGLineComponentDescriptor =
ConcreteComponentDescriptor<RNSVGLineShadowNode>;
using RNSVGMarkerComponentDescriptor =
ConcreteComponentDescriptor<RNSVGMarkerShadowNode>;
using RNSVGMaskComponentDescriptor =
ConcreteComponentDescriptor<RNSVGMaskShadowNode>;
using RNSVGPathComponentDescriptor =
ConcreteComponentDescriptor<RNSVGPathShadowNode>;
using RNSVGPatternComponentDescriptor =
ConcreteComponentDescriptor<RNSVGPatternShadowNode>;
using RNSVGRadialGradientComponentDescriptor =
ConcreteComponentDescriptor<RNSVGRadialGradientShadowNode>;
using RNSVGRectComponentDescriptor =
ConcreteComponentDescriptor<RNSVGRectShadowNode>;
using RNSVGSymbolComponentDescriptor =
ConcreteComponentDescriptor<RNSVGSymbolShadowNode>;
using RNSVGTextComponentDescriptor =
ConcreteComponentDescriptor<RNSVGTextShadowNode>;
using RNSVGTextPathComponentDescriptor =
ConcreteComponentDescriptor<RNSVGTextPathShadowNode>;
using RNSVGTSpanComponentDescriptor =
ConcreteComponentDescriptor<RNSVGTSpanShadowNode>;
using RNSVGUseComponentDescriptor =
ConcreteComponentDescriptor<RNSVGUseShadowNode>;
} // namespace facebook::react

View File

@@ -0,0 +1,59 @@
#pragma once
#include <react/renderer/components/view/ViewEventEmitter.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/ConcreteShadowNode.h>
#include "RNSVGLayoutableShadowNode.h"
namespace facebook::react {
template <const char *concreteComponentName, typename PropsT = ViewProps>
class RNSVGConcreteShadowNode : public ConcreteShadowNode<
concreteComponentName,
RNSVGLayoutableShadowNode,
PropsT,
ViewEventEmitter,
StateData,
false> {
public:
using BaseShadowNode = ConcreteShadowNode<
concreteComponentName,
RNSVGLayoutableShadowNode,
PropsT,
ViewEventEmitter,
StateData,
false>;
using ConcreteViewProps = PropsT;
using BaseShadowNode::BaseShadowNode;
static ShadowNodeTraits BaseTraits() {
auto traits = BaseShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::ViewKind);
traits.set(ShadowNodeTraits::Trait::FormsStackingContext);
traits.set(ShadowNodeTraits::Trait::FormsView);
return traits;
}
Transform getTransform() const override {
auto layoutMetrics = BaseShadowNode::getLayoutMetrics();
return BaseShadowNode::getConcreteProps().resolveTransform(layoutMetrics);
}
bool canBeTouchTarget() const override {
auto pointerEvents =
BaseShadowNode::getConcreteProps().ViewProps::pointerEvents;
return pointerEvents == PointerEventsMode::Auto ||
pointerEvents == PointerEventsMode::BoxOnly;
}
bool canChildrenBeTouchTarget() const override {
auto pointerEvents =
BaseShadowNode::getConcreteProps().ViewProps::pointerEvents;
return pointerEvents == PointerEventsMode::Auto ||
pointerEvents == PointerEventsMode::BoxNone;
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,42 @@
#include "RNSVGLayoutableShadowNode.h"
#include <react/renderer/core/LayoutContext.h>
namespace facebook::react {
RNSVGLayoutableShadowNode::RNSVGLayoutableShadowNode(
const ShadowNodeFragment &fragment,
const ShadowNodeFamily::Shared &family,
ShadowNodeTraits traits)
: YogaLayoutableShadowNode(fragment, family, traits) {
// SVG handles its layout manually on the native side and does not depend on
// the Yoga layout. Setting the dimensions to 0 eliminates randomly positioned
// views in the layout inspector when Yoga attempts to interpret SVG
// properties like width when viewBox scale is set.
auto style = yogaNode_.style();
style.setDimension(yoga::Dimension::Width, yoga::value::points(0));
style.setDimension(yoga::Dimension::Height, yoga::value::points(0));
yogaNode_.setStyle(style);
}
RNSVGLayoutableShadowNode::RNSVGLayoutableShadowNode(
const ShadowNode &sourceShadowNode,
const ShadowNodeFragment &fragment)
: YogaLayoutableShadowNode(sourceShadowNode, fragment) {
// SVG handles its layout manually on the native side and does not depend on
// the Yoga layout. Setting the dimensions to 0 eliminates randomly positioned
// views in the layout inspector when Yoga attempts to interpret SVG
// properties like width when viewBox scale is set.
auto style = yogaNode_.style();
style.setDimension(yoga::Dimension::Width, yoga::value::points(0));
style.setDimension(yoga::Dimension::Height, yoga::value::points(0));
yogaNode_.setStyle(style);
}
void RNSVGLayoutableShadowNode::layout(LayoutContext layoutContext) {
auto affectedNodes = layoutContext.affectedNodes;
layoutContext.affectedNodes = nullptr;
YogaLayoutableShadowNode::layout(layoutContext);
layoutContext.affectedNodes = affectedNodes;
}
} // namespace facebook::react

View File

@@ -0,0 +1,19 @@
#include <react/renderer/components/view/YogaLayoutableShadowNode.h>
namespace facebook::react {
class RNSVGLayoutableShadowNode : public YogaLayoutableShadowNode {
public:
RNSVGLayoutableShadowNode(
const ShadowNodeFragment &fragment,
const ShadowNodeFamily::Shared &family,
ShadowNodeTraits traits);
RNSVGLayoutableShadowNode(
const ShadowNode &sourceShadowNode,
const ShadowNodeFragment &fragment);
void layout(LayoutContext layoutContext) override;
};
} // namespace facebook::react

View File

@@ -0,0 +1,33 @@
#include <react/renderer/components/rnsvg/ShadowNodes.h>
namespace facebook::react {
extern const char RNSVGCircleComponentName[] = "RNSVGCircle";
extern const char RNSVGClipPathComponentName[] = "RNSVGClipPath";
extern const char RNSVGDefsComponentName[] = "RNSVGDefs";
extern const char RNSVGEllipseComponentName[] = "RNSVGEllipse";
extern const char RNSVGFeBlendComponentName[] = "RNSVGFeBlend";
extern const char RNSVGFeColorMatrixComponentName[] = "RNSVGFeColorMatrix";
extern const char RNSVGFeCompositeComponentName[] = "RNSVGFeComposite";
extern const char RNSVGFeFloodComponentName[] = "RNSVGFeFlood";
extern const char RNSVGFeGaussianBlurComponentName[] = "RNSVGFeGaussianBlur";
extern const char RNSVGFeMergeComponentName[] = "RNSVGFeMerge";
extern const char RNSVGFeOffsetComponentName[] = "RNSVGFeOffset";
extern const char RNSVGFilterComponentName[] = "RNSVGFilter";
extern const char RNSVGForeignObjectComponentName[] = "RNSVGForeignObject";
extern const char RNSVGGroupComponentName[] = "RNSVGGroup";
extern const char RNSVGLinearGradientComponentName[] = "RNSVGLinearGradient";
extern const char RNSVGLineComponentName[] = "RNSVGLine";
extern const char RNSVGMarkerComponentName[] = "RNSVGMarker";
extern const char RNSVGMaskComponentName[] = "RNSVGMask";
extern const char RNSVGPathComponentName[] = "RNSVGPath";
extern const char RNSVGPatternComponentName[] = "RNSVGPattern";
extern const char RNSVGRadialGradientComponentName[] = "RNSVGRadialGradient";
extern const char RNSVGRectComponentName[] = "RNSVGRect";
extern const char RNSVGSymbolComponentName[] = "RNSVGSymbol";
extern const char RNSVGTextComponentName[] = "RNSVGText";
extern const char RNSVGTextPathComponentName[] = "RNSVGTextPath";
extern const char RNSVGTSpanComponentName[] = "RNSVGTSpan";
extern const char RNSVGUseComponentName[] = "RNSVGUse";
} // namespace facebook::react

View File

@@ -0,0 +1,233 @@
#pragma once
#include <jsi/jsi.h>
#include <react/renderer/components/rnsvg/EventEmitters.h>
#include <react/renderer/components/rnsvg/Props.h>
#include "RNSVGConcreteShadowNode.h"
#include "RNSVGImageState.h"
namespace facebook::react {
JSI_EXPORT extern const char RNSVGCircleComponentName[];
/*
* `ShadowNode` for <RNSVGCircle> component.
*/
using RNSVGCircleShadowNode =
RNSVGConcreteShadowNode<RNSVGCircleComponentName, RNSVGCircleProps>;
JSI_EXPORT extern const char RNSVGClipPathComponentName[];
/*
* `ShadowNode` for <RNSVGClipPath> component.
*/
using RNSVGClipPathShadowNode =
RNSVGConcreteShadowNode<RNSVGClipPathComponentName, RNSVGClipPathProps>;
JSI_EXPORT extern const char RNSVGDefsComponentName[];
/*
* `ShadowNode` for <RNSVGDefs> component.
*/
using RNSVGDefsShadowNode =
RNSVGConcreteShadowNode<RNSVGDefsComponentName, RNSVGDefsProps>;
JSI_EXPORT extern const char RNSVGEllipseComponentName[];
/*
* `ShadowNode` for <RNSVGEllipse> component.
*/
using RNSVGEllipseShadowNode =
RNSVGConcreteShadowNode<RNSVGEllipseComponentName, RNSVGEllipseProps>;
JSI_EXPORT extern const char RNSVGFeBlendComponentName[];
/*
* `ShadowNode` for <RNSVGFeBlend> component.
*/
using RNSVGFeBlendShadowNode =
RNSVGConcreteShadowNode<RNSVGFeBlendComponentName, RNSVGFeBlendProps>;
JSI_EXPORT extern const char RNSVGFeColorMatrixComponentName[];
/*
* `ShadowNode` for <RNSVGFeColorMatrix> component.
*/
using RNSVGFeColorMatrixShadowNode = RNSVGConcreteShadowNode<
RNSVGFeColorMatrixComponentName,
RNSVGFeColorMatrixProps>;
JSI_EXPORT extern const char RNSVGFeCompositeComponentName[];
/*
* `ShadowNode` for <RNSVGFeComposite> component.
*/
using RNSVGFeCompositeShadowNode = RNSVGConcreteShadowNode<
RNSVGFeCompositeComponentName,
RNSVGFeCompositeProps>;
JSI_EXPORT extern const char RNSVGFeFloodComponentName[];
/*
* `ShadowNode` for <RNSVGFeFlood> component.
*/
using RNSVGFeFloodShadowNode =
RNSVGConcreteShadowNode<RNSVGFeFloodComponentName, RNSVGFeFloodProps>;
JSI_EXPORT extern const char RNSVGFeGaussianBlurComponentName[];
/*
* `ShadowNode` for <RNSVGFeGaussianBlur> component.
*/
using RNSVGFeGaussianBlurShadowNode = RNSVGConcreteShadowNode<
RNSVGFeGaussianBlurComponentName,
RNSVGFeGaussianBlurProps>;
JSI_EXPORT extern const char RNSVGFeMergeComponentName[];
/*
* `ShadowNode` for <RNSVGFeMerge> component.
*/
using RNSVGFeMergeShadowNode =
RNSVGConcreteShadowNode<RNSVGFeMergeComponentName, RNSVGFeMergeProps>;
JSI_EXPORT extern const char RNSVGFeOffsetComponentName[];
/*
* `ShadowNode` for <RNSVGFeOffset> component.
*/
using RNSVGFeOffsetShadowNode =
RNSVGConcreteShadowNode<RNSVGFeOffsetComponentName, RNSVGFeOffsetProps>;
JSI_EXPORT extern const char RNSVGFilterComponentName[];
/*
* `ShadowNode` for <RNSVGFilter> component.
*/
using RNSVGFilterShadowNode =
RNSVGConcreteShadowNode<RNSVGFilterComponentName, RNSVGFilterProps>;
JSI_EXPORT extern const char RNSVGForeignObjectComponentName[];
/*
* `ShadowNode` for <RNSVGForeignObject> component.
*/
using RNSVGForeignObjectShadowNode = RNSVGConcreteShadowNode<
RNSVGForeignObjectComponentName,
RNSVGForeignObjectProps>;
JSI_EXPORT extern const char RNSVGGroupComponentName[];
/*
* `ShadowNode` for <RNSVGGroup> component.
*/
using RNSVGGroupShadowNode =
RNSVGConcreteShadowNode<RNSVGGroupComponentName, RNSVGGroupProps>;
JSI_EXPORT extern const char RNSVGLinearGradientComponentName[];
/*
* `ShadowNode` for <RNSVGLinearGradient> component.
*/
using RNSVGLinearGradientShadowNode = RNSVGConcreteShadowNode<
RNSVGLinearGradientComponentName,
RNSVGLinearGradientProps>;
JSI_EXPORT extern const char RNSVGLineComponentName[];
/*
* `ShadowNode` for <RNSVGLine> component.
*/
using RNSVGLineShadowNode =
RNSVGConcreteShadowNode<RNSVGLineComponentName, RNSVGLineProps>;
JSI_EXPORT extern const char RNSVGMarkerComponentName[];
/*
* `ShadowNode` for <RNSVGMarker> component.
*/
using RNSVGMarkerShadowNode =
RNSVGConcreteShadowNode<RNSVGMarkerComponentName, RNSVGMarkerProps>;
JSI_EXPORT extern const char RNSVGMaskComponentName[];
/*
* `ShadowNode` for <RNSVGMask> component.
*/
using RNSVGMaskShadowNode =
RNSVGConcreteShadowNode<RNSVGMaskComponentName, RNSVGMaskProps>;
JSI_EXPORT extern const char RNSVGPathComponentName[];
/*
* `ShadowNode` for <RNSVGPath> component.
*/
using RNSVGPathShadowNode =
RNSVGConcreteShadowNode<RNSVGPathComponentName, RNSVGPathProps>;
JSI_EXPORT extern const char RNSVGPatternComponentName[];
/*
* `ShadowNode` for <RNSVGPattern> component.
*/
using RNSVGPatternShadowNode =
RNSVGConcreteShadowNode<RNSVGPatternComponentName, RNSVGPatternProps>;
JSI_EXPORT extern const char RNSVGRadialGradientComponentName[];
/*
* `ShadowNode` for <RNSVGRadialGradient> component.
*/
using RNSVGRadialGradientShadowNode = RNSVGConcreteShadowNode<
RNSVGRadialGradientComponentName,
RNSVGRadialGradientProps>;
JSI_EXPORT extern const char RNSVGRectComponentName[];
/*
* `ShadowNode` for <RNSVGRect> component.
*/
using RNSVGRectShadowNode =
RNSVGConcreteShadowNode<RNSVGRectComponentName, RNSVGRectProps>;
JSI_EXPORT extern const char RNSVGSymbolComponentName[];
/*
* `ShadowNode` for <RNSVGSymbol> component.
*/
using RNSVGSymbolShadowNode =
RNSVGConcreteShadowNode<RNSVGSymbolComponentName, RNSVGSymbolProps>;
JSI_EXPORT extern const char RNSVGTextComponentName[];
/*
* `ShadowNode` for <RNSVGText> component.
*/
using RNSVGTextShadowNode =
RNSVGConcreteShadowNode<RNSVGTextComponentName, RNSVGTextProps>;
JSI_EXPORT extern const char RNSVGTextPathComponentName[];
/*
* `ShadowNode` for <RNSVGTextPath> component.
*/
using RNSVGTextPathShadowNode =
RNSVGConcreteShadowNode<RNSVGTextPathComponentName, RNSVGTextPathProps>;
JSI_EXPORT extern const char RNSVGTSpanComponentName[];
/*
* `ShadowNode` for <RNSVGTSpan> component.
*/
using RNSVGTSpanShadowNode =
RNSVGConcreteShadowNode<RNSVGTSpanComponentName, RNSVGTSpanProps>;
JSI_EXPORT extern const char RNSVGUseComponentName[];
/*
* `ShadowNode` for <RNSVGUse> component.
*/
using RNSVGUseShadowNode =
RNSVGConcreteShadowNode<RNSVGUseComponentName, RNSVGUseProps>;
} // namespace facebook::react