706 lines
25 KiB
Mathematica
706 lines
25 KiB
Mathematica
|
/*
|
||
|
* 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 "RCTShadowView.h"
|
||
|
|
||
|
#import "RCTConvert.h"
|
||
|
#import "RCTI18nUtil.h"
|
||
|
#import "RCTLayout.h"
|
||
|
#import "RCTLog.h"
|
||
|
#import "RCTShadowView+Layout.h"
|
||
|
#import "RCTUtils.h"
|
||
|
#import "UIView+Private.h"
|
||
|
#import "UIView+React.h"
|
||
|
|
||
|
typedef void (^RCTActionBlock)(RCTShadowView *shadowViewSelf, id value);
|
||
|
typedef void (^RCTResetActionBlock)(RCTShadowView *shadowViewSelf);
|
||
|
|
||
|
typedef NS_ENUM(unsigned int, meta_prop_t) {
|
||
|
META_PROP_LEFT,
|
||
|
META_PROP_TOP,
|
||
|
META_PROP_RIGHT,
|
||
|
META_PROP_BOTTOM,
|
||
|
META_PROP_START,
|
||
|
META_PROP_END,
|
||
|
META_PROP_HORIZONTAL,
|
||
|
META_PROP_VERTICAL,
|
||
|
META_PROP_ALL,
|
||
|
META_PROP_COUNT,
|
||
|
};
|
||
|
|
||
|
@implementation RCTShadowView {
|
||
|
NSDictionary *_lastParentProperties;
|
||
|
NSMutableArray<RCTShadowView *> *_reactSubviews;
|
||
|
BOOL _recomputePadding;
|
||
|
BOOL _recomputeMargin;
|
||
|
BOOL _recomputeBorder;
|
||
|
YGValue _paddingMetaProps[META_PROP_COUNT];
|
||
|
YGValue _marginMetaProps[META_PROP_COUNT];
|
||
|
YGValue _borderMetaProps[META_PROP_COUNT];
|
||
|
}
|
||
|
|
||
|
+ (YGConfigRef)yogaConfig
|
||
|
{
|
||
|
static YGConfigRef yogaConfig;
|
||
|
static dispatch_once_t onceToken;
|
||
|
dispatch_once(&onceToken, ^{
|
||
|
yogaConfig = YGConfigNew();
|
||
|
YGConfigSetPointScaleFactor(yogaConfig, RCTScreenScale());
|
||
|
YGConfigSetUseLegacyStretchBehaviour(yogaConfig, true);
|
||
|
});
|
||
|
return yogaConfig;
|
||
|
}
|
||
|
|
||
|
@synthesize reactTag = _reactTag;
|
||
|
@synthesize rootTag = _rootTag;
|
||
|
|
||
|
// YogaNode API
|
||
|
|
||
|
static void RCTPrint(YGNodeRef node)
|
||
|
{
|
||
|
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
|
||
|
printf("%s(%lld), ", shadowView.viewName.UTF8String, (long long)shadowView.reactTag.integerValue);
|
||
|
}
|
||
|
|
||
|
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \
|
||
|
switch (ygvalue.unit) { \
|
||
|
case YGUnitAuto: \
|
||
|
case YGUnitUndefined: \
|
||
|
setter(__VA_ARGS__, YGUndefined); \
|
||
|
break; \
|
||
|
case YGUnitPoint: \
|
||
|
setter(__VA_ARGS__, ygvalue.value); \
|
||
|
break; \
|
||
|
case YGUnitPercent: \
|
||
|
setter##Percent(__VA_ARGS__, ygvalue.value); \
|
||
|
break; \
|
||
|
}
|
||
|
|
||
|
#define RCT_SET_YGVALUE_AUTO(ygvalue, setter, ...) \
|
||
|
switch (ygvalue.unit) { \
|
||
|
case YGUnitAuto: \
|
||
|
setter##Auto(__VA_ARGS__); \
|
||
|
break; \
|
||
|
case YGUnitUndefined: \
|
||
|
setter(__VA_ARGS__, YGUndefined); \
|
||
|
break; \
|
||
|
case YGUnitPoint: \
|
||
|
setter(__VA_ARGS__, ygvalue.value); \
|
||
|
break; \
|
||
|
case YGUnitPercent: \
|
||
|
setter##Percent(__VA_ARGS__, ygvalue.value); \
|
||
|
break; \
|
||
|
}
|
||
|
|
||
|
static void RCTProcessMetaPropsPadding(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
|
||
|
{
|
||
|
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_START], YGNodeStyleSetPadding, node, YGEdgeStart);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_END], YGNodeStyleSetPadding, node, YGEdgeEnd);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_LEFT], YGNodeStyleSetPadding, node, YGEdgeLeft);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_RIGHT], YGNodeStyleSetPadding, node, YGEdgeRight);
|
||
|
} else {
|
||
|
YGValue start =
|
||
|
metaProps[META_PROP_START].unit == YGUnitUndefined ? metaProps[META_PROP_LEFT] : metaProps[META_PROP_START];
|
||
|
YGValue end =
|
||
|
metaProps[META_PROP_END].unit == YGUnitUndefined ? metaProps[META_PROP_RIGHT] : metaProps[META_PROP_END];
|
||
|
RCT_SET_YGVALUE(start, YGNodeStyleSetPadding, node, YGEdgeStart);
|
||
|
RCT_SET_YGVALUE(end, YGNodeStyleSetPadding, node, YGEdgeEnd);
|
||
|
}
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_TOP], YGNodeStyleSetPadding, node, YGEdgeTop);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_BOTTOM], YGNodeStyleSetPadding, node, YGEdgeBottom);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetPadding, node, YGEdgeHorizontal);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_VERTICAL], YGNodeStyleSetPadding, node, YGEdgeVertical);
|
||
|
RCT_SET_YGVALUE(metaProps[META_PROP_ALL], YGNodeStyleSetPadding, node, YGEdgeAll);
|
||
|
}
|
||
|
|
||
|
static void RCTProcessMetaPropsMargin(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
|
||
|
{
|
||
|
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_START], YGNodeStyleSetMargin, node, YGEdgeStart);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_END], YGNodeStyleSetMargin, node, YGEdgeEnd);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_LEFT], YGNodeStyleSetMargin, node, YGEdgeLeft);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_RIGHT], YGNodeStyleSetMargin, node, YGEdgeRight);
|
||
|
} else {
|
||
|
YGValue start =
|
||
|
metaProps[META_PROP_START].unit == YGUnitUndefined ? metaProps[META_PROP_LEFT] : metaProps[META_PROP_START];
|
||
|
YGValue end =
|
||
|
metaProps[META_PROP_END].unit == YGUnitUndefined ? metaProps[META_PROP_RIGHT] : metaProps[META_PROP_END];
|
||
|
RCT_SET_YGVALUE_AUTO(start, YGNodeStyleSetMargin, node, YGEdgeStart);
|
||
|
RCT_SET_YGVALUE_AUTO(end, YGNodeStyleSetMargin, node, YGEdgeEnd);
|
||
|
}
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_TOP], YGNodeStyleSetMargin, node, YGEdgeTop);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_BOTTOM], YGNodeStyleSetMargin, node, YGEdgeBottom);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetMargin, node, YGEdgeHorizontal);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_VERTICAL], YGNodeStyleSetMargin, node, YGEdgeVertical);
|
||
|
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_ALL], YGNodeStyleSetMargin, node, YGEdgeAll);
|
||
|
}
|
||
|
|
||
|
static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
|
||
|
{
|
||
|
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
|
||
|
YGNodeStyleSetBorder(node, YGEdgeStart, metaProps[META_PROP_START].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeEnd, metaProps[META_PROP_END].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeLeft, metaProps[META_PROP_LEFT].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeRight, metaProps[META_PROP_RIGHT].value);
|
||
|
} else {
|
||
|
const float start = YGFloatIsUndefined(metaProps[META_PROP_START].value) ? metaProps[META_PROP_LEFT].value
|
||
|
: metaProps[META_PROP_START].value;
|
||
|
const float end = YGFloatIsUndefined(metaProps[META_PROP_END].value) ? metaProps[META_PROP_RIGHT].value
|
||
|
: metaProps[META_PROP_END].value;
|
||
|
YGNodeStyleSetBorder(node, YGEdgeStart, start);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeEnd, end);
|
||
|
}
|
||
|
YGNodeStyleSetBorder(node, YGEdgeTop, metaProps[META_PROP_TOP].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeBottom, metaProps[META_PROP_BOTTOM].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeHorizontal, metaProps[META_PROP_HORIZONTAL].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeVertical, metaProps[META_PROP_VERTICAL].value);
|
||
|
YGNodeStyleSetBorder(node, YGEdgeAll, metaProps[META_PROP_ALL].value);
|
||
|
}
|
||
|
|
||
|
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
|
||
|
{
|
||
|
CGPoint offset = CGPointZero;
|
||
|
RCTShadowView *shadowView = self;
|
||
|
while (shadowView && shadowView != ancestor) {
|
||
|
offset.x += shadowView.layoutMetrics.frame.origin.x;
|
||
|
offset.y += shadowView.layoutMetrics.frame.origin.y;
|
||
|
shadowView = shadowView->_superview;
|
||
|
}
|
||
|
if (ancestor != shadowView) {
|
||
|
return CGRectNull;
|
||
|
}
|
||
|
return (CGRect){offset, self.layoutMetrics.frame.size};
|
||
|
}
|
||
|
|
||
|
- (BOOL)viewIsDescendantOf:(RCTShadowView *)ancestor
|
||
|
{
|
||
|
RCTShadowView *shadowView = self;
|
||
|
while (shadowView && shadowView != ancestor) {
|
||
|
shadowView = shadowView->_superview;
|
||
|
}
|
||
|
return ancestor == shadowView;
|
||
|
}
|
||
|
|
||
|
- (instancetype)init
|
||
|
{
|
||
|
if (self = [super init]) {
|
||
|
for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) {
|
||
|
_paddingMetaProps[ii] = YGValueUndefined;
|
||
|
_marginMetaProps[ii] = YGValueUndefined;
|
||
|
_borderMetaProps[ii] = YGValueUndefined;
|
||
|
}
|
||
|
|
||
|
_intrinsicContentSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
|
||
|
|
||
|
_newView = YES;
|
||
|
|
||
|
_reactSubviews = [NSMutableArray array];
|
||
|
|
||
|
_yogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
|
||
|
YGNodeSetContext(_yogaNode, (__bridge void *)self);
|
||
|
YGNodeSetPrintFunc(_yogaNode, RCTPrint);
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (BOOL)isReactRootView
|
||
|
{
|
||
|
return RCTIsReactRootView(self.reactTag);
|
||
|
}
|
||
|
|
||
|
- (void)dealloc
|
||
|
{
|
||
|
YGNodeFree(_yogaNode);
|
||
|
}
|
||
|
|
||
|
- (BOOL)canHaveSubviews
|
||
|
{
|
||
|
return YES;
|
||
|
}
|
||
|
|
||
|
- (BOOL)isYogaLeafNode
|
||
|
{
|
||
|
return NO;
|
||
|
}
|
||
|
|
||
|
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
|
||
|
{
|
||
|
RCTAssert(self.canHaveSubviews, @"Attempt to insert subview inside leaf view.");
|
||
|
|
||
|
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||
|
if (![self isYogaLeafNode]) {
|
||
|
YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
|
||
|
}
|
||
|
subview->_superview = self;
|
||
|
}
|
||
|
|
||
|
- (void)removeReactSubview:(RCTShadowView *)subview
|
||
|
{
|
||
|
subview->_superview = nil;
|
||
|
[_reactSubviews removeObject:subview];
|
||
|
if (![self isYogaLeafNode]) {
|
||
|
YGNodeRemoveChild(_yogaNode, subview.yogaNode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (NSArray<RCTShadowView *> *)reactSubviews
|
||
|
{
|
||
|
return _reactSubviews;
|
||
|
}
|
||
|
|
||
|
- (RCTShadowView *)reactSuperview
|
||
|
{
|
||
|
return _superview;
|
||
|
}
|
||
|
|
||
|
#pragma mark - Layout
|
||
|
|
||
|
- (void)layoutWithMinimumSize:(CGSize)minimumSize
|
||
|
maximumSize:(CGSize)maximumSize
|
||
|
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
|
||
|
layoutContext:(RCTLayoutContext)layoutContext
|
||
|
{
|
||
|
YGNodeRef yogaNode = _yogaNode;
|
||
|
|
||
|
CGSize oldMinimumSize = (CGSize){
|
||
|
RCTCoreGraphicsFloatFromYogaValue(YGNodeStyleGetMinWidth(yogaNode), 0.0),
|
||
|
RCTCoreGraphicsFloatFromYogaValue(YGNodeStyleGetMinHeight(yogaNode), 0.0)};
|
||
|
|
||
|
if (!CGSizeEqualToSize(oldMinimumSize, minimumSize)) {
|
||
|
YGNodeStyleSetMinWidth(yogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.width));
|
||
|
YGNodeStyleSetMinHeight(yogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.height));
|
||
|
}
|
||
|
|
||
|
YGNodeCalculateLayout(
|
||
|
yogaNode,
|
||
|
RCTYogaFloatFromCoreGraphicsFloat(maximumSize.width),
|
||
|
RCTYogaFloatFromCoreGraphicsFloat(maximumSize.height),
|
||
|
RCTYogaLayoutDirectionFromUIKitLayoutDirection(layoutDirection));
|
||
|
|
||
|
RCTAssert(!YGNodeIsDirty(yogaNode), @"Attempt to get layout metrics from dirtied Yoga node.");
|
||
|
|
||
|
if (!YGNodeGetHasNewLayout(yogaNode)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
YGNodeSetHasNewLayout(yogaNode, false);
|
||
|
|
||
|
RCTLayoutMetrics layoutMetrics = RCTLayoutMetricsFromYogaNode(yogaNode);
|
||
|
|
||
|
layoutContext.absolutePosition.x += layoutMetrics.frame.origin.x;
|
||
|
layoutContext.absolutePosition.y += layoutMetrics.frame.origin.y;
|
||
|
|
||
|
[self layoutWithMetrics:layoutMetrics layoutContext:layoutContext];
|
||
|
|
||
|
[self layoutSubviewsWithContext:layoutContext];
|
||
|
}
|
||
|
|
||
|
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext
|
||
|
{
|
||
|
if (!RCTLayoutMetricsEqualToLayoutMetrics(self.layoutMetrics, layoutMetrics)) {
|
||
|
self.layoutMetrics = layoutMetrics;
|
||
|
[layoutContext.affectedShadowViews addObject:self];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext
|
||
|
{
|
||
|
RCTLayoutMetrics layoutMetrics = self.layoutMetrics;
|
||
|
|
||
|
if (layoutMetrics.displayType == RCTDisplayTypeNone) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (RCTShadowView *childShadowView in _reactSubviews) {
|
||
|
YGNodeRef childYogaNode = childShadowView.yogaNode;
|
||
|
|
||
|
RCTAssert(!YGNodeIsDirty(childYogaNode), @"Attempt to get layout metrics from dirtied Yoga node.");
|
||
|
|
||
|
if (!YGNodeGetHasNewLayout(childYogaNode)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
YGNodeSetHasNewLayout(childYogaNode, false);
|
||
|
|
||
|
RCTLayoutMetrics childLayoutMetrics = RCTLayoutMetricsFromYogaNode(childYogaNode);
|
||
|
|
||
|
layoutContext.absolutePosition.x += childLayoutMetrics.frame.origin.x;
|
||
|
layoutContext.absolutePosition.y += childLayoutMetrics.frame.origin.y;
|
||
|
|
||
|
[childShadowView layoutWithMetrics:childLayoutMetrics layoutContext:layoutContext];
|
||
|
|
||
|
// Recursive call.
|
||
|
[childShadowView layoutSubviewsWithContext:layoutContext];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
|
||
|
{
|
||
|
YGNodeRef clonedYogaNode = YGNodeClone(self.yogaNode);
|
||
|
YGNodeRef constraintYogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
|
||
|
|
||
|
YGNodeInsertChild(constraintYogaNode, clonedYogaNode, 0);
|
||
|
|
||
|
YGNodeStyleSetMinWidth(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.width));
|
||
|
YGNodeStyleSetMinHeight(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.height));
|
||
|
YGNodeStyleSetMaxWidth(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(maximumSize.width));
|
||
|
YGNodeStyleSetMaxHeight(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(maximumSize.height));
|
||
|
|
||
|
YGNodeCalculateLayout(
|
||
|
constraintYogaNode,
|
||
|
YGUndefined,
|
||
|
YGUndefined,
|
||
|
RCTYogaLayoutDirectionFromUIKitLayoutDirection(self.layoutMetrics.layoutDirection));
|
||
|
|
||
|
CGSize measuredSize = (CGSize){
|
||
|
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetWidth(constraintYogaNode)),
|
||
|
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetHeight(constraintYogaNode)),
|
||
|
};
|
||
|
|
||
|
YGNodeRemoveChild(constraintYogaNode, clonedYogaNode);
|
||
|
YGNodeFree(constraintYogaNode);
|
||
|
YGNodeFree(clonedYogaNode);
|
||
|
|
||
|
return measuredSize;
|
||
|
}
|
||
|
|
||
|
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||
|
{
|
||
|
for (RCTShadowView *shadowView in _reactSubviews) {
|
||
|
if (CGRectContainsPoint(shadowView.layoutMetrics.frame, point)) {
|
||
|
CGPoint relativePoint = point;
|
||
|
CGPoint origin = shadowView.layoutMetrics.frame.origin;
|
||
|
relativePoint.x -= origin.x;
|
||
|
relativePoint.y -= origin.y;
|
||
|
return [shadowView reactTagAtPoint:relativePoint];
|
||
|
}
|
||
|
}
|
||
|
return self.reactTag;
|
||
|
}
|
||
|
|
||
|
- (NSString *)description
|
||
|
{
|
||
|
NSString *description = super.description;
|
||
|
description = [[description substringToIndex:description.length - 1]
|
||
|
stringByAppendingFormat:@"; viewName: %@; reactTag: %@; frame: %@>",
|
||
|
self.viewName,
|
||
|
self.reactTag,
|
||
|
NSStringFromCGRect(self.layoutMetrics.frame)];
|
||
|
return description;
|
||
|
}
|
||
|
|
||
|
- (void)addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
|
||
|
{
|
||
|
for (NSUInteger i = 0; i < level; i++) {
|
||
|
[string appendString:@" | "];
|
||
|
}
|
||
|
|
||
|
[string appendString:self.description];
|
||
|
[string appendString:@"\n"];
|
||
|
|
||
|
for (RCTShadowView *subview in _reactSubviews) {
|
||
|
[subview addRecursiveDescriptionToString:string atLevel:level + 1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (NSString *)recursiveDescription
|
||
|
{
|
||
|
NSMutableString *description = [NSMutableString string];
|
||
|
[self addRecursiveDescriptionToString:description atLevel:0];
|
||
|
return description;
|
||
|
}
|
||
|
|
||
|
// Margin
|
||
|
|
||
|
#define RCT_MARGIN_PROPERTY(prop, metaProp) \
|
||
|
-(void)setMargin##prop : (YGValue)value \
|
||
|
{ \
|
||
|
_marginMetaProps[META_PROP_##metaProp] = value; \
|
||
|
_recomputeMargin = YES; \
|
||
|
} \
|
||
|
-(YGValue)margin##prop \
|
||
|
{ \
|
||
|
return _marginMetaProps[META_PROP_##metaProp]; \
|
||
|
}
|
||
|
|
||
|
RCT_MARGIN_PROPERTY(, ALL)
|
||
|
RCT_MARGIN_PROPERTY(Vertical, VERTICAL)
|
||
|
RCT_MARGIN_PROPERTY(Horizontal, HORIZONTAL)
|
||
|
RCT_MARGIN_PROPERTY(Top, TOP)
|
||
|
RCT_MARGIN_PROPERTY(Left, LEFT)
|
||
|
RCT_MARGIN_PROPERTY(Bottom, BOTTOM)
|
||
|
RCT_MARGIN_PROPERTY(Right, RIGHT)
|
||
|
RCT_MARGIN_PROPERTY(Start, START)
|
||
|
RCT_MARGIN_PROPERTY(End, END)
|
||
|
|
||
|
// Padding
|
||
|
|
||
|
#define RCT_PADDING_PROPERTY(prop, metaProp) \
|
||
|
-(void)setPadding##prop : (YGValue)value \
|
||
|
{ \
|
||
|
_paddingMetaProps[META_PROP_##metaProp] = value; \
|
||
|
_recomputePadding = YES; \
|
||
|
} \
|
||
|
-(YGValue)padding##prop \
|
||
|
{ \
|
||
|
return _paddingMetaProps[META_PROP_##metaProp]; \
|
||
|
}
|
||
|
|
||
|
RCT_PADDING_PROPERTY(, ALL)
|
||
|
RCT_PADDING_PROPERTY(Vertical, VERTICAL)
|
||
|
RCT_PADDING_PROPERTY(Horizontal, HORIZONTAL)
|
||
|
RCT_PADDING_PROPERTY(Top, TOP)
|
||
|
RCT_PADDING_PROPERTY(Left, LEFT)
|
||
|
RCT_PADDING_PROPERTY(Bottom, BOTTOM)
|
||
|
RCT_PADDING_PROPERTY(Right, RIGHT)
|
||
|
RCT_PADDING_PROPERTY(Start, START)
|
||
|
RCT_PADDING_PROPERTY(End, END)
|
||
|
|
||
|
// Border
|
||
|
|
||
|
#define RCT_BORDER_PROPERTY(prop, metaProp) \
|
||
|
-(void)setBorder##prop##Width : (float)value \
|
||
|
{ \
|
||
|
_borderMetaProps[META_PROP_##metaProp].value = value; \
|
||
|
_recomputeBorder = YES; \
|
||
|
} \
|
||
|
-(float)border##prop##Width \
|
||
|
{ \
|
||
|
return _borderMetaProps[META_PROP_##metaProp].value; \
|
||
|
}
|
||
|
|
||
|
RCT_BORDER_PROPERTY(, ALL)
|
||
|
RCT_BORDER_PROPERTY(Top, TOP)
|
||
|
RCT_BORDER_PROPERTY(Left, LEFT)
|
||
|
RCT_BORDER_PROPERTY(Bottom, BOTTOM)
|
||
|
RCT_BORDER_PROPERTY(Right, RIGHT)
|
||
|
RCT_BORDER_PROPERTY(Start, START)
|
||
|
RCT_BORDER_PROPERTY(End, END)
|
||
|
|
||
|
// Dimensions
|
||
|
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
|
||
|
-(void)set##setProp : (YGValue)value \
|
||
|
{ \
|
||
|
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSet##cssProp, _yogaNode); \
|
||
|
} \
|
||
|
-(YGValue)getProp \
|
||
|
{ \
|
||
|
return YGNodeStyleGet##cssProp(_yogaNode); \
|
||
|
}
|
||
|
|
||
|
#define RCT_MIN_MAX_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
|
||
|
-(void)set##setProp : (YGValue)value \
|
||
|
{ \
|
||
|
RCT_SET_YGVALUE(value, YGNodeStyleSet##cssProp, _yogaNode); \
|
||
|
} \
|
||
|
-(YGValue)getProp \
|
||
|
{ \
|
||
|
return YGNodeStyleGet##cssProp(_yogaNode); \
|
||
|
}
|
||
|
|
||
|
RCT_DIMENSION_PROPERTY(Width, width, Width)
|
||
|
RCT_DIMENSION_PROPERTY(Height, height, Height)
|
||
|
RCT_MIN_MAX_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth)
|
||
|
RCT_MIN_MAX_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight)
|
||
|
RCT_MIN_MAX_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth)
|
||
|
RCT_MIN_MAX_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight)
|
||
|
|
||
|
// Position
|
||
|
|
||
|
#define RCT_POSITION_PROPERTY(setProp, getProp, edge) \
|
||
|
-(void)set##setProp : (YGValue)value \
|
||
|
{ \
|
||
|
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge); \
|
||
|
} \
|
||
|
-(YGValue)getProp \
|
||
|
{ \
|
||
|
return YGNodeStyleGetPosition(_yogaNode, edge); \
|
||
|
}
|
||
|
|
||
|
RCT_POSITION_PROPERTY(Top, top, YGEdgeTop)
|
||
|
RCT_POSITION_PROPERTY(Bottom, bottom, YGEdgeBottom)
|
||
|
RCT_POSITION_PROPERTY(Start, start, YGEdgeStart)
|
||
|
RCT_POSITION_PROPERTY(End, end, YGEdgeEnd)
|
||
|
|
||
|
- (void)setLeft:(YGValue)value
|
||
|
{
|
||
|
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
|
||
|
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
|
||
|
}
|
||
|
- (YGValue)left
|
||
|
{
|
||
|
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
|
||
|
return YGNodeStyleGetPosition(_yogaNode, edge);
|
||
|
}
|
||
|
|
||
|
- (void)setRight:(YGValue)value
|
||
|
{
|
||
|
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
|
||
|
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
|
||
|
}
|
||
|
- (YGValue)right
|
||
|
{
|
||
|
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
|
||
|
return YGNodeStyleGetPosition(_yogaNode, edge);
|
||
|
}
|
||
|
|
||
|
// Size
|
||
|
|
||
|
- (CGSize)size
|
||
|
{
|
||
|
YGValue width = YGNodeStyleGetWidth(_yogaNode);
|
||
|
YGValue height = YGNodeStyleGetHeight(_yogaNode);
|
||
|
|
||
|
return CGSizeMake(width.unit == YGUnitPoint ? width.value : NAN, height.unit == YGUnitPoint ? height.value : NAN);
|
||
|
}
|
||
|
|
||
|
- (void)setSize:(CGSize)size
|
||
|
{
|
||
|
YGNodeStyleSetWidth(_yogaNode, size.width);
|
||
|
YGNodeStyleSetHeight(_yogaNode, size.height);
|
||
|
}
|
||
|
|
||
|
// IntrinsicContentSize
|
||
|
|
||
|
static inline YGSize
|
||
|
RCTShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
|
||
|
{
|
||
|
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
|
||
|
|
||
|
CGSize intrinsicContentSize = shadowView->_intrinsicContentSize;
|
||
|
// Replace `UIViewNoIntrinsicMetric` (which equals `-1`) with zero.
|
||
|
intrinsicContentSize.width = MAX(0, intrinsicContentSize.width);
|
||
|
intrinsicContentSize.height = MAX(0, intrinsicContentSize.height);
|
||
|
|
||
|
YGSize result;
|
||
|
|
||
|
switch (widthMode) {
|
||
|
case YGMeasureModeUndefined:
|
||
|
result.width = intrinsicContentSize.width;
|
||
|
break;
|
||
|
case YGMeasureModeExactly:
|
||
|
result.width = width;
|
||
|
break;
|
||
|
case YGMeasureModeAtMost:
|
||
|
result.width = MIN(width, intrinsicContentSize.width);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (heightMode) {
|
||
|
case YGMeasureModeUndefined:
|
||
|
result.height = intrinsicContentSize.height;
|
||
|
break;
|
||
|
case YGMeasureModeExactly:
|
||
|
result.height = height;
|
||
|
break;
|
||
|
case YGMeasureModeAtMost:
|
||
|
result.height = MIN(height, intrinsicContentSize.height);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
- (void)setIntrinsicContentSize:(CGSize)intrinsicContentSize
|
||
|
{
|
||
|
if (CGSizeEqualToSize(_intrinsicContentSize, intrinsicContentSize)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_intrinsicContentSize = intrinsicContentSize;
|
||
|
|
||
|
if (CGSizeEqualToSize(_intrinsicContentSize, CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric))) {
|
||
|
YGNodeSetMeasureFunc(_yogaNode, NULL);
|
||
|
} else {
|
||
|
YGNodeSetMeasureFunc(_yogaNode, RCTShadowViewMeasure);
|
||
|
}
|
||
|
|
||
|
YGNodeMarkDirty(_yogaNode);
|
||
|
}
|
||
|
|
||
|
// Local Data
|
||
|
|
||
|
- (void)setLocalData:(__unused NSObject *)localData
|
||
|
{
|
||
|
// Do nothing by default.
|
||
|
}
|
||
|
|
||
|
// Flex
|
||
|
|
||
|
- (void)setFlexBasis:(YGValue)value
|
||
|
{
|
||
|
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSetFlexBasis, _yogaNode);
|
||
|
}
|
||
|
|
||
|
- (YGValue)flexBasis
|
||
|
{
|
||
|
return YGNodeStyleGetFlexBasis(_yogaNode);
|
||
|
}
|
||
|
|
||
|
#define RCT_GAP_PROPERTY(setProp, getProp, cssProp, type, gap) \
|
||
|
-(void)set##setProp : (type)value \
|
||
|
{ \
|
||
|
YGNodeStyleSet##cssProp(_yogaNode, gap, value); \
|
||
|
} \
|
||
|
-(type)getProp \
|
||
|
{ \
|
||
|
return YGNodeStyleGet##cssProp(_yogaNode, gap); \
|
||
|
}
|
||
|
|
||
|
RCT_GAP_PROPERTY(RowGap, rowGap, Gap, float, YGGutterRow);
|
||
|
RCT_GAP_PROPERTY(ColumnGap, columnGap, Gap, float, YGGutterColumn);
|
||
|
RCT_GAP_PROPERTY(Gap, gap, Gap, float, YGGutterAll);
|
||
|
|
||
|
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
|
||
|
-(void)set##setProp : (type)value \
|
||
|
{ \
|
||
|
YGNodeStyleSet##cssProp(_yogaNode, value); \
|
||
|
} \
|
||
|
-(type)getProp \
|
||
|
{ \
|
||
|
return YGNodeStyleGet##cssProp(_yogaNode); \
|
||
|
}
|
||
|
|
||
|
RCT_STYLE_PROPERTY(Flex, flex, Flex, float)
|
||
|
RCT_STYLE_PROPERTY(FlexGrow, flexGrow, FlexGrow, float)
|
||
|
RCT_STYLE_PROPERTY(FlexShrink, flexShrink, FlexShrink, float)
|
||
|
RCT_STYLE_PROPERTY(FlexDirection, flexDirection, FlexDirection, YGFlexDirection)
|
||
|
RCT_STYLE_PROPERTY(JustifyContent, justifyContent, JustifyContent, YGJustify)
|
||
|
RCT_STYLE_PROPERTY(AlignSelf, alignSelf, AlignSelf, YGAlign)
|
||
|
RCT_STYLE_PROPERTY(AlignItems, alignItems, AlignItems, YGAlign)
|
||
|
RCT_STYLE_PROPERTY(AlignContent, alignContent, AlignContent, YGAlign)
|
||
|
RCT_STYLE_PROPERTY(Position, position, PositionType, YGPositionType)
|
||
|
RCT_STYLE_PROPERTY(FlexWrap, flexWrap, FlexWrap, YGWrap)
|
||
|
RCT_STYLE_PROPERTY(Overflow, overflow, Overflow, YGOverflow)
|
||
|
RCT_STYLE_PROPERTY(Display, display, Display, YGDisplay)
|
||
|
RCT_STYLE_PROPERTY(Direction, direction, Direction, YGDirection)
|
||
|
RCT_STYLE_PROPERTY(AspectRatio, aspectRatio, AspectRatio, float)
|
||
|
|
||
|
- (void)didUpdateReactSubviews
|
||
|
{
|
||
|
// Does nothing by default
|
||
|
}
|
||
|
|
||
|
- (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
|
||
|
{
|
||
|
if (_recomputePadding) {
|
||
|
RCTProcessMetaPropsPadding(_paddingMetaProps, _yogaNode);
|
||
|
}
|
||
|
if (_recomputeMargin) {
|
||
|
RCTProcessMetaPropsMargin(_marginMetaProps, _yogaNode);
|
||
|
}
|
||
|
if (_recomputeBorder) {
|
||
|
RCTProcessMetaPropsBorder(_borderMetaProps, _yogaNode);
|
||
|
}
|
||
|
_recomputeMargin = NO;
|
||
|
_recomputePadding = NO;
|
||
|
_recomputeBorder = NO;
|
||
|
}
|
||
|
|
||
|
@end
|