Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Yoga Beta] Improvements to the experimental support for Yoga layout. #59

Merged
merged 10 commits into from
Apr 27, 2017
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 3 additions & 1 deletion Source/ASDisplayNode+Beta.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if these are implemented using C++ atomics, we ought to declare them as atomic.

@property (nonatomic, assign, readwrite) CGFloat spacing;
@property (nonatomic, assign, readwrite) ASStackLayoutJustifyContent justifyContent;
@property (nonatomic, assign, readwrite) ASStackLayoutAlignItems alignItems;
Expand Down
45 changes: 32 additions & 13 deletions Source/ASDisplayNode+Yoga.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! That class method may not be safe off-main, but I expect you've considered that. Even if so, the risk here is teeny tiny.

}

- (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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
*/
Expand Down
32 changes: 21 additions & 11 deletions Source/Layout/ASDimension.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma once
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASAssert.h>

Expand Down Expand Up @@ -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

/**
Expand Down Expand Up @@ -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
9 changes: 5 additions & 4 deletions Source/Layout/ASDimension.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -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
9 changes: 6 additions & 3 deletions Source/Layout/ASLayoutElement.mm
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ @implementation ASLayoutElementStyle {
std::atomic<CGPoint> _layoutPosition;

#if YOGA
std::atomic<ASStackLayoutDirection> _direction;
std::atomic<ASStackLayoutDirection> _flexDirection;
std::atomic<YGDirection> _direction;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(You knew it was coming!) Maybe these would be better-off as Objective-C atomic properties to improve performance and cut down on boilerplate?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As all of them are currently c++ atomics let's go with it for now and do the whole change in a another diff if we want to.

std::atomic<CGFloat> _spacing;
std::atomic<ASStackLayoutJustifyContent> _justifyContent;
std::atomic<ASStackLayoutAlignItems> _alignItems;
Expand Down Expand Up @@ -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(); }
Expand All @@ -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); }
Expand Down
8 changes: 8 additions & 0 deletions Source/Private/ASDisplayNode+FrameworkPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions Source/Private/ASDisplayNode+UIViewBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down