Fix SPM ompilation error
This commit is contained in:
519
YogaKit/Source/YogaKit.m
Normal file
519
YogaKit/Source/YogaKit.m
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "UIView+Yoga.h"
|
||||
#import "YGLayout+Private.h"
|
||||
|
||||
#define YG_PROPERTY(type, lowercased_name, capitalized_name) \
|
||||
-(type)lowercased_name { \
|
||||
return YGNodeStyleGet##capitalized_name(self.node); \
|
||||
} \
|
||||
\
|
||||
-(void)set##capitalized_name : (type)lowercased_name { \
|
||||
YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \
|
||||
}
|
||||
|
||||
#define YG_VALUE_PROPERTY(lowercased_name, capitalized_name) \
|
||||
-(YGValue)lowercased_name { \
|
||||
return YGNodeStyleGet##capitalized_name(self.node); \
|
||||
} \
|
||||
\
|
||||
-(void)set##capitalized_name : (YGValue)lowercased_name { \
|
||||
switch (lowercased_name.unit) { \
|
||||
case YGUnitUndefined: \
|
||||
YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \
|
||||
break; \
|
||||
case YGUnitPoint: \
|
||||
YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \
|
||||
break; \
|
||||
case YGUnitPercent: \
|
||||
YGNodeStyleSet##capitalized_name##Percent( \
|
||||
self.node, lowercased_name.value); \
|
||||
break; \
|
||||
default: \
|
||||
NSAssert(NO, @"Not implemented"); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define YG_AUTO_VALUE_PROPERTY(lowercased_name, capitalized_name) \
|
||||
-(YGValue)lowercased_name { \
|
||||
return YGNodeStyleGet##capitalized_name(self.node); \
|
||||
} \
|
||||
\
|
||||
-(void)set##capitalized_name : (YGValue)lowercased_name { \
|
||||
switch (lowercased_name.unit) { \
|
||||
case YGUnitPoint: \
|
||||
YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \
|
||||
break; \
|
||||
case YGUnitPercent: \
|
||||
YGNodeStyleSet##capitalized_name##Percent( \
|
||||
self.node, lowercased_name.value); \
|
||||
break; \
|
||||
case YGUnitAuto: \
|
||||
YGNodeStyleSet##capitalized_name##Auto(self.node); \
|
||||
break; \
|
||||
default: \
|
||||
NSAssert(NO, @"Not implemented"); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define YG_EDGE_PROPERTY_GETTER( \
|
||||
type, lowercased_name, capitalized_name, property, edge) \
|
||||
-(type)lowercased_name { \
|
||||
return YGNodeStyleGet##property(self.node, edge); \
|
||||
}
|
||||
|
||||
#define YG_EDGE_PROPERTY_SETTER( \
|
||||
lowercased_name, capitalized_name, property, edge) \
|
||||
-(void)set##capitalized_name : (CGFloat)lowercased_name { \
|
||||
YGNodeStyleSet##property(self.node, edge, lowercased_name); \
|
||||
}
|
||||
|
||||
#define YG_EDGE_PROPERTY(lowercased_name, capitalized_name, property, edge) \
|
||||
YG_EDGE_PROPERTY_GETTER( \
|
||||
CGFloat, lowercased_name, capitalized_name, property, edge) \
|
||||
YG_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge)
|
||||
|
||||
#define YG_VALUE_EDGE_PROPERTY_SETTER( \
|
||||
objc_lowercased_name, objc_capitalized_name, c_name, edge) \
|
||||
-(void)set##objc_capitalized_name : (YGValue)objc_lowercased_name { \
|
||||
switch (objc_lowercased_name.unit) { \
|
||||
case YGUnitUndefined: \
|
||||
YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \
|
||||
break; \
|
||||
case YGUnitPoint: \
|
||||
YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \
|
||||
break; \
|
||||
case YGUnitPercent: \
|
||||
YGNodeStyleSet##c_name##Percent( \
|
||||
self.node, edge, objc_lowercased_name.value); \
|
||||
break; \
|
||||
default: \
|
||||
NSAssert(NO, @"Not implemented"); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name, capitalized_name, property, edge) \
|
||||
YG_EDGE_PROPERTY_GETTER( \
|
||||
YGValue, lowercased_name, capitalized_name, property, edge) \
|
||||
YG_VALUE_EDGE_PROPERTY_SETTER( \
|
||||
lowercased_name, capitalized_name, property, edge)
|
||||
|
||||
#define YG_VALUE_EDGES_PROPERTIES(lowercased_name, capitalized_name) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Left, \
|
||||
capitalized_name##Left, \
|
||||
capitalized_name, \
|
||||
YGEdgeLeft) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Top, \
|
||||
capitalized_name##Top, \
|
||||
capitalized_name, \
|
||||
YGEdgeTop) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Right, \
|
||||
capitalized_name##Right, \
|
||||
capitalized_name, \
|
||||
YGEdgeRight) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Bottom, \
|
||||
capitalized_name##Bottom, \
|
||||
capitalized_name, \
|
||||
YGEdgeBottom) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Start, \
|
||||
capitalized_name##Start, \
|
||||
capitalized_name, \
|
||||
YGEdgeStart) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##End, \
|
||||
capitalized_name##End, \
|
||||
capitalized_name, \
|
||||
YGEdgeEnd) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Horizontal, \
|
||||
capitalized_name##Horizontal, \
|
||||
capitalized_name, \
|
||||
YGEdgeHorizontal) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name##Vertical, \
|
||||
capitalized_name##Vertical, \
|
||||
capitalized_name, \
|
||||
YGEdgeVertical) \
|
||||
YG_VALUE_EDGE_PROPERTY( \
|
||||
lowercased_name, capitalized_name, capitalized_name, YGEdgeAll)
|
||||
|
||||
YGValue YGPointValue(CGFloat value) {
|
||||
return (YGValue){.value = value, .unit = YGUnitPoint};
|
||||
}
|
||||
|
||||
YGValue YGPercentValue(CGFloat value) {
|
||||
return (YGValue){.value = value, .unit = YGUnitPercent};
|
||||
}
|
||||
|
||||
static YGConfigRef globalConfig;
|
||||
#if TARGET_OS_OSX
|
||||
NS_INLINE CGFloat scaleFactor() { return [NSScreen mainScreen].backingScaleFactor; }
|
||||
#else
|
||||
NS_INLINE CGFloat scaleFactor() { return [UIScreen mainScreen].scale; }
|
||||
#endif
|
||||
|
||||
@interface YGLayout ()
|
||||
|
||||
@property(nonatomic, weak, readonly) UIView* view;
|
||||
@property(nonatomic, assign, readonly) BOOL isUIView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation YGLayout
|
||||
|
||||
@synthesize isEnabled = _isEnabled;
|
||||
@synthesize isIncludedInLayout = _isIncludedInLayout;
|
||||
@synthesize node = _node;
|
||||
|
||||
+ (void)initialize {
|
||||
globalConfig = YGConfigNew();
|
||||
YGConfigSetExperimentalFeatureEnabled(
|
||||
globalConfig, YGExperimentalFeatureWebFlexBasis, true);
|
||||
YGConfigSetPointScaleFactor(globalConfig, scaleFactor());
|
||||
}
|
||||
|
||||
- (instancetype)initWithView:(UIView*)view {
|
||||
if (self = [super init]) {
|
||||
_view = view;
|
||||
_node = YGNodeNewWithConfig(globalConfig);
|
||||
YGNodeSetContext(_node, (__bridge void*)view);
|
||||
_isEnabled = NO;
|
||||
_isIncludedInLayout = YES;
|
||||
_isUIView = [view isMemberOfClass:[UIView class]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
YGNodeFree(self.node);
|
||||
}
|
||||
|
||||
- (BOOL)isDirty {
|
||||
return YGNodeIsDirty(self.node);
|
||||
}
|
||||
|
||||
- (void)markDirty {
|
||||
if (self.isDirty || !self.isLeaf) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Yoga is not happy if we try to mark a node as "dirty" before we have set
|
||||
// the measure function. Since we already know that this is a leaf,
|
||||
// this *should* be fine. Forgive me Hack Gods.
|
||||
const YGNodeRef node = self.node;
|
||||
if (!YGNodeHasMeasureFunc(node)) {
|
||||
YGNodeSetMeasureFunc(node, YGMeasureView);
|
||||
}
|
||||
|
||||
YGNodeMarkDirty(node);
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfChildren {
|
||||
return YGNodeGetChildCount(self.node);
|
||||
}
|
||||
|
||||
- (BOOL)isLeaf {
|
||||
NSAssert(
|
||||
[NSThread isMainThread],
|
||||
@"This method must be called on the main thread.");
|
||||
if (self.isEnabled) {
|
||||
for (UIView* subview in self.view.subviews) {
|
||||
YGLayout* const yoga = subview.yoga;
|
||||
if (yoga.isEnabled && yoga.isIncludedInLayout) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Style
|
||||
|
||||
- (YGPositionType)position {
|
||||
return YGNodeStyleGetPositionType(self.node);
|
||||
}
|
||||
|
||||
- (void)setPosition:(YGPositionType)position {
|
||||
YGNodeStyleSetPositionType(self.node, position);
|
||||
}
|
||||
|
||||
YG_PROPERTY(YGDirection, direction, Direction)
|
||||
YG_PROPERTY(YGFlexDirection, flexDirection, FlexDirection)
|
||||
YG_PROPERTY(YGJustify, justifyContent, JustifyContent)
|
||||
YG_PROPERTY(YGAlign, alignContent, AlignContent)
|
||||
YG_PROPERTY(YGAlign, alignItems, AlignItems)
|
||||
YG_PROPERTY(YGAlign, alignSelf, AlignSelf)
|
||||
YG_PROPERTY(YGWrap, flexWrap, FlexWrap)
|
||||
YG_PROPERTY(YGOverflow, overflow, Overflow)
|
||||
YG_PROPERTY(YGDisplay, display, Display)
|
||||
|
||||
YG_PROPERTY(CGFloat, flex, Flex)
|
||||
YG_PROPERTY(CGFloat, flexGrow, FlexGrow)
|
||||
YG_PROPERTY(CGFloat, flexShrink, FlexShrink)
|
||||
YG_AUTO_VALUE_PROPERTY(flexBasis, FlexBasis)
|
||||
|
||||
YG_VALUE_EDGE_PROPERTY(left, Left, Position, YGEdgeLeft)
|
||||
YG_VALUE_EDGE_PROPERTY(top, Top, Position, YGEdgeTop)
|
||||
YG_VALUE_EDGE_PROPERTY(right, Right, Position, YGEdgeRight)
|
||||
YG_VALUE_EDGE_PROPERTY(bottom, Bottom, Position, YGEdgeBottom)
|
||||
YG_VALUE_EDGE_PROPERTY(start, Start, Position, YGEdgeStart)
|
||||
YG_VALUE_EDGE_PROPERTY(end, End, Position, YGEdgeEnd)
|
||||
YG_VALUE_EDGES_PROPERTIES(margin, Margin)
|
||||
YG_VALUE_EDGES_PROPERTIES(padding, Padding)
|
||||
|
||||
YG_EDGE_PROPERTY(borderLeftWidth, BorderLeftWidth, Border, YGEdgeLeft)
|
||||
YG_EDGE_PROPERTY(borderTopWidth, BorderTopWidth, Border, YGEdgeTop)
|
||||
YG_EDGE_PROPERTY(borderRightWidth, BorderRightWidth, Border, YGEdgeRight)
|
||||
YG_EDGE_PROPERTY(borderBottomWidth, BorderBottomWidth, Border, YGEdgeBottom)
|
||||
YG_EDGE_PROPERTY(borderStartWidth, BorderStartWidth, Border, YGEdgeStart)
|
||||
YG_EDGE_PROPERTY(borderEndWidth, BorderEndWidth, Border, YGEdgeEnd)
|
||||
YG_EDGE_PROPERTY(borderWidth, BorderWidth, Border, YGEdgeAll)
|
||||
|
||||
YG_AUTO_VALUE_PROPERTY(width, Width)
|
||||
YG_AUTO_VALUE_PROPERTY(height, Height)
|
||||
YG_VALUE_PROPERTY(minWidth, MinWidth)
|
||||
YG_VALUE_PROPERTY(minHeight, MinHeight)
|
||||
YG_VALUE_PROPERTY(maxWidth, MaxWidth)
|
||||
YG_VALUE_PROPERTY(maxHeight, MaxHeight)
|
||||
YG_PROPERTY(CGFloat, aspectRatio, AspectRatio)
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (YGDirection)resolvedDirection {
|
||||
return YGNodeLayoutGetDirection(self.node);
|
||||
}
|
||||
|
||||
- (void)applyLayout {
|
||||
[self calculateLayoutWithSize:self.view.bounds.size];
|
||||
YGApplyLayoutToViewHierarchy(self.view, NO);
|
||||
}
|
||||
|
||||
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin {
|
||||
[self calculateLayoutWithSize:self.view.bounds.size];
|
||||
YGApplyLayoutToViewHierarchy(self.view, preserveOrigin);
|
||||
}
|
||||
|
||||
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin
|
||||
dimensionFlexibility:
|
||||
(YGDimensionFlexibility)dimensionFlexibility {
|
||||
CGSize size = self.view.bounds.size;
|
||||
if (dimensionFlexibility & YGDimensionFlexibilityFlexibleWidth) {
|
||||
size.width = YGUndefined;
|
||||
}
|
||||
if (dimensionFlexibility & YGDimensionFlexibilityFlexibleHeight) {
|
||||
size.height = YGUndefined;
|
||||
}
|
||||
[self calculateLayoutWithSize:size];
|
||||
YGApplyLayoutToViewHierarchy(self.view, preserveOrigin);
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicSize {
|
||||
const CGSize constrainedSize = {
|
||||
.width = YGUndefined,
|
||||
.height = YGUndefined,
|
||||
};
|
||||
return [self calculateLayoutWithSize:constrainedSize];
|
||||
}
|
||||
|
||||
- (CGSize)calculateLayoutWithSize:(CGSize)size {
|
||||
NSAssert([NSThread isMainThread], @"Yoga calculation must be done on main.");
|
||||
NSAssert(self.isEnabled, @"Yoga is not enabled for this view.");
|
||||
|
||||
YGAttachNodesFromViewHierachy(self.view);
|
||||
|
||||
const YGNodeRef node = self.node;
|
||||
YGNodeCalculateLayout(
|
||||
node, size.width, size.height, YGNodeStyleGetDirection(node));
|
||||
|
||||
return (CGSize){
|
||||
.width = YGNodeLayoutGetWidth(node),
|
||||
.height = YGNodeLayoutGetHeight(node),
|
||||
};
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
static YGSize YGMeasureView(
|
||||
YGNodeRef node,
|
||||
float width,
|
||||
YGMeasureMode widthMode,
|
||||
float height,
|
||||
YGMeasureMode heightMode) {
|
||||
const CGFloat constrainedWidth =
|
||||
(widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width;
|
||||
const CGFloat constrainedHeight =
|
||||
(heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : height;
|
||||
|
||||
UIView* view = (__bridge UIView*)YGNodeGetContext(node);
|
||||
CGSize sizeThatFits = CGSizeZero;
|
||||
|
||||
// The default implementation of sizeThatFits: returns the existing size of
|
||||
// the view. That means that if we want to layout an empty UIView, which
|
||||
// already has got a frame set, its measured size should be CGSizeZero, but
|
||||
// UIKit returns the existing size.
|
||||
//
|
||||
// See https://github.com/facebook/yoga/issues/606 for more information.
|
||||
if (!view.yoga.isUIView || [view.subviews count] > 0) {
|
||||
#if TARGET_OS_OSX
|
||||
CGSize fittingSize = view.fittingSize;
|
||||
sizeThatFits = (CGSize){
|
||||
.width = fmin(constrainedWidth, fittingSize.width),
|
||||
.height = fmin(constrainedHeight, fittingSize.height)
|
||||
};
|
||||
#else
|
||||
sizeThatFits = [view sizeThatFits:(CGSize){
|
||||
.width = constrainedWidth,
|
||||
.height = constrainedHeight,
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
return (YGSize){
|
||||
.width = YGSanitizeMeasurement(
|
||||
constrainedWidth, sizeThatFits.width, widthMode),
|
||||
.height = YGSanitizeMeasurement(
|
||||
constrainedHeight, sizeThatFits.height, heightMode),
|
||||
};
|
||||
}
|
||||
|
||||
static CGFloat YGSanitizeMeasurement(
|
||||
CGFloat constrainedSize,
|
||||
CGFloat measuredSize,
|
||||
YGMeasureMode measureMode) {
|
||||
CGFloat result;
|
||||
if (measureMode == YGMeasureModeExactly) {
|
||||
result = constrainedSize;
|
||||
} else if (measureMode == YGMeasureModeAtMost) {
|
||||
result = MIN(constrainedSize, measuredSize);
|
||||
} else {
|
||||
result = measuredSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL YGNodeHasExactSameChildren(
|
||||
const YGNodeRef node,
|
||||
NSArray<UIView*>* subviews) {
|
||||
if (YGNodeGetChildCount(node) != subviews.count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (int i = 0; i < subviews.count; i++) {
|
||||
if (YGNodeGetChild(node, i) != subviews[i].yoga.node) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static void YGAttachNodesFromViewHierachy(UIView* const view) {
|
||||
YGLayout* const yoga = view.yoga;
|
||||
const YGNodeRef node = yoga.node;
|
||||
|
||||
// Only leaf nodes should have a measure function
|
||||
if (yoga.isLeaf) {
|
||||
YGRemoveAllChildren(node);
|
||||
YGNodeSetMeasureFunc(node, YGMeasureView);
|
||||
} else {
|
||||
YGNodeSetMeasureFunc(node, NULL);
|
||||
|
||||
NSMutableArray<UIView*>* subviewsToInclude =
|
||||
[[NSMutableArray alloc] initWithCapacity:view.subviews.count];
|
||||
for (UIView* subview in view.subviews) {
|
||||
if (subview.yoga.isEnabled && subview.yoga.isIncludedInLayout) {
|
||||
[subviewsToInclude addObject:subview];
|
||||
}
|
||||
}
|
||||
|
||||
if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) {
|
||||
YGRemoveAllChildren(node);
|
||||
for (int i = 0; i < subviewsToInclude.count; i++) {
|
||||
YGNodeInsertChild(node, subviewsToInclude[i].yoga.node, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (UIView* const subview in subviewsToInclude) {
|
||||
YGAttachNodesFromViewHierachy(subview);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void YGRemoveAllChildren(const YGNodeRef node) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
YGNodeRemoveAllChildren(node);
|
||||
}
|
||||
|
||||
static CGFloat YGRoundPixelValue(CGFloat value) {
|
||||
static CGFloat scale;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^() {
|
||||
scale = scaleFactor();
|
||||
});
|
||||
|
||||
return roundf(value * scale) / scale;
|
||||
}
|
||||
|
||||
static void YGApplyLayoutToViewHierarchy(UIView* view, BOOL preserveOrigin) {
|
||||
NSCAssert(
|
||||
[NSThread isMainThread],
|
||||
@"Framesetting should only be done on the main thread.");
|
||||
|
||||
const YGLayout* yoga = view.yoga;
|
||||
|
||||
if (!yoga.isIncludedInLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
YGNodeRef node = yoga.node;
|
||||
const CGPoint topLeft = {
|
||||
YGNodeLayoutGetLeft(node),
|
||||
YGNodeLayoutGetTop(node),
|
||||
};
|
||||
|
||||
const CGPoint bottomRight = {
|
||||
topLeft.x + YGNodeLayoutGetWidth(node),
|
||||
topLeft.y + YGNodeLayoutGetHeight(node),
|
||||
};
|
||||
|
||||
const CGPoint origin = preserveOrigin ? view.frame.origin : CGPointZero;
|
||||
view.frame = (CGRect){
|
||||
.origin =
|
||||
{
|
||||
.x = YGRoundPixelValue(topLeft.x + origin.x),
|
||||
.y = YGRoundPixelValue(topLeft.y + origin.y),
|
||||
},
|
||||
.size =
|
||||
{
|
||||
.width = YGRoundPixelValue(bottomRight.x) -
|
||||
YGRoundPixelValue(topLeft.x),
|
||||
.height = YGRoundPixelValue(bottomRight.y) -
|
||||
YGRoundPixelValue(topLeft.y),
|
||||
},
|
||||
};
|
||||
|
||||
if (!yoga.isLeaf) {
|
||||
for (NSUInteger i = 0; i < view.subviews.count; i++) {
|
||||
YGApplyLayoutToViewHierarchy(view.subviews[i], NO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user