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

Extract ASLayoutElement and ASLayoutElementStylability into categories #trivial #131

Merged
merged 3 commits into from
May 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ extern NSInteger const ASDefaultDrawingPriority;
*
*/

@interface ASDisplayNode : NSObject <ASLayoutElement, ASLayoutElementStylability>
@interface ASDisplayNode : NSObject <ASLayoutElementFinalLayoutElement>

/** @name Initializing a node object */

Expand Down Expand Up @@ -298,23 +298,6 @@ extern NSInteger const ASDefaultDrawingPriority;

/** @name Managing dimensions */

/**
* @abstract Asks the node to return a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;

/**
* @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
* implement layoutSpecThatFits:
Expand Down Expand Up @@ -755,6 +738,31 @@ extern NSInteger const ASDefaultDrawingPriority;

@end

@interface ASDisplayNode (ASLayoutElement) <ASLayoutElement>

/**
* @abstract Asks the node to return a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;

@end

@interface ASDisplayNode (ASLayoutElementStylability) <ASLayoutElementStylability>

@end

@interface ASDisplayNode (LayoutTransitioning)

/**
Expand Down
230 changes: 122 additions & 108 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -859,40 +859,17 @@ - (void)nodeViewDidAddGestureRecognizer
+ (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \
@"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class))

#pragma mark <ASLayoutElement>

- (ASLayoutElementStyle *)style
{
ASDN::MutexLocker l(__instanceLock__);
if (_style == nil) {
_style = [[ASLayoutElementStyle alloc] init];
}
return _style;
}

- (ASLayoutElementType)layoutElementType
{
return ASLayoutElementTypeDisplayNode;
}
#pragma mark <ASLayoutElementTransition>

- (BOOL)canLayoutAsynchronous
{
return !self.isNodeLoaded;
}

- (NSArray<id<ASLayoutElement>> *)sublayoutElements
{
return self.subnodes;
}

- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock
{
styleBlock(self.style);
return self;
}

ASLayoutElementFinalLayoutElementDefault

#pragma mark <ASDebugNameProvider>

- (NSString *)debugName
{
ASDN::MutexLocker l(__instanceLock__);
Expand All @@ -907,47 +884,6 @@ - (void)setDebugName:(NSString *)debugName
}
}

#pragma mark Measurement Pass

- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// For now we just call the deprecated measureWithSizeRange: method to not break old API
return [self measureWithSizeRange:constrainedSize];
#pragma clang diagnostic pop
}

- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
{
ASDN::MutexLocker l(__instanceLock__);

// If one or multiple layout transitions are in flight it still can happen that layout information is requested
// on other threads. As the pending and calculated layout to be updated in the layout transition in here just a
// layout calculation wil be performed without side effect
if ([self _isLayoutTransitionInvalid]) {
return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];
}

if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) {
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self);
// Our calculated layout is suitable for this constrainedSize, so keep using it and
// invalidate any pending layout that has been generated in the past.
_pendingDisplayNodeLayout = nullptr;
return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
}

// Create a pending display node layout for the layout pass
_pendingDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(
[self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize],
constrainedSize,
parentSize
);

ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self);
return _pendingDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
}

#pragma mark Layout Pass

- (void)__setNeedsLayout
Expand Down Expand Up @@ -1297,7 +1233,6 @@ - (void)_locked_setCalculatedDisplayNodeLayout:(std::shared_ptr<ASDisplayNodeLay
_calculatedDisplayNodeLayout = displayNodeLayout;
}


- (CGSize)calculatedSize
{
ASDN::MutexLocker l(__instanceLock__);
Expand Down Expand Up @@ -4063,46 +3998,13 @@ - (CGRect)_frameInWindow
}
}

#pragma mark - ASPrimitiveTraitCollection

- (ASPrimitiveTraitCollection)primitiveTraitCollection
{
ASDN::MutexLocker l(__instanceLock__);
return _primitiveTraitCollection;
}

- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection
{
__instanceLock__.lock();
if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) {
_primitiveTraitCollection = traitCollection;
ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection));
__instanceLock__.unlock();

[self asyncTraitCollectionDidChange];
return;
}

__instanceLock__.unlock();
}

- (ASTraitCollection *)asyncTraitCollection
{
ASDN::MutexLocker l(__instanceLock__);
return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection];
}
#pragma mark - Trait Collection Hooks

- (void)asyncTraitCollectionDidChange
{
// Subclass override
}

ASPrimitiveTraitCollectionDeprecatedImplementation

#pragma mark - ASLayoutElementStyleExtensibility

ASLayoutElementStyleExtensibilityForwarding

#if TARGET_OS_TV
#pragma mark - UIFocusEnvironment Protocol (tvOS)

Expand Down Expand Up @@ -4136,23 +4038,125 @@ - (UIView *)preferredFocusedView
}
#endif

#pragma mark - Deprecated
@end

#pragma mark - ASDisplayNode (ASLayoutElement)

// This methods cannot be moved into the category ASDisplayNode (Deprecated). So they need to be declared in ASDisplayNode until removed
@implementation ASDisplayNode (ASLayoutElement)

#pragma mark <ASLayoutElement>

- (ASLayoutElementStyle *)style
{
ASDN::MutexLocker l(__instanceLock__);
if (_style == nil) {
_style = [[ASLayoutElementStyle alloc] init];
}
return _style;
}

- (ASLayoutElementType)layoutElementType
{
return ASLayoutElementTypeDisplayNode;
}

- (NSArray<id<ASLayoutElement>> *)sublayoutElements
{
return self.subnodes;
}

#pragma mark Measurement Pass

- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// For now we just call the deprecated measureWithSizeRange: method to not break old API
return [self measureWithSizeRange:constrainedSize];
#pragma clang diagnostic pop
}

- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
}

- (BOOL)usesImplicitHierarchyManagement
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
{
return self.automaticallyManagesSubnodes;
ASDN::MutexLocker l(__instanceLock__);

// If one or multiple layout transitions are in flight it still can happen that layout information is requested
// on other threads. As the pending and calculated layout to be updated in the layout transition in here just a
// layout calculation wil be performed without side effect
if ([self _isLayoutTransitionInvalid]) {
return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];
}

if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) {
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self);
// Our calculated layout is suitable for this constrainedSize, so keep using it and
// invalidate any pending layout that has been generated in the past.
_pendingDisplayNodeLayout = nullptr;
return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
}

// Create a pending display node layout for the layout pass
_pendingDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(
[self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize],
constrainedSize,
parentSize
);

ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self);
return _pendingDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
}

- (void)setUsesImplicitHierarchyManagement:(BOOL)enabled
#pragma mark ASLayoutElementStyleExtensibility

ASLayoutElementStyleExtensibilityForwarding

#pragma mark ASPrimitiveTraitCollection

- (ASPrimitiveTraitCollection)primitiveTraitCollection
{
self.automaticallyManagesSubnodes = enabled;
ASDN::MutexLocker l(__instanceLock__);
return _primitiveTraitCollection;
}

- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection
{
__instanceLock__.lock();
if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) {
_primitiveTraitCollection = traitCollection;
ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection));
__instanceLock__.unlock();

[self asyncTraitCollectionDidChange];
return;
}

__instanceLock__.unlock();
}

- (ASTraitCollection *)asyncTraitCollection
{
ASDN::MutexLocker l(__instanceLock__);
return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection];
}

ASPrimitiveTraitCollectionDeprecatedImplementation

@end


#pragma mark - ASDisplayNode (ASLayoutElementStylability)

@implementation ASDisplayNode (ASLayoutElementStylability)

- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock
{
styleBlock(self.style);
return self;
}

@end
Expand Down Expand Up @@ -4298,6 +4302,16 @@ - (CGSize)preferredFrameSize
return isPoints ? CGSizeMake(size.width.value, size.height.value) : CGSizeZero;
}

- (BOOL)usesImplicitHierarchyManagement
{
return self.automaticallyManagesSubnodes;
}

- (void)setUsesImplicitHierarchyManagement:(BOOL)enabled
{
self.automaticallyManagesSubnodes = enabled;
}

- (CGSize)measure:(CGSize)constrainedSize
{
return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
Expand Down
4 changes: 2 additions & 2 deletions Source/Details/ASTraitCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

@class ASTraitCollection;
@protocol ASLayoutElement;
@protocol ASTraitEnvironment;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -63,7 +64,7 @@ extern NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollecti
* This function will walk the layout element hierarchy and updates the layout element trait collection for every
* layout element within the hierarchy.
*/
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> root, ASPrimitiveTraitCollection traitCollection);
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection);

/// For backward compatibility reasons we redefine the old layout element trait collection struct name
#define ASEnvironmentTraitCollection ASPrimitiveTraitCollection
Expand All @@ -88,7 +89,6 @@ ASDISPLAYNODE_EXTERN_C_END
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection;

/**
* Returns an NSObject-representation of the environment's ASEnvironmentDisplayTraits
*/
- (ASTraitCollection *)asyncTraitCollection;

Expand Down
Loading