From 36721c36433d0e0cf884b2b82224072cb6d75aba Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Sun, 3 Mar 2019 17:14:03 -0800 Subject: [PATCH] Add `setNeedsLayout` to yoga tree changes. --- Source/ASDisplayNode+Layout.mm | 16 ++- Source/ASDisplayNode+Yoga.mm | 2 + Source/Layout/ASLayoutElement.mm | 226 +++++++++++++++++++------------ 3 files changed, 154 insertions(+), 90 deletions(-) diff --git a/Source/ASDisplayNode+Layout.mm b/Source/ASDisplayNode+Layout.mm index 9fbecf39f..e836523e2 100644 --- a/Source/ASDisplayNode+Layout.mm +++ b/Source/ASDisplayNode+Layout.mm @@ -19,6 +19,19 @@ #import #import +@interface ASDisplayNode (ASLayoutElementStyleDelegate) +@end + +@implementation ASDisplayNode (ASLayoutElementStyleDelegate) + +#pragma mark + +- (void)style:(ASLayoutElementStyle *)style propertyDidChange:(NSString *)propertyName { + [self setNeedsLayout]; +} + +@end + #pragma mark - ASDisplayNode (ASLayoutElement) @implementation ASDisplayNode (ASLayoutElement) @@ -42,8 +55,9 @@ - (ASLayoutElementStyle *)style - (ASLayoutElementStyle *)_locked_style { + ASAssertLocked(__instanceLock__); if (_style == nil) { - _style = [[ASLayoutElementStyle alloc] init]; + _style = [[ASLayoutElementStyle alloc] initWithDelegate:self]; } return _style; } diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index ecd44805d..512c0ce71 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -91,6 +91,7 @@ - (void)_locked_removeYogaChild:(ASDisplayNode *)child // YGNodeRef removal is done in setParent: child.yogaParent = nil; + [self setNeedsLayout]; } - (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index @@ -115,6 +116,7 @@ - (void)_locked_insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index // YGNodeRef insertion is done in setParent: child.yogaParent = self; + [self setNeedsLayout]; } #pragma mark - Subclass Hooks diff --git a/Source/Layout/ASLayoutElement.mm b/Source/Layout/ASLayoutElement.mm index 5a33a11f4..7cc168083 100644 --- a/Source/Layout/ASLayoutElement.mm +++ b/Source/Layout/ASLayoutElement.mm @@ -139,12 +139,19 @@ void ASLayoutElementPopContext() NSString * const ASYogaAspectRatioProperty = @"ASYogaAspectRatioProperty"; #endif -#define ASLayoutElementStyleSetSizeWithScope(x) \ - __instanceLock__.lock(); \ - ASLayoutElementSize newSize = _size.load(); \ - { x } \ - _size.store(newSize); \ - __instanceLock__.unlock(); +#define ASLayoutElementStyleSetSizeWithScope(x) \ + ({ \ + __instanceLock__.lock(); \ + const ASLayoutElementSize oldSize = _size.load(); \ + ASLayoutElementSize newSize = oldSize; \ + {x}; \ + BOOL changed = !ASLayoutElementSizeEqualToLayoutElementSize(oldSize, newSize); \ + if (changed) { \ + _size.store(newSize); \ + } \ + __instanceLock__.unlock(); \ + changed; \ + }) #define ASLayoutElementStyleCallDelegate(propertyName)\ do {\ @@ -202,9 +209,13 @@ - (instancetype)init { self = [super init]; if (self) { - _size = ASLayoutElementSizeMake(); + std::atomic_init(&_size, ASLayoutElementSizeMake()); + std::atomic_init(&_flexBasis, ASDimensionAuto); #if YOGA _parentAlignStyle = ASStackLayoutAlignItemsNotSet; + std::atomic_init(&_flexDirection, ASStackLayoutDirectionVertical); + std::atomic_init(&_alignItems, ASStackLayoutAlignItemsStretch); + std::atomic_init(&_aspectRatio, static_cast(YGUndefined)); #endif } return self; @@ -236,10 +247,10 @@ - (ASDimension)width - (void)setWidth:(ASDimension)width { - ASLayoutElementStyleSetSizeWithScope({ - newSize.width = width; - }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.width = width; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + } } - (ASDimension)height @@ -249,10 +260,10 @@ - (ASDimension)height - (void)setHeight:(ASDimension)height { - ASLayoutElementStyleSetSizeWithScope({ - newSize.height = height; - }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.height = height; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + } } - (ASDimension)minWidth @@ -262,10 +273,10 @@ - (ASDimension)minWidth - (void)setMinWidth:(ASDimension)minWidth { - ASLayoutElementStyleSetSizeWithScope({ - newSize.minWidth = minWidth; - }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.minWidth = minWidth; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + } } - (ASDimension)maxWidth @@ -275,10 +286,10 @@ - (ASDimension)maxWidth - (void)setMaxWidth:(ASDimension)maxWidth { - ASLayoutElementStyleSetSizeWithScope({ - newSize.maxWidth = maxWidth; - }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.maxWidth = maxWidth; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + } } - (ASDimension)minHeight @@ -288,10 +299,10 @@ - (ASDimension)minHeight - (void)setMinHeight:(ASDimension)minHeight { - ASLayoutElementStyleSetSizeWithScope({ - newSize.minHeight = minHeight; - }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.minHeight = minHeight; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + } } - (ASDimension)maxHeight @@ -301,10 +312,10 @@ - (ASDimension)maxHeight - (void)setMaxHeight:(ASDimension)maxHeight { - ASLayoutElementStyleSetSizeWithScope({ - newSize.maxHeight = maxHeight; - }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.maxHeight = maxHeight; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + } } @@ -312,12 +323,14 @@ - (void)setMaxHeight:(ASDimension)maxHeight - (void)setPreferredSize:(CGSize)preferredSize { - ASLayoutElementStyleSetSizeWithScope({ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.width = ASDimensionMakeWithPoints(preferredSize.width); newSize.height = ASDimensionMakeWithPoints(preferredSize.height); }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + } } - (CGSize)preferredSize @@ -338,22 +351,26 @@ - (CGSize)preferredSize - (void)setMinSize:(CGSize)minSize { - ASLayoutElementStyleSetSizeWithScope({ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.minWidth = ASDimensionMakeWithPoints(minSize.width); newSize.minHeight = ASDimensionMakeWithPoints(minSize.height); }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + } } - (void)setMaxSize:(CGSize)maxSize { - ASLayoutElementStyleSetSizeWithScope({ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.maxWidth = ASDimensionMakeWithPoints(maxSize.width); newSize.maxHeight = ASDimensionMakeWithPoints(maxSize.height); }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + } } - (ASLayoutSize)preferredLayoutSize @@ -364,12 +381,14 @@ - (ASLayoutSize)preferredLayoutSize - (void)setPreferredLayoutSize:(ASLayoutSize)preferredLayoutSize { - ASLayoutElementStyleSetSizeWithScope({ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.width = preferredLayoutSize.width; newSize.height = preferredLayoutSize.height; }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + } } - (ASLayoutSize)minLayoutSize @@ -380,12 +399,14 @@ - (ASLayoutSize)minLayoutSize - (void)setMinLayoutSize:(ASLayoutSize)minLayoutSize { - ASLayoutElementStyleSetSizeWithScope({ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.minWidth = minLayoutSize.width; newSize.minHeight = minLayoutSize.height; }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + } } - (ASLayoutSize)maxLayoutSize @@ -396,20 +417,23 @@ - (ASLayoutSize)maxLayoutSize - (void)setMaxLayoutSize:(ASLayoutSize)maxLayoutSize { - ASLayoutElementStyleSetSizeWithScope({ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.maxWidth = maxLayoutSize.width; newSize.maxHeight = maxLayoutSize.height; }); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + } } #pragma mark - ASStackLayoutElement - (void)setSpacingBefore:(CGFloat)spacingBefore { - _spacingBefore.store(spacingBefore); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingBeforeProperty); + if (_spacingBefore.exchange(spacingBefore) != spacingBefore) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingBeforeProperty); + } } - (CGFloat)spacingBefore @@ -419,8 +443,9 @@ - (CGFloat)spacingBefore - (void)setSpacingAfter:(CGFloat)spacingAfter { - _spacingAfter.store(spacingAfter); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingAfterProperty); + if (_spacingAfter.exchange(spacingAfter) != spacingAfter) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingAfterProperty); + } } - (CGFloat)spacingAfter @@ -430,8 +455,9 @@ - (CGFloat)spacingAfter - (void)setFlexGrow:(CGFloat)flexGrow { - _flexGrow.store(flexGrow); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexGrowProperty); + if (_flexGrow.exchange(flexGrow) != flexGrow) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexGrowProperty); + } } - (CGFloat)flexGrow @@ -441,8 +467,9 @@ - (CGFloat)flexGrow - (void)setFlexShrink:(CGFloat)flexShrink { - _flexShrink.store(flexShrink); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexShrinkProperty); + if (_flexShrink.exchange(flexShrink) != flexShrink) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexShrinkProperty); + } } - (CGFloat)flexShrink @@ -452,8 +479,9 @@ - (CGFloat)flexShrink - (void)setFlexBasis:(ASDimension)flexBasis { - _flexBasis.store(flexBasis); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexBasisProperty); + if (!ASDimensionEqualToDimension(_flexBasis.exchange(flexBasis), flexBasis)) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexBasisProperty); + } } - (ASDimension)flexBasis @@ -463,8 +491,9 @@ - (ASDimension)flexBasis - (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf { - _alignSelf.store(alignSelf); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAlignSelfProperty); + if (_alignSelf.exchange(alignSelf) != alignSelf) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAlignSelfProperty); + } } - (ASStackLayoutAlignSelf)alignSelf @@ -474,8 +503,9 @@ - (ASStackLayoutAlignSelf)alignSelf - (void)setAscender:(CGFloat)ascender { - _ascender.store(ascender); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAscenderProperty); + if (_ascender.exchange(ascender) != ascender) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAscenderProperty); + } } - (CGFloat)ascender @@ -485,8 +515,9 @@ - (CGFloat)ascender - (void)setDescender:(CGFloat)descender { - _descender.store(descender); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleDescenderProperty); + if (_descender.exchange(descender) != descender) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleDescenderProperty); + } } - (CGFloat)descender @@ -498,8 +529,9 @@ - (CGFloat)descender - (void)setLayoutPosition:(CGPoint)layoutPosition { - _layoutPosition.store(layoutPosition); - ASLayoutElementStyleCallDelegate(ASLayoutElementStyleLayoutPositionProperty); + if (!CGPointEqualToPoint(_layoutPosition.exchange(layoutPosition), layoutPosition)) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleLayoutPositionProperty); + } } - (CGPoint)layoutPosition @@ -787,48 +819,64 @@ - (ASStackLayoutAlignItems)parentAlignStyle { } - (void)setFlexWrap:(YGWrap)flexWrap { - _flexWrap.store(flexWrap); - ASLayoutElementStyleCallDelegate(ASYogaFlexWrapProperty); + if (_flexWrap.exchange(flexWrap) != flexWrap) { + ASLayoutElementStyleCallDelegate(ASYogaFlexWrapProperty); + } } - (void)setFlexDirection:(ASStackLayoutDirection)flexDirection { - _flexDirection.store(flexDirection); - ASLayoutElementStyleCallDelegate(ASYogaFlexDirectionProperty); + if (_flexDirection.exchange(flexDirection) != flexDirection) { + ASLayoutElementStyleCallDelegate(ASYogaFlexDirectionProperty); + } } - (void)setDirection:(YGDirection)direction { - _direction.store(direction); - ASLayoutElementStyleCallDelegate(ASYogaDirectionProperty); + if (_direction.exchange(direction) != direction) { + ASLayoutElementStyleCallDelegate(ASYogaDirectionProperty); + } } - (void)setJustifyContent:(ASStackLayoutJustifyContent)justify { - _justifyContent.store(justify); - ASLayoutElementStyleCallDelegate(ASYogaJustifyContentProperty); + if (_justifyContent.exchange(justify) != justify) { + ASLayoutElementStyleCallDelegate(ASYogaJustifyContentProperty); + } } - (void)setAlignItems:(ASStackLayoutAlignItems)alignItems { - _alignItems.store(alignItems); - ASLayoutElementStyleCallDelegate(ASYogaAlignItemsProperty); + if (_alignItems.exchange(alignItems) != alignItems) { + ASLayoutElementStyleCallDelegate(ASYogaAlignItemsProperty); + } } - (void)setPositionType:(YGPositionType)positionType { - _positionType.store(positionType); - ASLayoutElementStyleCallDelegate(ASYogaPositionTypeProperty); + if (_positionType.exchange(positionType) != positionType) { + ASLayoutElementStyleCallDelegate(ASYogaPositionTypeProperty); + } } +/// TODO: smart compare ASEdgeInsets instead of memory compare. - (void)setPosition:(ASEdgeInsets)position { - _position.store(position); - ASLayoutElementStyleCallDelegate(ASYogaPositionProperty); + ASEdgeInsets oldValue = _position.exchange(position); + if (0 != memcmp(&position, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaPositionProperty); + } } - (void)setMargin:(ASEdgeInsets)margin { - _margin.store(margin); - ASLayoutElementStyleCallDelegate(ASYogaMarginProperty); + ASEdgeInsets oldValue = _margin.exchange(margin); + if (0 != memcmp(&margin, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaMarginProperty); + } } - (void)setPadding:(ASEdgeInsets)padding { - _padding.store(padding); - ASLayoutElementStyleCallDelegate(ASYogaPaddingProperty); + ASEdgeInsets oldValue = _padding.exchange(padding); + if (0 != memcmp(&padding, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaPaddingProperty); + } } - (void)setBorder:(ASEdgeInsets)border { - _border.store(border); - ASLayoutElementStyleCallDelegate(ASYogaBorderProperty); + ASEdgeInsets oldValue = _border.exchange(border); + if (0 != memcmp(&border, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaBorderProperty); + } } - (void)setAspectRatio:(CGFloat)aspectRatio { - _aspectRatio.store(aspectRatio); - ASLayoutElementStyleCallDelegate(ASYogaAspectRatioProperty); + if (_aspectRatio.exchange(aspectRatio) != aspectRatio) { + ASLayoutElementStyleCallDelegate(ASYogaAspectRatioProperty); + } } // private (ASLayoutElementStylePrivate.h) - (void)setParentAlignStyle:(ASStackLayoutAlignItems)style {