diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m index a966f9022..0d586cc16 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m @@ -28,10 +28,10 @@ { [super setUp]; - self.parentView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flexDirection = CSSFlexDirectionColumn; - style->dimensions[0] = 440; - style->dimensions[1] = 440; + self.parentView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlexDirection(node, CSSFlexDirectionColumn); + CSSNodeStyleSetWidth(node, 440); + CSSNodeStyleSetHeight(node, 440); }]; self.parentView.reactTag = @1; // must be valid rootView tag } @@ -50,43 +50,43 @@ // - (void)testApplyingLayoutRecursivelyToShadowView { - RCTShadowView *leftView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flex = 1; + RCTShadowView *leftView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlex(node, 1); }]; - RCTShadowView *centerView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flex = 2; - style->margin[0] = 10; - style->margin[2] = 10; + RCTShadowView *centerView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlex(node, 2); + CSSNodeStyleSetMarginLeft(node, 10); + CSSNodeStyleSetMarginRight(node, 10); }]; - RCTShadowView *rightView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flex = 1; + RCTShadowView *rightView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlex(node, 1); }]; - RCTShadowView *mainView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flexDirection = CSSFlexDirectionRow; - style->flex = 2; - style->margin[1] = 10; - style->margin[3] = 10; + RCTShadowView *mainView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlexDirection(node, CSSFlexDirectionRow); + CSSNodeStyleSetFlex(node, 2); + CSSNodeStyleSetMarginTop(node, 10); + CSSNodeStyleSetMarginBottom(node, 10); }]; [mainView insertReactSubview:leftView atIndex:0]; [mainView insertReactSubview:centerView atIndex:1]; [mainView insertReactSubview:rightView atIndex:2]; - RCTShadowView *headerView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flex = 1; + RCTShadowView *headerView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlex(node, 1); }]; - RCTShadowView *footerView = [self _shadowViewWithStyle:^(CSSStyle *style) { - style->flex = 1; + RCTShadowView *footerView = [self _shadowViewWithConfig:^(CSSNodeRef node) { + CSSNodeStyleSetFlex(node, 1); }]; - self.parentView.cssNode->style.padding[0] = 10; - self.parentView.cssNode->style.padding[1] = 10; - self.parentView.cssNode->style.padding[2] = 10; - self.parentView.cssNode->style.padding[3] = 10; + CSSNodeStyleSetPaddingLeft(self.parentView.cssNode, 10); + CSSNodeStyleSetPaddingTop(self.parentView.cssNode, 10); + CSSNodeStyleSetPaddingRight(self.parentView.cssNode, 10); + CSSNodeStyleSetPaddingBottom(self.parentView.cssNode, 10); [self.parentView insertReactSubview:headerView atIndex:0]; [self.parentView insertReactSubview:mainView atIndex:1]; @@ -108,10 +108,10 @@ - (void)testAssignsSuggestedWidthDimension { - [self _withShadowViewWithStyle:^(CSSStyle *style) { - style->position[CSSPositionLeft] = 0; - style->position[CSSPositionTop] = 0; - style->dimensions[CSSDimensionHeight] = 10; + [self _withShadowViewWithStyle:^(CSSNodeRef node) { + CSSNodeStyleSetPositionLeft(node, 0); + CSSNodeStyleSetPositionTop(node, 0); + CSSNodeStyleSetHeight(node, 10); } assertRelativeLayout:CGRectMake(0, 0, 3, 10) withIntrinsicContentSize:CGSizeMake(3, UIViewNoIntrinsicMetric)]; @@ -119,10 +119,10 @@ - (void)testAssignsSuggestedHeightDimension { - [self _withShadowViewWithStyle:^(CSSStyle *style) { - style->position[CSSPositionLeft] = 0; - style->position[CSSPositionTop] = 0; - style->dimensions[CSSDimensionWidth] = 10; + [self _withShadowViewWithStyle:^(CSSNodeRef node) { + CSSNodeStyleSetPositionLeft(node, 0); + CSSNodeStyleSetPositionTop(node, 0); + CSSNodeStyleSetWidth(node, 10); } assertRelativeLayout:CGRectMake(0, 0, 10, 4) withIntrinsicContentSize:CGSizeMake(UIViewNoIntrinsicMetric, 4)]; @@ -130,11 +130,11 @@ - (void)testDoesNotOverrideDimensionStyleWithSuggestedDimensions { - [self _withShadowViewWithStyle:^(CSSStyle *style) { - style->position[CSSPositionLeft] = 0; - style->position[CSSPositionTop] = 0; - style->dimensions[CSSDimensionWidth] = 10; - style->dimensions[CSSDimensionHeight] = 10; + [self _withShadowViewWithStyle:^(CSSNodeRef node) { + CSSNodeStyleSetPositionLeft(node, 0); + CSSNodeStyleSetPositionTop(node, 0); + CSSNodeStyleSetWidth(node, 10); + CSSNodeStyleSetHeight(node, 10); } assertRelativeLayout:CGRectMake(0, 0, 10, 10) withIntrinsicContentSize:CGSizeMake(3, 4)]; @@ -142,20 +142,20 @@ - (void)testDoesNotAssignSuggestedDimensionsWhenStyledWithFlexAttribute { - float parentWidth = self.parentView.cssNode->style.dimensions[CSSDimensionWidth]; - float parentHeight = self.parentView.cssNode->style.dimensions[CSSDimensionHeight]; - [self _withShadowViewWithStyle:^(CSSStyle *style) { - style->flex = 1; + float parentWidth = CSSNodeStyleGetWidth(self.parentView.cssNode); + float parentHeight = CSSNodeStyleGetHeight(self.parentView.cssNode); + [self _withShadowViewWithStyle:^(CSSNodeRef node) { + CSSNodeStyleSetFlex(node, 1); } assertRelativeLayout:CGRectMake(0, 0, parentWidth, parentHeight) withIntrinsicContentSize:CGSizeMake(3, 4)]; } -- (void)_withShadowViewWithStyle:(void(^)(CSSStyle *style))styleBlock +- (void)_withShadowViewWithStyle:(void(^)(CSSNodeRef node))configBlock assertRelativeLayout:(CGRect)expectedRect withIntrinsicContentSize:(CGSize)contentSize { - RCTShadowView *view = [self _shadowViewWithStyle:styleBlock]; + RCTShadowView *view = [self _shadowViewWithConfig:configBlock]; [self.parentView insertReactSubview:view atIndex:0]; view.intrinsicContentSize = contentSize; [self.parentView collectViewsWithUpdatedFrames]; @@ -166,14 +166,10 @@ NSStringFromCGRect(actualRect)); } -- (RCTRootShadowView *)_shadowViewWithStyle:(void(^)(CSSStyle *style))styleBlock +- (RCTRootShadowView *)_shadowViewWithConfig:(void(^)(CSSNodeRef node))configBlock { RCTRootShadowView *shadowView = [RCTRootShadowView new]; - - CSSStyle style = shadowView.cssNode->style; - styleBlock(&style); - shadowView.cssNode->style = style; - + configBlock(shadowView.cssNode); return shadowView; } diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m index bbff4ac1b..4243dbff7 100644 --- a/Libraries/Text/RCTShadowText.m +++ b/Libraries/Text/RCTShadowText.m @@ -33,7 +33,7 @@ NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName"; CGFloat _effectiveLetterSpacing; } -static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode) +static CSSSize RCTMeasure(void *context, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode) { RCTShadowText *shadowText = (__bridge RCTShadowText *)context; NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:width widthMode:widthMode]; @@ -41,12 +41,12 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi NSTextContainer *textContainer = layoutManager.textContainers.firstObject; CGSize computedSize = [layoutManager usedRectForTextContainer:textContainer].size; - CSSMeasureResult result; - result.dimensions[CSSDimensionWidth] = RCTCeilPixelValue(computedSize.width); + CSSSize result; + result.width = RCTCeilPixelValue(computedSize.width); if (shadowText->_effectiveLetterSpacing < 0) { - result.dimensions[CSSDimensionWidth] -= shadowText->_effectiveLetterSpacing; + result.width -= shadowText->_effectiveLetterSpacing; } - result.dimensions[CSSDimensionHeight] = RCTCeilPixelValue(computedSize.height); + result.height = RCTCeilPixelValue(computedSize.height); return result; } @@ -61,7 +61,7 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi _cachedTextStorageWidth = -1; _cachedTextStorageWidthMode = -1; _fontSizeMultiplier = 1.0; - self.cssNode->measure = RCTMeasure; + CSSNodeSetMeasureFunc(self.cssNode, RCTMeasure); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentSizeMultiplierDidChange:) name:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification @@ -128,7 +128,7 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi return parentProperties; } -- (void)applyLayoutNode:(CSSNode *)node +- (void)applyLayoutNode:(CSSNodeRef)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition { @@ -136,7 +136,7 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi [self dirtyPropagation]; } -- (void)applyLayoutToChildren:(CSSNode *)node +- (void)applyLayoutToChildren:(CSSNodeRef)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition { @@ -148,9 +148,9 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; [layoutManager.textStorage enumerateAttribute:RCTShadowViewAttributeName inRange:characterRange options:0 usingBlock:^(RCTShadowView *child, NSRange range, BOOL *_) { if (child) { - CSSNode *childNode = child.cssNode; - float width = childNode->style.dimensions[CSSDimensionWidth]; - float height = childNode->style.dimensions[CSSDimensionHeight]; + CSSNodeRef childNode = child.cssNode; + float width = CSSNodeStyleGetWidth(childNode); + float height = CSSNodeStyleGetHeight(childNode); if (isUndefined(width) || isUndefined(height)) { RCTLogError(@"Views nested within a must have a width and height"); } @@ -291,8 +291,8 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:shadowRawText.text ?: @""]]; [child setTextComputed]; } else { - float width = child.cssNode->style.dimensions[CSSDimensionWidth]; - float height = child.cssNode->style.dimensions[CSSDimensionHeight]; + float width = CSSNodeStyleGetWidth(child.cssNode); + float height = CSSNodeStyleGetHeight(child.cssNode); if (isUndefined(width) || isUndefined(height)) { RCTLogError(@"Views nested within a must have a width and height"); } @@ -384,10 +384,10 @@ static CSSMeasureResult RCTMeasure(void *context, float width, CSSMeasureMode wi // We will climb up to the first node which style has been setted as non-inherit if (newTextAlign == NSTextAlignmentRight || newTextAlign == NSTextAlignmentLeft) { RCTShadowView *view = self; - while (view != nil && view.cssNode->style.direction == CSSDirectionInherit) { + while (view != nil && CSSNodeStyleGetDirection(view.cssNode) == CSSDirectionInherit) { view = [view reactSuperview]; } - if (view != nil && view.cssNode->style.direction == CSSDirectionRTL) { + if (view != nil && CSSNodeStyleGetDirection(view.cssNode) == CSSDirectionRTL) { if (newTextAlign == NSTextAlignmentRight) { newTextAlign = NSTextAlignmentLeft; } else if (newTextAlign == NSTextAlignmentLeft) { diff --git a/React/CSSLayout/CSSLayout-internal.h b/React/CSSLayout/CSSLayout-internal.h new file mode 100644 index 000000000..68e6d3d30 --- /dev/null +++ b/React/CSSLayout/CSSLayout-internal.h @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef __CSS_LAYOUT_INTERNAL_H +#define __CSS_LAYOUT_INTERNAL_H + +#include +#include + +#include "CSSLayout.h" + +CSS_EXTERN_C_BEGIN + +typedef struct CSSCachedMeasurement { + float availableWidth; + float availableHeight; + CSSMeasureMode widthMeasureMode; + CSSMeasureMode heightMeasureMode; + + float computedWidth; + float computedHeight; +} CSSCachedMeasurement; + +// This value was chosen based on empiracle data. Even the most complicated +// layouts should not require more than 16 entries to fit within the cache. +enum { + CSS_MAX_CACHED_RESULT_COUNT = 16 +}; + +typedef struct CSSLayout { + float position[4]; + float dimensions[2]; + CSSDirection direction; + + float flexBasis; + + // Instead of recomputing the entire layout every single time, we + // cache some information to break early when nothing changed + int generationCount; + CSSDirection lastParentDirection; + + int nextCachedMeasurementsIndex; + CSSCachedMeasurement cachedMeasurements[CSS_MAX_CACHED_RESULT_COUNT]; + float measuredDimensions[2]; + + CSSCachedMeasurement cached_layout; +} CSSLayout; + +typedef struct CSSStyle { + CSSDirection direction; + CSSFlexDirection flexDirection; + CSSJustify justifyContent; + CSSAlign alignContent; + CSSAlign alignItems; + CSSAlign alignSelf; + CSSPositionType positionType; + CSSWrapType flexWrap; + CSSOverflow overflow; + float flex; + float margin[6]; + float position[4]; + /** + * You should skip all the rules that contain negative values for the + * following attributes. For example: + * {padding: 10, paddingLeft: -5} + * should output: + * {left: 10 ...} + * the following two are incorrect: + * {left: -5 ...} + * {left: 0 ...} + */ + float padding[6]; + float border[6]; + float dimensions[2]; + float minDimensions[2]; + float maxDimensions[2]; +} CSSStyle; + +typedef struct CSSNode { + CSSStyle style; + CSSLayout layout; + int childCount; + int lineIndex; + bool shouldUpdate; + + struct CSSNode* nextChild; + + CSSSize (*measure)(void *context, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode); + struct CSSNode* (*getChild)(void *context, int i); + bool (*isDirty)(void *context); + bool (*isTextNode)(void *context); + void (*print)(void *context); + void *context; +} CSSNode; + +CSS_EXTERN_C_END + +#endif diff --git a/React/CSSLayout/CSSLayout.c b/React/CSSLayout/CSSLayout.c index 6db70b40e..6eab4f3e5 100644 --- a/React/CSSLayout/CSSLayout.c +++ b/React/CSSLayout/CSSLayout.c @@ -13,7 +13,7 @@ #include #include -#include "CSSLayout.h" +#include "CSSLayout-internal.h" #ifdef _MSC_VER #include @@ -29,23 +29,17 @@ __forceinline const float fmaxf(const float a, const float b) { #define POSITIVE_FLEX_IS_AUTO 0 -int gCurrentGenerationCount = 0; - -bool layoutNodeInternal(CSSNode* node, float availableWidth, float availableHeight, CSSDirection parentDirection, - CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, bool performLayout, char* reason); - -bool isUndefined(float value) { - return isnan(value); +CSSNodeRef CSSNodeNew() { + CSSNode* node = (CSSNode*)calloc(1, sizeof(*node)); + CSSNodeInit(node); + return node; } -static bool eq(float a, float b) { - if (isUndefined(a)) { - return isUndefined(b); - } - return fabs(a - b) < 0.0001; +void CSSNodeFree(CSSNodeRef node) { + free(node); } -void CSSNodeInit(CSSNode* node) { +void CSSNodeInit(CSSNodeRef node) { node->style.alignItems = CSSAlignStretch; node->style.alignContent = CSSAlignFlexStart; @@ -81,7 +75,7 @@ void CSSNodeInit(CSSNode* node) { // Such that the comparison is always going to be false node->layout.lastParentDirection = (CSSDirection)-1; - node->layout.shouldUpdate = true; + node->shouldUpdate = true; node->layout.nextCachedMeasurementsIndex = 0; node->layout.measuredDimensions[CSSDimensionWidth] = CSSUndefined; @@ -90,14 +84,103 @@ void CSSNodeInit(CSSNode* node) { node->layout.cached_layout.heightMeasureMode = (CSSMeasureMode)-1; } -CSSNode* CSSNodeNew() { - CSSNode* node = (CSSNode*)calloc(1, sizeof(*node)); - CSSNodeInit(node); - return node; +#define CSS_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \ +void CSSNodeSet##name(CSSNodeRef node, type paramName) { \ + node->instanceName = paramName;\ +} \ +\ +type CSSNodeGet##name(CSSNodeRef node) { \ + return node->instanceName;\ +} \ + +#define CSS_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \ +void CSSNodeStyleSet##name(CSSNodeRef node, type paramName) { \ + node->style.instanceName = paramName;\ +} \ +\ +type CSSNodeStyleGet##name(CSSNodeRef node) { \ + return node->style.instanceName;\ +} \ + +#define CSS_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \ +type CSSNodeLayoutGet##name(CSSNodeRef node) { \ + return node->layout.instanceName;\ +} \ + +CSS_NODE_PROPERTY_IMPL(void*, Context, context, context); +CSS_NODE_PROPERTY_IMPL(int, ChildCount, childCount, childCount); +CSS_NODE_PROPERTY_IMPL(CSSMeasureFunc, MeasureFunc, measureFunc, measure); +CSS_NODE_PROPERTY_IMPL(CSSChildFunc, ChildFunc, childFunc, getChild); +CSS_NODE_PROPERTY_IMPL(CSSIsDirtyFunc, IsDirtyFunc, isDirtyFunc, isDirty); +CSS_NODE_PROPERTY_IMPL(CSSIsTextFunc, IsTextFunc, isTextFunc, isTextNode); +CSS_NODE_PROPERTY_IMPL(CSSPrintFunc, PrintFunc, printFunc, print); +CSS_NODE_PROPERTY_IMPL(bool, ShouldUpdate, shouldUpdate, shouldUpdate); + +CSS_NODE_STYLE_PROPERTY_IMPL(CSSDirection, Direction, direction, direction); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSFlexDirection, FlexDirection, flexDirection, flexDirection); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSJustify, JustifyContent, justifyContent, justifyContent); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSAlign, AlignContent, alignContent, alignContent); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSAlign, AlignItems, alignItems, alignItems); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSAlign, AlignSelf, alignSelf, alignSelf); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSPositionType, PositionType, positionType, positionType); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSWrapType, FlexWrap, flexWrap, flexWrap); +CSS_NODE_STYLE_PROPERTY_IMPL(CSSOverflow, Overflow, overflow, overflow); +CSS_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex); + +CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionLeft, positionLeft, position[CSSPositionLeft]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionTop, positionTop, position[CSSPositionTop]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionRight, positionRight, position[CSSPositionRight]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionBottom, positionBottom, position[CSSPositionBottom]); + +CSS_NODE_STYLE_PROPERTY_IMPL(float, MarginLeft, marginLeft, margin[CSSPositionLeft]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MarginTop, marginTop, margin[CSSPositionTop]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MarginRight, marginRight, margin[CSSPositionRight]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MarginBottom, marginBottom, margin[CSSPositionBottom]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MarginStart, marginStart, margin[CSSPositionStart]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MarginEnd, marginEnd, margin[CSSPositionEnd]); + +CSS_NODE_STYLE_PROPERTY_IMPL(float, PaddingLeft, paddingLeft, padding[CSSPositionLeft]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PaddingTop, paddingTop, padding[CSSPositionTop]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PaddingRight, paddingRight, padding[CSSPositionRight]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PaddingBottom, paddingBottom, padding[CSSPositionBottom]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PaddingStart, paddingStart, padding[CSSPositionStart]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, PaddingEnd, paddingEnd, padding[CSSPositionEnd]); + +CSS_NODE_STYLE_PROPERTY_IMPL(float, BorderLeft, borderLeft, border[CSSPositionLeft]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, BorderTop, borderTop, border[CSSPositionTop]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, BorderRight, borderRight, border[CSSPositionRight]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, BorderBottom, borderBottom, border[CSSPositionBottom]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, BorderStart, borderStart, border[CSSPositionStart]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, BorderEnd, BorderEnd, border[CSSPositionEnd]); + +CSS_NODE_STYLE_PROPERTY_IMPL(float, Width, width, dimensions[CSSDimensionWidth]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, Height, height, dimensions[CSSDimensionHeight]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MinWidth, minWidth, minDimensions[CSSDimensionWidth]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MinHeight, minHeight, minDimensions[CSSDimensionHeight]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MaxWidth, maxWidth, maxDimensions[CSSDimensionWidth]); +CSS_NODE_STYLE_PROPERTY_IMPL(float, MaxHeight, maxHeight, maxDimensions[CSSDimensionHeight]); + +CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[CSSPositionLeft]); +CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[CSSPositionTop]); +CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[CSSPositionRight]); +CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[CSSPositionBottom]); +CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[CSSDimensionWidth]); +CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[CSSDimensionHeight]); + +int gCurrentGenerationCount = 0; + +bool layoutNodeInternal(CSSNode* node, float availableWidth, float availableHeight, CSSDirection parentDirection, + CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, bool performLayout, char* reason); + +bool isUndefined(float value) { + return isnan(value); } -void CSSNodeFree(CSSNode* node) { - free(node); +static bool eq(float a, float b) { + if (isUndefined(a)) { + return isUndefined(b); + } + return fabs(a - b) < 0.0001; } static void indent(int n) { @@ -682,7 +765,7 @@ static void layoutNodeImpl(CSSNode* node, float availableWidth, float availableH } else { // Measure the text under the current constraints. - CSSMeasureResult measureDim = node->measure( + CSSSize measuredSize = node->measure( node->context, innerWidth, @@ -693,11 +776,11 @@ static void layoutNodeImpl(CSSNode* node, float availableWidth, float availableH node->layout.measuredDimensions[CSSDimensionWidth] = boundAxis(node, CSSFlexDirectionRow, (widthMeasureMode == CSSMeasureModeUndefined || widthMeasureMode == CSSMeasureModeAtMost) ? - measureDim.dimensions[CSSDimensionWidth] + paddingAndBorderAxisRow : + measuredSize.width + paddingAndBorderAxisRow : availableWidth - marginAxisRow); node->layout.measuredDimensions[CSSDimensionHeight] = boundAxis(node, CSSFlexDirectionColumn, (heightMeasureMode == CSSMeasureModeUndefined || heightMeasureMode == CSSMeasureModeAtMost) ? - measureDim.dimensions[CSSDimensionHeight] + paddingAndBorderAxisColumn : + measuredSize.height + paddingAndBorderAxisColumn : availableHeight - marginAxisColumn); } @@ -1764,7 +1847,7 @@ bool layoutNodeInternal(CSSNode* node, float availableWidth, float availableHeig if (performLayout) { node->layout.dimensions[CSSDimensionWidth] = node->layout.measuredDimensions[CSSDimensionWidth]; node->layout.dimensions[CSSDimensionHeight] = node->layout.measuredDimensions[CSSDimensionHeight]; - layout->shouldUpdate = true; + node->shouldUpdate = true; } gDepth--; @@ -1772,7 +1855,7 @@ bool layoutNodeInternal(CSSNode* node, float availableWidth, float availableHeig return (needToVisitNode || cachedResults == NULL); } -void layoutNode(CSSNode* node, float availableWidth, float availableHeight, CSSDirection parentDirection) { +void CSSNodeCalculateLayout(CSSNode* node, float availableWidth, float availableHeight, CSSDirection parentDirection) { // Increment the generation count. This will force the recursive routine to visit // all dirty nodes at least once. Subsequent visits will be skipped if the input // parameters don't change. diff --git a/React/CSSLayout/CSSLayout.h b/React/CSSLayout/CSSLayout.h index f85c72970..99d792a94 100644 --- a/React/CSSLayout/CSSLayout.h +++ b/React/CSSLayout/CSSLayout.h @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef __LAYOUT_H -#define __LAYOUT_H +#ifndef __CSS_LAYOUT_H +#define __CSS_LAYOUT_H #include #ifndef __cplusplus @@ -97,110 +97,110 @@ typedef enum CSSDimension { CSSDimensionHeight, } CSSDimension; -typedef struct CSSCachedMeasurement { - float availableWidth; - float availableHeight; - CSSMeasureMode widthMeasureMode; - CSSMeasureMode heightMeasureMode; - - float computedWidth; - float computedHeight; -} CSSCachedMeasurement; - -// This value was chosen based on empiracle data. Even the most complicated -// layouts should not require more than 16 entries to fit within the cache. -enum { - CSS_MAX_CACHED_RESULT_COUNT = 16 -}; - -typedef struct CSSLayout { - float position[4]; - float dimensions[2]; - CSSDirection direction; - - float flexBasis; - - // Instead of recomputing the entire layout every single time, we - // cache some information to break early when nothing changed - bool shouldUpdate; - int generationCount; - CSSDirection lastParentDirection; - - int nextCachedMeasurementsIndex; - CSSCachedMeasurement cachedMeasurements[CSS_MAX_CACHED_RESULT_COUNT]; - float measuredDimensions[2]; - - CSSCachedMeasurement cached_layout; -} CSSLayout; - -typedef struct CSSMeasureResult { - float dimensions[2]; -} CSSMeasureResult; - -typedef struct CSSStyle { - CSSDirection direction; - CSSFlexDirection flexDirection; - CSSJustify justifyContent; - CSSAlign alignContent; - CSSAlign alignItems; - CSSAlign alignSelf; - CSSPositionType positionType; - CSSWrapType flexWrap; - CSSOverflow overflow; - float flex; - float margin[6]; - float position[4]; - /** - * You should skip all the rules that contain negative values for the - * following attributes. For example: - * {padding: 10, paddingLeft: -5} - * should output: - * {left: 10 ...} - * the following two are incorrect: - * {left: -5 ...} - * {left: 0 ...} - */ - float padding[6]; - float border[6]; - float dimensions[2]; - float minDimensions[2]; - float maxDimensions[2]; -} CSSStyle; - -typedef struct CSSNode { - CSSStyle style; - CSSLayout layout; - int childCount; - int lineIndex; - - struct CSSNode* nextChild; - - CSSMeasureResult (*measure)(void *context, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode); - void (*print)(void *context); - struct CSSNode* (*getChild)(void *context, int i); - bool (*isDirty)(void *context); - bool (*isTextNode)(void *context); - void *context; -} CSSNode; - -// Lifecycle of nodes and children -CSSNode *CSSNodeNew(); -void CSSNodeInit(CSSNode *node); -void CSSNodeFree(CSSNode *node); - -// Print utilities typedef enum CSSPrintOptions { CSSPrintOptionsLayout = 1, CSSPrintOptionsStyle = 2, CSSPrintOptionsChildren = 4, } CSSPrintOptions; -void CSSNodePrint(CSSNode *node, CSSPrintOptions options); +typedef struct CSSSize { + float width; + float height; +} CSSSize; + +typedef struct CSSNode * CSSNodeRef; +typedef CSSSize (*CSSMeasureFunc)(void *context, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode); +typedef CSSNodeRef (*CSSChildFunc)(void *context, int i); +typedef bool (*CSSIsDirtyFunc)(void *context); +typedef bool (*CSSIsTextFunc)(void *context); +typedef void (*CSSPrintFunc)(void *context); + +// CSSNode +CSSNodeRef CSSNodeNew(); +void CSSNodeInit(CSSNodeRef node); +void CSSNodeFree(CSSNodeRef node); + +void CSSNodeCalculateLayout( + CSSNodeRef node, + float availableWidth, + float availableHeight, + CSSDirection parentDirection); + +void CSSNodePrint(CSSNodeRef node, CSSPrintOptions options); -// Function that computes the layout! -void layoutNode(CSSNode *node, float availableWidth, float availableHeight, CSSDirection parentDirection); bool isUndefined(float value); +#define CSS_NODE_PROPERTY(type, name, paramName) \ +void CSSNodeSet##name(CSSNodeRef node, type paramName); \ +type CSSNodeGet##name(CSSNodeRef node); + +#define CSS_NODE_STYLE_PROPERTY(type, name, paramName) \ +void CSSNodeStyleSet##name(CSSNodeRef node, type paramName); \ +type CSSNodeStyleGet##name(CSSNodeRef node); + +#define CSS_NODE_LAYOUT_PROPERTY(type, name) \ +type CSSNodeLayoutGet##name(CSSNodeRef node); + +CSS_NODE_PROPERTY(void*, Context, context); +CSS_NODE_PROPERTY(int, ChildCount, childCount); +CSS_NODE_PROPERTY(CSSMeasureFunc, MeasureFunc, measureFunc); +CSS_NODE_PROPERTY(CSSChildFunc, ChildFunc, childFunc); +CSS_NODE_PROPERTY(CSSIsDirtyFunc, IsDirtyFunc, isDirtyFunc); +CSS_NODE_PROPERTY(CSSIsTextFunc, IsTextFunc, isTextFunc); +CSS_NODE_PROPERTY(CSSPrintFunc, PrintFunc, printFunc); +CSS_NODE_PROPERTY(bool, ShouldUpdate, shouldUpdate); + +CSS_NODE_STYLE_PROPERTY(CSSDirection, Direction, direction); +CSS_NODE_STYLE_PROPERTY(CSSFlexDirection, FlexDirection, flexDirection); +CSS_NODE_STYLE_PROPERTY(CSSJustify, JustifyContent, justifyContent); +CSS_NODE_STYLE_PROPERTY(CSSAlign, AlignContent, alignContent); +CSS_NODE_STYLE_PROPERTY(CSSAlign, AlignItems, alignItems); +CSS_NODE_STYLE_PROPERTY(CSSAlign, AlignSelf, alignSelf); +CSS_NODE_STYLE_PROPERTY(CSSPositionType, PositionType, positionType); +CSS_NODE_STYLE_PROPERTY(CSSWrapType, FlexWrap, flexWrap); +CSS_NODE_STYLE_PROPERTY(CSSOverflow, Overflow, overflow); +CSS_NODE_STYLE_PROPERTY(float, Flex, flex); + +CSS_NODE_STYLE_PROPERTY(float, PositionLeft, positionLeft); +CSS_NODE_STYLE_PROPERTY(float, PositionTop, positionTop); +CSS_NODE_STYLE_PROPERTY(float, PositionRight, positionRight); +CSS_NODE_STYLE_PROPERTY(float, PositionBottom, positionBottom); + +CSS_NODE_STYLE_PROPERTY(float, MarginLeft, marginLeft); +CSS_NODE_STYLE_PROPERTY(float, MarginTop, marginTop); +CSS_NODE_STYLE_PROPERTY(float, MarginRight, marginRight); +CSS_NODE_STYLE_PROPERTY(float, MarginBottom, marginBottom); +CSS_NODE_STYLE_PROPERTY(float, MarginStart, marginStart); +CSS_NODE_STYLE_PROPERTY(float, MarginEnd, marginEnd); + +CSS_NODE_STYLE_PROPERTY(float, PaddingLeft, paddingLeft); +CSS_NODE_STYLE_PROPERTY(float, PaddingTop, paddingTop); +CSS_NODE_STYLE_PROPERTY(float, PaddingRight, paddingRight); +CSS_NODE_STYLE_PROPERTY(float, PaddingBottom, paddingBottom); +CSS_NODE_STYLE_PROPERTY(float, PaddingStart, paddingStart); +CSS_NODE_STYLE_PROPERTY(float, PaddingEnd, paddingEnd); + +CSS_NODE_STYLE_PROPERTY(float, BorderLeft, borderLeft); +CSS_NODE_STYLE_PROPERTY(float, BorderTop, borderTop); +CSS_NODE_STYLE_PROPERTY(float, BorderRight, borderRight); +CSS_NODE_STYLE_PROPERTY(float, BorderBottom, borderBottom); +CSS_NODE_STYLE_PROPERTY(float, BorderStart, borderStart); +CSS_NODE_STYLE_PROPERTY(float, BorderEnd, borderEnd); + +CSS_NODE_STYLE_PROPERTY(float, Width, width); +CSS_NODE_STYLE_PROPERTY(float, Height, height); +CSS_NODE_STYLE_PROPERTY(float, MinWidth, minWidth); +CSS_NODE_STYLE_PROPERTY(float, MinHeight, minHeight); +CSS_NODE_STYLE_PROPERTY(float, MaxWidth, maxWidth); +CSS_NODE_STYLE_PROPERTY(float, MaxHeight, maxHeight); + +CSS_NODE_LAYOUT_PROPERTY(float, Left); +CSS_NODE_LAYOUT_PROPERTY(float, Top); +CSS_NODE_LAYOUT_PROPERTY(float, Right); +CSS_NODE_LAYOUT_PROPERTY(float, Bottom); +CSS_NODE_LAYOUT_PROPERTY(float, Width); +CSS_NODE_LAYOUT_PROPERTY(float, Height); + CSS_EXTERN_C_END #endif diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 5c19887e0..cf33c300a 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -121,6 +121,7 @@ 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; }; 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSStackFrame.m; sourceTree = ""; }; 008341F51D1DB34400876D9A /* RCTJSStackFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSStackFrame.h; sourceTree = ""; }; + 131541CF1D3E4893006A0E08 /* CSSLayout-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSSLayout-internal.h"; sourceTree = ""; }; 131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControl.h; sourceTree = ""; }; 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControl.m; sourceTree = ""; }; 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControlManager.h; sourceTree = ""; }; @@ -342,6 +343,7 @@ 133683431D37ACA10077D0C3 /* CSSLayout */ = { isa = PBXGroup; children = ( + 131541CF1D3E4893006A0E08 /* CSSLayout-internal.h */, 133683441D37ACA10077D0C3 /* CSSLayout.c */, 133683451D37ACA10077D0C3 /* CSSLayout.h */, 133683481D37ACA10077D0C3 /* CSSMacros.h */, diff --git a/React/Views/RCTRootShadowView.m b/React/Views/RCTRootShadowView.m index d6d8cfe27..c3efd53d3 100644 --- a/React/Views/RCTRootShadowView.m +++ b/React/Views/RCTRootShadowView.m @@ -21,7 +21,7 @@ self = [super init]; if (self) { if ([[RCTI18nUtil sharedInstance] isRTL]) { - self.cssNode->style.direction = CSSDirectionRTL; + CSSNodeStyleSetDirection(self.cssNode, CSSDirectionRTL); } } return self; @@ -33,14 +33,14 @@ case RCTRootViewSizeFlexibilityNone: break; case RCTRootViewSizeFlexibilityWidth: - self.cssNode->style.dimensions[CSSDimensionWidth] = CSSUndefined; + CSSNodeStyleSetWidth(self.cssNode, CSSUndefined); break; case RCTRootViewSizeFlexibilityHeight: - self.cssNode->style.dimensions[CSSDimensionHeight] = CSSUndefined; + CSSNodeStyleSetHeight(self.cssNode, CSSUndefined); break; case RCTRootViewSizeFlexibilityWidthAndHeight: - self.cssNode->style.dimensions[CSSDimensionWidth] = CSSUndefined; - self.cssNode->style.dimensions[CSSDimensionHeight] = CSSUndefined; + CSSNodeStyleSetWidth(self.cssNode, CSSUndefined); + CSSNodeStyleSetHeight(self.cssNode, CSSUndefined); break; } } @@ -49,7 +49,7 @@ { [self applySizeConstraints]; - layoutNode(self.cssNode, CSSUndefined, CSSUndefined, CSSDirectionInherit); + CSSNodeCalculateLayout(self.cssNode, CSSUndefined, CSSUndefined, CSSDirectionInherit); NSMutableSet *viewsWithNewFrame = [NSMutableSet set]; [self applyLayoutNode:self.cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero]; diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 454a8e8e4..8547c1ddc 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -44,7 +44,7 @@ typedef void (^RCTApplierBlock)(NSDictionary *viewRegistry - (void)removeReactSubview:(RCTShadowView *)subview NS_REQUIRES_SUPER; @property (nonatomic, weak, readonly) RCTShadowView *superview; -@property (nonatomic, assign, readonly) CSSNode *cssNode; +@property (nonatomic, assign, readonly) CSSNodeRef cssNode; @property (nonatomic, copy) NSString *viewName; @property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children @property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle; @@ -172,14 +172,14 @@ typedef void (^RCTApplierBlock)(NSDictionary *viewRegistry * is split into two methods so subclasses can override `applyLayoutToChildren:` * while using default implementation of `applyLayoutNode:`. */ -- (void)applyLayoutNode:(CSSNode *)node +- (void)applyLayoutNode:(CSSNodeRef)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition NS_REQUIRES_SUPER; /** * Enumerate the child nodes and tell them to apply layout. */ -- (void)applyLayoutToChildren:(CSSNode *)node +- (void)applyLayoutToChildren:(CSSNodeRef)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition; diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index 4dd78a1cb..66a0c07d2 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -56,7 +56,7 @@ static void RCTPrint(void *context) printf("%s(%zd), ", shadowView.viewName.UTF8String, shadowView.reactTag.integerValue); } -static CSSNode *RCTGetChild(void *context, int i) +static CSSNodeRef RCTGetChild(void *context, int i) { RCTShadowView *shadowView = (__bridge RCTShadowView *)context; RCTShadowView *child = [shadowView reactSubviews][i]; @@ -70,25 +70,53 @@ static bool RCTIsDirty(void *context) } // Enforces precedence rules, e.g. marginLeft > marginHorizontal > margin. -static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float style[CSSPositionCount]) { - style[CSSPositionLeft] = !isUndefined(metaProps[META_PROP_LEFT]) ? metaProps[META_PROP_LEFT] - : !isUndefined(metaProps[META_PROP_HORIZONTAL]) ? metaProps[META_PROP_HORIZONTAL] - : !isUndefined(metaProps[META_PROP_ALL]) ? metaProps[META_PROP_ALL] - : 0; - style[CSSPositionRight] = !isUndefined(metaProps[META_PROP_RIGHT]) ? metaProps[META_PROP_RIGHT] - : !isUndefined(metaProps[META_PROP_HORIZONTAL]) ? metaProps[META_PROP_HORIZONTAL] - : !isUndefined(metaProps[META_PROP_ALL]) ? metaProps[META_PROP_ALL] - : 0; - style[CSSPositionTop] = !isUndefined(metaProps[META_PROP_TOP]) ? metaProps[META_PROP_TOP] - : !isUndefined(metaProps[META_PROP_VERTICAL]) ? metaProps[META_PROP_VERTICAL] - : !isUndefined(metaProps[META_PROP_ALL]) ? metaProps[META_PROP_ALL] - : 0; - style[CSSPositionBottom] = !isUndefined(metaProps[META_PROP_BOTTOM]) ? metaProps[META_PROP_BOTTOM] - : !isUndefined(metaProps[META_PROP_VERTICAL]) ? metaProps[META_PROP_VERTICAL] - : !isUndefined(metaProps[META_PROP_ALL]) ? metaProps[META_PROP_ALL] - : 0; +#define DEFINE_PROCESS_META_PROPS(type) \ +static void RCTProcessMetaProps##type(const float metaProps[META_PROP_COUNT], CSSNodeRef node) { \ + if (!isUndefined(metaProps[META_PROP_LEFT])) { \ + CSSNodeStyleSet##type##Left(node, metaProps[META_PROP_LEFT]); \ + } else if (!isUndefined(metaProps[META_PROP_HORIZONTAL])) { \ + CSSNodeStyleSet##type##Left(node, metaProps[META_PROP_HORIZONTAL]); \ + } else if (!isUndefined(metaProps[META_PROP_ALL])) { \ + CSSNodeStyleSet##type##Left(node, metaProps[META_PROP_ALL]); \ + } else { \ + CSSNodeStyleSet##type##Left(node, 0); \ + } \ + \ + if (!isUndefined(metaProps[META_PROP_RIGHT])) { \ + CSSNodeStyleSet##type##Right(node, metaProps[META_PROP_RIGHT]); \ + } else if (!isUndefined(metaProps[META_PROP_HORIZONTAL])) { \ + CSSNodeStyleSet##type##Right(node, metaProps[META_PROP_HORIZONTAL]); \ + } else if (!isUndefined(metaProps[META_PROP_ALL])) { \ + CSSNodeStyleSet##type##Right(node, metaProps[META_PROP_ALL]); \ + } else { \ + CSSNodeStyleSet##type##Right(node, 0); \ + } \ + \ + if (!isUndefined(metaProps[META_PROP_TOP])) { \ + CSSNodeStyleSet##type##Top(node, metaProps[META_PROP_TOP]); \ + } else if (!isUndefined(metaProps[META_PROP_VERTICAL])) { \ + CSSNodeStyleSet##type##Top(node, metaProps[META_PROP_VERTICAL]); \ + } else if (!isUndefined(metaProps[META_PROP_ALL])) { \ + CSSNodeStyleSet##type##Top(node, metaProps[META_PROP_ALL]); \ + } else { \ + CSSNodeStyleSet##type##Top(node, 0); \ + } \ + \ + if (!isUndefined(metaProps[META_PROP_BOTTOM])) { \ + CSSNodeStyleSet##type##Bottom(node, metaProps[META_PROP_BOTTOM]); \ + } else if (!isUndefined(metaProps[META_PROP_VERTICAL])) { \ + CSSNodeStyleSet##type##Bottom(node, metaProps[META_PROP_VERTICAL]); \ + } else if (!isUndefined(metaProps[META_PROP_ALL])) { \ + CSSNodeStyleSet##type##Bottom(node, metaProps[META_PROP_ALL]); \ + } else { \ + CSSNodeStyleSet##type##Bottom(node, 0); \ + } \ } +DEFINE_PROCESS_META_PROPS(Padding); +DEFINE_PROCESS_META_PROPS(Margin); +DEFINE_PROCESS_META_PROPS(Border); + // The absolute stuff is so that we can take into account our absolute position when rounding in order to // snap to the pixel grid. For example, say you have the following structure: // @@ -118,29 +146,29 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st // width = 213.5 - 106.5 = 107 // You'll notice that this is the same width we calculated for the parent view because we've taken its position into account. -- (void)applyLayoutNode:(CSSNode *)node +- (void)applyLayoutNode:(CSSNodeRef)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition { - if (!node->layout.shouldUpdate) { + if (!CSSNodeGetShouldUpdate(node)) { return; } - node->layout.shouldUpdate = false; + CSSNodeSetShouldUpdate(node, false); _layoutLifecycle = RCTUpdateLifecycleComputed; CGPoint absoluteTopLeft = { - absolutePosition.x + node->layout.position[CSSPositionLeft], - absolutePosition.y + node->layout.position[CSSPositionTop] + absolutePosition.x + CSSNodeLayoutGetLeft(node), + absolutePosition.y + CSSNodeLayoutGetTop(node) }; CGPoint absoluteBottomRight = { - absolutePosition.x + node->layout.position[CSSPositionLeft] + node->layout.dimensions[CSSDimensionWidth], - absolutePosition.y + node->layout.position[CSSPositionTop] + node->layout.dimensions[CSSDimensionHeight] + absolutePosition.x + CSSNodeLayoutGetLeft(node) + CSSNodeLayoutGetWidth(node), + absolutePosition.y + CSSNodeLayoutGetTop(node) + CSSNodeLayoutGetHeight(node) }; CGRect frame = {{ - RCTRoundPixelValue(node->layout.position[CSSPositionLeft]), - RCTRoundPixelValue(node->layout.position[CSSPositionTop]), + RCTRoundPixelValue(CSSNodeLayoutGetLeft(node)), + RCTRoundPixelValue(CSSNodeLayoutGetTop(node)), }, { RCTRoundPixelValue(absoluteBottomRight.x - absoluteTopLeft.x), RCTRoundPixelValue(absoluteBottomRight.y - absoluteTopLeft.y) @@ -151,19 +179,19 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st [viewsWithNewFrame addObject:self]; } - absolutePosition.x += node->layout.position[CSSPositionLeft]; - absolutePosition.y += node->layout.position[CSSPositionTop]; + absolutePosition.x += CSSNodeLayoutGetLeft(node); + absolutePosition.y += CSSNodeLayoutGetTop(node); [self applyLayoutToChildren:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition]; } -- (void)applyLayoutToChildren:(CSSNode *)node +- (void)applyLayoutToChildren:(CSSNodeRef)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition { - for (int i = 0; i < node->childCount; ++i) { + for (int i = 0; i < CSSNodeGetChildCount(node); ++i) { RCTShadowView *child = (RCTShadowView *)_reactSubviews[i]; - [child applyLayoutNode:node->getChild(node->context, i) + [child applyLayoutNode:RCTGetChild(CSSNodeGetContext(node), i) viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition]; } @@ -237,18 +265,19 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st } if (!CGRectEqualToRect(frame, _frame)) { - _cssNode->style.positionType = CSSPositionTypeAbsolute; - _cssNode->style.dimensions[CSSDimensionWidth] = frame.size.width; - _cssNode->style.dimensions[CSSDimensionHeight] = frame.size.height; - _cssNode->style.position[CSSPositionLeft] = frame.origin.x; - _cssNode->style.position[CSSPositionTop] = frame.origin.y; + CSSNodeStyleSetPositionType(_cssNode, CSSPositionTypeAbsolute); + CSSNodeStyleSetWidth(_cssNode, frame.size.width); + CSSNodeStyleSetHeight(_cssNode, frame.size.height); + CSSNodeStyleSetPositionLeft(_cssNode, frame.origin.x); + CSSNodeStyleSetPositionTop(_cssNode, frame.origin.y); + // Our parent has asked us to change our cssNode->styles. Dirty the layout // so that we can rerun layout on this node. The request came from our parent // so there's no need to dirty our ancestors by calling dirtyLayout. _layoutLifecycle = RCTUpdateLifecycleDirtied; } - layoutNode(_cssNode, frame.size.width, frame.size.height, CSSDirectionInherit); + CSSNodeCalculateLayout(_cssNode, frame.size.width, frame.size.height, CSSDirectionInherit); [self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition]; } @@ -289,10 +318,10 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st _reactSubviews = [NSMutableArray array]; _cssNode = CSSNodeNew(); - _cssNode->context = (__bridge void *)self; - _cssNode->print = RCTPrint; - _cssNode->getChild = RCTGetChild; - _cssNode->isDirty = RCTIsDirty; + CSSNodeSetContext(_cssNode, (__bridge void *)self); + CSSNodeSetChildFunc(_cssNode, RCTGetChild); + CSSNodeSetPrintFunc(_cssNode, RCTPrint); + CSSNodeSetIsDirtyFunc(_cssNode, RCTIsDirty); } return self; } @@ -359,7 +388,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st - (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex { [_reactSubviews insertObject:subview atIndex:atIndex]; - _cssNode->childCount = [self isCSSLeafNode] ? 0 : (int)_reactSubviews.count; + CSSNodeSetChildCount(_cssNode, [self isCSSLeafNode] ? 0 : (int)_reactSubviews.count); subview->_superview = self; _didUpdateSubviews = YES; [self dirtyText]; @@ -375,7 +404,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st _didUpdateSubviews = YES; subview->_superview = nil; [_reactSubviews removeObject:subview]; - _cssNode->childCount = [self isCSSLeafNode] ? 0 : (int)_reactSubviews.count; + CSSNodeSetChildCount(_cssNode, [self isCSSLeafNode] ? 0 : (int)_reactSubviews.count); } - (NSArray *)reactSubviews @@ -475,10 +504,10 @@ RCT_PADDING_PROPERTY(Right, RIGHT) - (UIEdgeInsets)paddingAsInsets { return (UIEdgeInsets){ - _cssNode->style.padding[CSSPositionTop], - _cssNode->style.padding[CSSPositionLeft], - _cssNode->style.padding[CSSPositionBottom], - _cssNode->style.padding[CSSPositionRight] + CSSNodeStyleGetPaddingTop(_cssNode), + CSSNodeStyleGetPaddingLeft(_cssNode), + CSSNodeStyleGetPaddingBottom(_cssNode), + CSSNodeStyleGetPaddingRight(_cssNode) }; } @@ -504,58 +533,66 @@ RCT_BORDER_PROPERTY(Right, RIGHT) // Dimensions -#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp, category) \ +#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \ - (void)set##setProp:(CGFloat)value \ { \ - _cssNode->style.category[CSS##cssProp] = value; \ + CSSNodeStyleSet##cssProp(_cssNode, value); \ [self dirtyLayout]; \ [self dirtyText]; \ } \ - (CGFloat)getProp \ { \ - return _cssNode->style.category[CSS##cssProp]; \ + return CSSNodeStyleGet##cssProp(_cssNode); \ } -RCT_DIMENSION_PROPERTY(Width, width, DimensionWidth, dimensions) -RCT_DIMENSION_PROPERTY(Height, height, DimensionHeight, dimensions) - -RCT_DIMENSION_PROPERTY(MinWidth, minWidth, DimensionWidth, minDimensions) -RCT_DIMENSION_PROPERTY(MaxWidth, maxWidth, DimensionWidth, maxDimensions) -RCT_DIMENSION_PROPERTY(MinHeight, minHeight, DimensionHeight, minDimensions) -RCT_DIMENSION_PROPERTY(MaxHeight, maxHeight, DimensionHeight, maxDimensions) +RCT_DIMENSION_PROPERTY(Width, width, Width) +RCT_DIMENSION_PROPERTY(Height, height, Height) +RCT_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth) +RCT_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight) +RCT_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth) +RCT_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight) // Position -#define RCT_POSITION_PROPERTY(setProp, getProp, cssProp) \ -RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp, position) - -RCT_POSITION_PROPERTY(Top, top, PositionTop) -RCT_POSITION_PROPERTY(Right, right, PositionRight) -RCT_POSITION_PROPERTY(Bottom, bottom, PositionBottom) -RCT_POSITION_PROPERTY(Left, left, PositionLeft) +RCT_DIMENSION_PROPERTY(Top, top, PositionTop) +RCT_DIMENSION_PROPERTY(Right, right, PositionRight) +RCT_DIMENSION_PROPERTY(Bottom, bottom, PositionBottom) +RCT_DIMENSION_PROPERTY(Left, left, PositionLeft) - (void)setFrame:(CGRect)frame { - _cssNode->style.position[CSSPositionLeft] = CGRectGetMinX(frame); - _cssNode->style.position[CSSPositionTop] = CGRectGetMinY(frame); - _cssNode->style.dimensions[CSSDimensionWidth] = CGRectGetWidth(frame); - _cssNode->style.dimensions[CSSDimensionHeight] = CGRectGetHeight(frame); + CSSNodeStyleSetPositionLeft(_cssNode, CGRectGetMinX(frame)); + CSSNodeStyleSetPositionTop(_cssNode, CGRectGetMinY(frame)); + CSSNodeStyleSetWidth(_cssNode, CGRectGetWidth(frame)); + CSSNodeStyleSetHeight(_cssNode, CGRectGetHeight(frame)); [self dirtyLayout]; } -static inline BOOL RCTAssignSuggestedDimension(CSSNode *cssNode, int dimension, CGFloat amount) +static inline BOOL RCTAssignSuggestedDimension(CSSNodeRef cssNode, CSSDimension dimension, CGFloat amount) { - if (amount != UIViewNoIntrinsicMetric - && isnan(cssNode->style.dimensions[dimension])) { - cssNode->style.dimensions[dimension] = amount; - return YES; + if (amount != UIViewNoIntrinsicMetric) { + switch (dimension) { + case CSSDimensionWidth: + if (isnan(CSSNodeStyleGetWidth(cssNode))) { + CSSNodeStyleSetWidth(cssNode, amount); + return YES; + } + break; + case CSSDimensionHeight: + if (isnan(CSSNodeStyleGetHeight(cssNode))) { + CSSNodeStyleSetHeight(cssNode, amount); + return YES; + } + break; + } } + return NO; } - (void)setIntrinsicContentSize:(CGSize)size { - if (_cssNode->style.flex == 0) { + if (CSSNodeStyleGetFlex(_cssNode) == 0) { BOOL dirty = NO; dirty |= RCTAssignSuggestedDimension(_cssNode, CSSDimensionHeight, size.height); dirty |= RCTAssignSuggestedDimension(_cssNode, CSSDimensionWidth, size.width); @@ -567,15 +604,15 @@ static inline BOOL RCTAssignSuggestedDimension(CSSNode *cssNode, int dimension, - (void)setTopLeft:(CGPoint)topLeft { - _cssNode->style.position[CSSPositionLeft] = topLeft.x; - _cssNode->style.position[CSSPositionTop] = topLeft.y; + CSSNodeStyleSetPositionLeft(_cssNode, topLeft.x); + CSSNodeStyleSetPositionTop(_cssNode, topLeft.y); [self dirtyLayout]; } - (void)setSize:(CGSize)size { - _cssNode->style.dimensions[CSSDimensionWidth] = size.width; - _cssNode->style.dimensions[CSSDimensionHeight] = size.height; + CSSNodeStyleSetWidth(_cssNode, size.width); + CSSNodeStyleSetHeight(_cssNode, size.height); [self dirtyLayout]; } @@ -584,21 +621,21 @@ static inline BOOL RCTAssignSuggestedDimension(CSSNode *cssNode, int dimension, #define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \ - (void)set##setProp:(type)value \ { \ - _cssNode->style.cssProp = value; \ + CSSNodeStyleSet##cssProp(_cssNode, value); \ [self dirtyLayout]; \ } \ - (type)getProp \ { \ - return _cssNode->style.cssProp; \ + return CSSNodeStyleGet##cssProp(_cssNode); \ } -RCT_STYLE_PROPERTY(Flex, flex, flex, CGFloat) -RCT_STYLE_PROPERTY(FlexDirection, flexDirection, flexDirection, CSSFlexDirection) -RCT_STYLE_PROPERTY(JustifyContent, justifyContent, justifyContent, CSSJustify) -RCT_STYLE_PROPERTY(AlignSelf, alignSelf, alignSelf, CSSAlign) -RCT_STYLE_PROPERTY(AlignItems, alignItems, alignItems, CSSAlign) -RCT_STYLE_PROPERTY(Position, position, positionType, CSSPositionType) -RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flexWrap, CSSWrapType) +RCT_STYLE_PROPERTY(Flex, flex, Flex, CGFloat) +RCT_STYLE_PROPERTY(FlexDirection, flexDirection, FlexDirection, CSSFlexDirection) +RCT_STYLE_PROPERTY(JustifyContent, justifyContent, JustifyContent, CSSJustify) +RCT_STYLE_PROPERTY(AlignSelf, alignSelf, AlignSelf, CSSAlign) +RCT_STYLE_PROPERTY(AlignItems, alignItems, AlignItems, CSSAlign) +RCT_STYLE_PROPERTY(Position, position, PositionType, CSSPositionType) +RCT_STYLE_PROPERTY(FlexWrap, flexWrap, FlexWrap, CSSWrapType) - (void)setBackgroundColor:(UIColor *)color { @@ -624,13 +661,13 @@ RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flexWrap, CSSWrapType) - (void)didSetProps:(__unused NSArray *)changedProps { if (_recomputePadding) { - RCTProcessMetaProps(_paddingMetaProps, _cssNode->style.padding); + RCTProcessMetaPropsPadding(_paddingMetaProps, _cssNode); } if (_recomputeMargin) { - RCTProcessMetaProps(_marginMetaProps, _cssNode->style.margin); + RCTProcessMetaPropsMargin(_marginMetaProps, _cssNode); } if (_recomputeBorder) { - RCTProcessMetaProps(_borderMetaProps, _cssNode->style.border); + RCTProcessMetaPropsBorder(_borderMetaProps, _cssNode); } if (_recomputePadding || _recomputeMargin || _recomputeBorder) { [self dirtyLayout];