Extract ASLayoutElement and ASLayoutElementStylability into categories #trivial (#131)

* Initial move of code into layout category

* Cleanup

* Some more
This commit is contained in:
Michael Schneider 2017-05-02 14:38:06 -07:00 committed by GitHub
parent c671d2c6c6
commit b6734faa3b
10 changed files with 165 additions and 163 deletions

View File

@ -121,7 +121,7 @@ extern NSInteger const ASDefaultDrawingPriority;
* *
*/ */
@interface ASDisplayNode : NSObject <ASLayoutElement, ASLayoutElementStylability> @interface ASDisplayNode : NSObject <ASLayoutElementFinalLayoutElement>
/** @name Initializing a node object */ /** @name Initializing a node object */
@ -298,23 +298,6 @@ extern NSInteger const ASDefaultDrawingPriority;
/** @name Managing dimensions */ /** @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 * @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
* implement layoutSpecThatFits: * implement layoutSpecThatFits:
@ -755,6 +738,31 @@ extern NSInteger const ASDefaultDrawingPriority;
@end @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) @interface ASDisplayNode (LayoutTransitioning)
/** /**

View File

@ -859,40 +859,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
+ (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \ + (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)) @"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> #pragma mark <ASLayoutElementTransition>
- (ASLayoutElementStyle *)style
{
ASDN::MutexLocker l(__instanceLock__);
if (_style == nil) {
_style = [[ASLayoutElementStyle alloc] init];
}
return _style;
}
- (ASLayoutElementType)layoutElementType
{
return ASLayoutElementTypeDisplayNode;
}
- (BOOL)canLayoutAsynchronous - (BOOL)canLayoutAsynchronous
{ {
return !self.isNodeLoaded; 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 ASLayoutElementFinalLayoutElementDefault
#pragma mark <ASDebugNameProvider>
- (NSString *)debugName - (NSString *)debugName
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
@ -907,47 +884,6 @@ ASLayoutElementFinalLayoutElementDefault
} }
} }
#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 #pragma mark Layout Pass
- (void)__setNeedsLayout - (void)__setNeedsLayout
@ -1297,7 +1233,6 @@ ASLayoutElementFinalLayoutElementDefault
_calculatedDisplayNodeLayout = displayNodeLayout; _calculatedDisplayNodeLayout = displayNodeLayout;
} }
- (CGSize)calculatedSize - (CGSize)calculatedSize
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
@ -4063,46 +3998,13 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
} }
} }
#pragma mark - ASPrimitiveTraitCollection #pragma mark - Trait Collection Hooks
- (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];
}
- (void)asyncTraitCollectionDidChange - (void)asyncTraitCollectionDidChange
{ {
// Subclass override // Subclass override
} }
ASPrimitiveTraitCollectionDeprecatedImplementation
#pragma mark - ASLayoutElementStyleExtensibility
ASLayoutElementStyleExtensibilityForwarding
#if TARGET_OS_TV #if TARGET_OS_TV
#pragma mark - UIFocusEnvironment Protocol (tvOS) #pragma mark - UIFocusEnvironment Protocol (tvOS)
@ -4136,23 +4038,125 @@ ASLayoutElementStyleExtensibilityForwarding
} }
#endif #endif
#pragma mark - Deprecated @end
// This methods cannot be moved into the category ASDisplayNode (Deprecated). So they need to be declared in ASDisplayNode until removed #pragma mark - ASDisplayNode (ASLayoutElement)
@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 - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{ {
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; 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 @end
@ -4298,6 +4302,16 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
return isPoints ? CGSizeMake(size.width.value, size.height.value) : CGSizeZero; 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 - (CGSize)measure:(CGSize)constrainedSize
{ {
return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;

View File

@ -21,6 +21,7 @@
@class ASTraitCollection; @class ASTraitCollection;
@protocol ASLayoutElement; @protocol ASLayoutElement;
@protocol ASTraitEnvironment;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -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 * This function will walk the layout element hierarchy and updates the layout element trait collection for every
* layout element within the hierarchy. * 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 /// For backward compatibility reasons we redefine the old layout element trait collection struct name
#define ASEnvironmentTraitCollection ASPrimitiveTraitCollection #define ASEnvironmentTraitCollection ASPrimitiveTraitCollection
@ -88,7 +89,6 @@ ASDISPLAYNODE_EXTERN_C_END
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection; - (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection;
/** /**
* Returns an NSObject-representation of the environment's ASEnvironmentDisplayTraits
*/ */
- (ASTraitCollection *)asyncTraitCollection; - (ASTraitCollection *)asyncTraitCollection;

View File

@ -22,10 +22,14 @@
#pragma mark - ASPrimitiveTraitCollection #pragma mark - ASPrimitiveTraitCollection
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> root, ASPrimitiveTraitCollection traitCollection) { extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection) {
ASLayoutElementPerformBlockOnEveryElement(root, ^(id<ASLayoutElement> _Nonnull element) { if (element) {
element.primitiveTraitCollection = traitCollection; element.primitiveTraitCollection = traitCollection;
}); }
for (id<ASLayoutElement> subelement in element.sublayoutElements) {
ASTraitCollectionPropagateDown(subelement, traitCollection);
}
} }
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault()

View File

@ -42,16 +42,6 @@ typedef NS_ENUM(NSUInteger, ASLayoutElementType) {
ASLayoutElementTypeDisplayNode ASLayoutElementTypeDisplayNode
}; };
ASDISPLAYNODE_EXTERN_C_BEGIN
/**
This function will walk the layout element hierarchy. It does run the block on the node provided
directly to the function call.
*/
extern void ASLayoutElementPerformBlockOnEveryElement(id<ASLayoutElement> root, void(^block)(id<ASLayoutElement> element));
ASDISPLAYNODE_EXTERN_C_END
#pragma mark - ASLayoutElement #pragma mark - ASLayoutElement
/** /**
@ -70,7 +60,7 @@ ASDISPLAYNODE_EXTERN_C_END
* access to the options via convenience properties. If you are creating custom layout spec, then you can * access to the options via convenience properties. If you are creating custom layout spec, then you can
* extend the backing layout options class to accommodate any new layout options. * extend the backing layout options class to accommodate any new layout options.
*/ */
@protocol ASLayoutElement <ASLayoutElementExtensibility, ASLayoutElementFinalLayoutElement, ASTraitEnvironment> @protocol ASLayoutElement <ASLayoutElementExtensibility, ASTraitEnvironment>
#pragma mark - Getter #pragma mark - Getter

View File

@ -29,17 +29,6 @@
#import YOGA_HEADER_PATH #import YOGA_HEADER_PATH
#endif #endif
extern void ASLayoutElementPerformBlockOnEveryElement(id<ASLayoutElement> element, void(^block)(id<ASLayoutElement> element))
{
if (element) {
block(element);
}
for (id<ASLayoutElement> subelement in element.sublayoutElements) {
ASLayoutElementPerformBlockOnEveryElement(subelement, block);
}
}
#pragma mark - ASLayoutElementContext #pragma mark - ASLayoutElementContext
CGFloat const ASLayoutElementParentDimensionUndefined = NAN; CGFloat const ASLayoutElementParentDimensionUndefined = NAN;

View File

@ -58,7 +58,7 @@
#pragma mark - Final layoutElement #pragma mark - Final layoutElement
- (id<ASLayoutElement>)layoutElementToAddFromLayoutElement:(id<ASLayoutElement>)child - (id<ASLayoutElement>)layoutElementToAddFromLayoutElement:(id<ASLayoutElement, ASLayoutElementFinalLayoutElement>)child
{ {
if (self.isFinalLayoutElement == NO) { if (self.isFinalLayoutElement == NO) {
id<ASLayoutElement> finalLayoutElement = [child finalLayoutElement]; id<ASLayoutElement> finalLayoutElement = [child finalLayoutElement];

View File

@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* A layout spec is an immutable object that describes a layout, loosely inspired by React. * A layout spec is an immutable object that describes a layout, loosely inspired by React.
*/ */
@interface ASLayoutSpec : NSObject <ASLayoutElement, ASLayoutElementStylability, NSFastEnumeration> @interface ASLayoutSpec : NSObject <ASLayoutElement, ASLayoutElementFinalLayoutElement, ASLayoutElementStylability, NSFastEnumeration>
/** /**
* Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a

View File

@ -293,17 +293,14 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
*/ */
- (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy; - (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy;
/**
* Convenience method to access this node's trait collection struct. Externally, users should interact
* with the trait collection via ASTraitCollection
*/
- (ASPrimitiveTraitCollection)primitiveTraitCollection;
/** /**
* Whether this node rasterizes its descendants. See -enableSubtreeRasterization. * Whether this node rasterizes its descendants. See -enableSubtreeRasterization.
*/ */
@property (atomic, readonly) BOOL rasterizesSubtree; @property (atomic, readonly) BOOL rasterizesSubtree;
/**
* Called if a gesture recognizer was attached to an _ASDisplayView
*/
- (void)nodeViewDidAddGestureRecognizer; - (void)nodeViewDidAddGestureRecognizer;
@end @end

View File

@ -30,9 +30,9 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASLayoutElementTransition #pragma mark - ASLayoutElementTransition
/** /**
* Extend the layout element protocol to check if a the element can layout asynchronously. * Objects conform to this project returns if it's possible to layout asynchronous
*/ */
@protocol ASLayoutElementTransition <ASLayoutElement> @protocol ASLayoutElementTransition <NSObject>
/** /**
* @abstract Returns if the layoutElement can be used to layout in an asynchronous way on a background thread. * @abstract Returns if the layoutElement can be used to layout in an asynchronous way on a background thread.