diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 937f6c76..022bbb61 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -25,19 +25,9 @@ - (void)setValuesFromLayoutable:(id)layoutable; #pragma mark - Subclasses should implement these! -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor; - (void)setupDefaults; -- (instancetype)copyWithZone:(NSZone *)zone; - (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; -#pragma mark - Mutability checks - -@property (nonatomic, assign) BOOL isMutable; - -#if DEBUG -@property (nonatomic, assign) NSUInteger changeMonitor; -#endif - #pragma mark - ASStackLayoutable @property (nonatomic, readwrite) CGFloat spacingBefore; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m deleted file mode 100644 index 9d8d9c6a..00000000 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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. - * - */ - -#import "ASLayoutOptions.h" - -#import -#import -#import "ASInternalHelpers.h" - -@implementation ASLayoutOptions - -static Class gDefaultLayoutOptionsClass = nil; -+ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass -{ - gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; -} - -+ (Class)defaultLayoutOptionsClass -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (gDefaultLayoutOptionsClass == nil) { - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; - } - }); - return gDefaultLayoutOptionsClass; -} - -- (instancetype)init -{ - return [self initWithLayoutable:nil]; -} - -- (instancetype)initWithLayoutable:(id)layoutable; -{ - self = [super init]; - if (self) { - [self setupDefaults]; - [self setValuesFromLayoutable:layoutable]; - _isMutable = YES; -#if DEBUG - [self addObserver:self - forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; -#endif - } - return self; -} - -- (void)dealloc -{ -#if DEBUG - [self removeObserver:self forKeyPath:@"changeMonitor"]; -#endif -} - -#if DEBUG -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor -{ - NSMutableSet *keys = [NSMutableSet set]; - [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; - [keys addObjectsFromArray:@[@"ascender", @"descender"]]; - [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; - - return keys; -} - -#endif - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ -#if DEBUG - if ([keyPath isEqualToString:@"changeMonitor"]) { - ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); - } else -#endif - { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - - -#pragma mark - NSCopying -- (id)copyWithZone:(NSZone *)zone -{ - ASLayoutOptions *copy = [[[self class] alloc] init]; - [self copyIntoOptions:copy]; - return copy; -} - -- (void)copyIntoOptions:(ASLayoutOptions *)copy -{ - copy.flexBasis = self.flexBasis; - copy.spacingAfter = self.spacingAfter; - copy.spacingBefore = self.spacingBefore; - copy.flexGrow = self.flexGrow; - copy.flexShrink = self.flexShrink; - - copy.ascender = self.ascender; - copy.descender = self.descender; - - copy.sizeRange = self.sizeRange; - copy.layoutPosition = self.layoutPosition; -} - - -#pragma mark - Defaults -- (void)setupDefaults -{ - _flexBasis = ASRelativeDimensionUnconstrained; - _spacingBefore = 0; - _spacingAfter = 0; - _flexGrow = NO; - _flexShrink = NO; - _alignSelf = ASStackLayoutAlignSelfAuto; - - _ascender = 0; - _descender = 0; - - _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _layoutPosition = CGPointZero; -} - -// Do this here instead of in Node/Spec subclasses so that custom specs can set default values -- (void)setValuesFromLayoutable:(id)layoutable -{ - if ([layoutable isKindOfClass:[ASTextNode class]]) { - ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); - } - if ([layoutable isKindOfClass:[ASDisplayNode class]]) { - ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; - self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.layoutPosition = displayNode.frame.origin; - } -} - - -@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm new file mode 100644 index 00000000..ad6d2244 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -0,0 +1,250 @@ +/* + * 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. + * + */ + +#import "ASLayoutOptions.h" + +#import +#import +#import +#import "ASInternalHelpers.h" + +@interface ASLayoutOptions() +{ + ASDN::RecursiveMutex _propertyLock; +} +@end + +@implementation ASLayoutOptions + +@synthesize spacingBefore = _spacingBefore; +@synthesize spacingAfter = _spacingAfter; +@synthesize flexGrow = _flexGrow; +@synthesize flexShrink = _flexShrink; +@synthesize flexBasis = _flexBasis; +@synthesize alignSelf = _alignSelf; + +@synthesize ascender = _ascender; +@synthesize descender = _descender; + +@synthesize sizeRange = _sizeRange; +@synthesize layoutPosition = _layoutPosition; + +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } + }); + return gDefaultLayoutOptionsClass; +} + +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; + } + return self; +} + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} + +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ + ASDN::MutexLocker l(_propertyLock); + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.layoutPosition = self.layoutPosition; +} + + +#pragma mark - Defaults +- (void)setupDefaults +{ + self.flexBasis = ASRelativeDimensionUnconstrained; + self.spacingBefore = 0; + self.spacingAfter = 0; + self.flexGrow = NO; + self.flexShrink = NO; + self.alignSelf = ASStackLayoutAlignSelfAuto; + + self.ascender = 0; + self.descender = 0; + + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + self.layoutPosition = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + ASDN::MutexLocker l(_propertyLock); + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.layoutPosition = displayNode.frame.origin; + } +} + +- (CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingAfter; +} + +- (void)setSpacingAfter:(CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + _spacingAfter = spacingAfter; +} + +- (CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingBefore; +} + +- (void)setSpacingBefore:(CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + _spacingBefore = spacingBefore; +} + +- (BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + return _flexGrow; +} + +- (void)setFlexGrow:(BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + _flexGrow = flexGrow; +} + +- (BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + return _flexShrink; +} + +- (void)setFlexShrink:(BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + _flexShrink = flexShrink; +} + +- (ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + return _flexBasis; +} + +- (void)setFlexBasis:(ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + _flexBasis = flexBasis; +} + +- (ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + return _alignSelf; +} + +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + _alignSelf = alignSelf; +} + +- (CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + return _ascender; +} + +- (void)setAscender:(CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + _ascender = ascender; +} + +- (CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + return _descender; +} + +- (void)setDescender:(CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + _descender = descender; +} + +- (ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + return _sizeRange; +} + +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + _sizeRange = sizeRange; +} + +- (CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + return _layoutPosition; +} + +- (void)setLayoutPosition:(CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + _layoutPosition = layoutPosition; +} + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 6c506b65..0985014b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -62,12 +62,10 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { ASLayoutOptions *layoutOptions = [child layoutOptions]; - layoutOptions.isMutable = NO; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; - finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child;