diff --git a/CHANGELOG.md b/CHANGELOG.md index 206494197..9bcbcd664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,4 @@ - Add support for IGListKit post-removal-of-IGListSectionType, in preparation for IGListKit 3.0.0 release. (Adlai-Holler)[https://github.com/Adlai-Holler] (#49)[https://github.com/TextureGroup/Texture/pull/49] - Fix `__has_include` check in ASLog.h [Philipp Smorygo](Philipp.Smorygo@jetbrains.com) - Fix potential deadlock in ASControlNode [Garrett Moon](https://github.com/garrettmoon) +- [Yoga Beta] Improvements to the experimental support for Yoga layout [Scott Goodson](appleguy) \ No newline at end of file diff --git a/Source/ASDisplayNode+Beta.h b/Source/ASDisplayNode+Beta.h index 029b8047b..e5f1ee225 100644 --- a/Source/ASDisplayNode+Beta.h +++ b/Source/ASDisplayNode+Beta.h @@ -176,12 +176,14 @@ extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable // These methods should not normally be called directly. - (void)invalidateCalculatedYogaLayout; - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize; +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute; @end @interface ASLayoutElementStyle (Yoga) -@property (nonatomic, assign, readwrite) ASStackLayoutDirection direction; +@property (nonatomic, assign, readwrite) ASStackLayoutDirection flexDirection; +@property (nonatomic, assign, readwrite) YGDirection direction; @property (nonatomic, assign, readwrite) CGFloat spacing; @property (nonatomic, assign, readwrite) ASStackLayoutJustifyContent justifyContent; @property (nonatomic, assign, readwrite) ASStackLayoutAlignItems alignItems; diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index 29febc28b..43166d5c5 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -147,10 +147,15 @@ float yogaDimensionToPercent(ASDimension dimension) ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets) { switch (edge) { - case YGEdgeLeft: return insets.left; - case YGEdgeTop: return insets.top; - case YGEdgeRight: return insets.right; - case YGEdgeBottom: return insets.bottom; + case YGEdgeLeft: return insets.left; + case YGEdgeTop: return insets.top; + case YGEdgeRight: return insets.right; + case YGEdgeBottom: return insets.bottom; + case YGEdgeStart: return insets.start; + case YGEdgeEnd: return insets.end; + case YGEdgeHorizontal: return insets.horizontal; + case YGEdgeVertical: return insets.vertical; + case YGEdgeAll: return insets.all; default: ASDisplayNodeCAssert(NO, @"YGEdge other than ASEdgeInsets is not supported."); return ASDimensionAuto; } @@ -314,10 +319,9 @@ - (void)setupYogaCalculatedLayout - (void)setYogaMeasureFuncIfNeeded { - // Manual size calculation via calculateSizeThatFits: - // This will be used for ASTextNode, as well as any other leaf node that has no layout spec. - if ((self.methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == NO - && self.layoutSpecBlock == NULL && self.yogaChildren.count == 0) { + // Size calculation via calculateSizeThatFits: or layoutSpecThatFits: + // This will be used for ASTextNode, as well as any other node that has no Yoga children + if (self.yogaChildren.count == 0) { YGNodeRef yogaNode = self.yogaNode; // Use property to assign Ref if needed. YGNodeSetContext(yogaNode, (__bridge void *)self); YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc); @@ -333,8 +337,24 @@ - (void)invalidateCalculatedYogaLayout } } +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute +{ + if (AS_AT_LEAST_IOS9) { + UIUserInterfaceLayoutDirection layoutDirection = + [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; + self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight + ? YGDirectionLTR : YGDirectionRTL); + } +} + - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize { + if (self.yogaParent) { + if (ASHierarchyStateIncludesYogaLayoutMeasuring(self.hierarchyState) == NO) { + [self _setNeedsLayoutFromAbove]; + } + return; + } if (ASHierarchyStateIncludesYogaLayoutMeasuring(self.hierarchyState)) { ASDisplayNodeAssert(NO, @"A Yoga layout is being performed by a parent; children must not perform their own until it is done! %@", [self displayNodeRecursiveDescription]); return; @@ -356,14 +376,14 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize ASLayoutElementStyle *style = node.style; YGNodeRef yogaNode = node.yogaNode; - YGNodeStyleSetDirection (yogaNode, YGDirectionInherit); + YGNodeStyleSetDirection (yogaNode, style.direction); YGNodeStyleSetFlexWrap (yogaNode, style.flexWrap); YGNodeStyleSetFlexGrow (yogaNode, style.flexGrow); YGNodeStyleSetFlexShrink (yogaNode, style.flexShrink); YGNODE_STYLE_SET_DIMENSION (yogaNode, FlexBasis, style.flexBasis); - YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.direction)); + YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.flexDirection)); YGNodeStyleSetJustifyContent(yogaNode, yogaJustifyContent(style.justifyContent)); YGNodeStyleSetAlignSelf (yogaNode, yogaAlignSelf(style.alignSelf)); ASStackLayoutAlignItems alignItems = style.alignItems; @@ -378,12 +398,12 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize ASEdgeInsets border = style.border; YGEdge edge = YGEdgeLeft; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < YGEdgeAll + 1; ++i) { YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge); YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge); YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge); YGNODE_STYLE_SET_FLOAT_WITH_EDGE(yogaNode, Border, dimensionForEdgeWithEdgeInsets(edge, border), edge); - edge = (edge == YGEdgeLeft ? YGEdgeTop : (edge == YGEdgeTop ? YGEdgeRight : YGEdgeBottom)); + edge = (YGEdge)(edge + 1); } CGFloat aspectRatio = style.aspectRatio; @@ -406,7 +426,6 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize [node setYogaMeasureFuncIfNeeded]; /* TODO(appleguy): STYLE SETTER METHODS LEFT TO IMPLEMENT - void YGNodeStyleSetFlexDirection(YGNodeRef node, YGFlexDirection flexDirection); void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); void YGNodeStyleSetFlex(YGNodeRef node, float flex); */ diff --git a/Source/Layout/ASDimension.h b/Source/Layout/ASDimension.h index 960a857b2..21960f376 100644 --- a/Source/Layout/ASDimension.h +++ b/Source/Layout/ASDimension.h @@ -18,6 +18,7 @@ #pragma once #import #import +#import #import #import @@ -213,17 +214,6 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutSize(AS NSStringFromASDimension(size.height)]; } -#pragma mark - ASEdgeInsets - -typedef struct { - ASDimension top; - ASDimension left; - ASDimension bottom; - ASDimension right; -} ASEdgeInsets; - -extern ASEdgeInsets const ASEdgeInsetsZero; - #pragma mark - ASSizeRange /** @@ -308,5 +298,25 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeEqualToSizeRange(ASSi */ extern AS_WARN_UNUSED_RESULT NSString *NSStringFromASSizeRange(ASSizeRange sizeRange); +#if YOGA + +#pragma mark - ASEdgeInsets + +typedef struct { + ASDimension top; + ASDimension left; + ASDimension bottom; + ASDimension right; + ASDimension start; + ASDimension end; + ASDimension horizontal; + ASDimension vertical; + ASDimension all; +} ASEdgeInsets; + +extern ASEdgeInsets const ASEdgeInsetsZero; + +#endif + NS_ASSUME_NONNULL_END ASDISPLAYNODE_EXTERN_C_END diff --git a/Source/Layout/ASDimension.mm b/Source/Layout/ASDimension.mm index 88ae6a305..0b71cb088 100644 --- a/Source/Layout/ASDimension.mm +++ b/Source/Layout/ASDimension.mm @@ -67,10 +67,6 @@ ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension) ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto}; -#pragma mark - ASEdgeInsets - -ASEdgeInsets const ASEdgeInsetsZero = {}; - #pragma mark - ASSizeRange ASSizeRange const ASSizeRangeZero = {}; @@ -115,3 +111,8 @@ ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRan NSStringFromCGSize(sizeRange.min), NSStringFromCGSize(sizeRange.max)]; } + +#if YOGA +#pragma mark - Yoga - ASEdgeInsets +ASEdgeInsets const ASEdgeInsetsZero = {}; +#endif diff --git a/Source/Layout/ASLayoutElement.mm b/Source/Layout/ASLayoutElement.mm index fccdeff2b..07091da18 100644 --- a/Source/Layout/ASLayoutElement.mm +++ b/Source/Layout/ASLayoutElement.mm @@ -150,7 +150,8 @@ @implementation ASLayoutElementStyle { std::atomic _layoutPosition; #if YOGA - std::atomic _direction; + std::atomic _flexDirection; + std::atomic _direction; std::atomic _spacing; std::atomic _justifyContent; std::atomic _alignItems; @@ -600,7 +601,8 @@ - (NSString *)description #if YOGA -- (ASStackLayoutDirection)direction { return _direction.load(); } +- (ASStackLayoutDirection)flexDirection { return _flexDirection.load(); } +- (YGDirection)direction { return _direction.load(); } - (CGFloat)spacing { return _spacing.load(); } - (ASStackLayoutJustifyContent)justifyContent { return _justifyContent.load(); } - (ASStackLayoutAlignItems)alignItems { return _alignItems.load(); } @@ -612,7 +614,8 @@ - (ASEdgeInsets)border { return _border.load(); } - (CGFloat)aspectRatio { return _aspectRatio.load(); } - (YGWrap)flexWrap { return _flexWrap.load(); } -- (void)setDirection:(ASStackLayoutDirection)direction { _direction.store(direction); } +- (void)setFlexDirection:(ASStackLayoutDirection)flexDirection { _flexDirection.store(flexDirection); } +- (void)setDirection:(YGDirection)direction { _direction.store(direction); } - (void)setSpacing:(CGFloat)spacing { _spacing.store(spacing); } - (void)setJustifyContent:(ASStackLayoutJustifyContent)justify { _justifyContent.store(justify); } - (void)setAlignItems:(ASStackLayoutAlignItems)alignItems { _alignItems.store(alignItems); } diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index ee2e7b727..d55bb0eb6 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -221,6 +221,14 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat */ - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState; +/** + * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. + * + * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know + * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. + */ +- (void)_setNeedsLayoutFromAbove; + /** * @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes * size is invalidated and may need to result in a different size as the current calculated size. diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index 1b5b9c98e..73322a343 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -794,6 +794,9 @@ - (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentA _bridge_prologue_write; if (AS_AT_LEAST_IOS9) { _setToViewOnly(semanticContentAttribute, semanticContentAttribute); +#if YOGA + [self semanticContentAttributeDidChange:semanticContentAttribute]; +#endif } }