ComponentKit/Core/CKComponentLayout.mm (104 lines of code) (raw):
/*
* 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 "CKComponentLayout.h"
#import <ComponentKit/CKAnalyticsListener.h>
#import <ComponentKit/CKComponentAnimationPredicates.h>
#import <ComponentKit/CKComponentInternal.h>
#import <ComponentKit/CKComponentSubclass.h>
#import <ComponentKit/CKTreeVerificationHelpers.h>
#import <ComponentKit/ComponentLayoutContext.h>
#import <ComponentKit/CKComponentScopeRoot.h>
#import <pthread.h>
NSSet<id<CKMountable>> *CKMountComponentLayout(const RCLayout &layout,
UIView *view,
NSSet<id<CKMountable>> *previouslyMountedComponents,
id<CKMountable> supercomponent,
id<CKAnalyticsListener> analyticsListener)
{
((CKComponent *)layout.component).rootComponentMountedView = view;
[analyticsListener willMountComponentTreeWithRootComponent:layout.component];
CK::Component::MountAnalyticsContext mountAnalyticsContext;
const BOOL collectMountAnalytics =
[analyticsListener shouldCollectMountInformationForRootComponent:layout.component];
NSSet<id<CKMountable>> *const mountedComponents =
CKMountLayout(layout,
view,
previouslyMountedComponents,
supercomponent,
collectMountAnalytics ? &mountAnalyticsContext : nullptr,
analyticsListener.systraceListener);
[analyticsListener
didMountComponentTreeWithRootComponent:layout.component
mountAnalyticsContext:
collectMountAnalytics
? CK::Optional<CK::Component::MountAnalyticsContext> {mountAnalyticsContext}
: CK::none];
return mountedComponents;
}
static auto buildComponentsByPredicateMap(const RCLayout &layout,
const std::unordered_set<CKMountablePredicate> &predicates)
{
auto componentsByPredicate = CKComponentRootLayout::ComponentsByPredicateMap {};
if (predicates.empty()) {
return componentsByPredicate;
}
layout.enumerateLayouts([&](const auto &l){
if (l.component == nil) { return; }
for (const auto &p : predicates) {
if (p(l.component)) {
componentsByPredicate[p].push_back(l.component);
}
}
});
return componentsByPredicate;
}
CKComponentRootLayout CKComputeRootComponentLayout(id<CKMountable> rootComponent,
const CKSizeRange &sizeRange,
id<CKAnalyticsListener> analyticsListener,
CK::Optional<CKBuildTrigger> buildTrigger,
CKComponentScopeRoot *scopeRoot,
std::shared_ptr<RCLayoutCache> layoutCache)
{
[analyticsListener willLayoutComponentTreeWithRootComponent:rootComponent buildTrigger:buildTrigger];
CK::Component::LayoutSystraceContext systraceContext([analyticsListener systraceListener]);
RCLayoutResult layoutResult;
if (layoutCache) {
layoutResult = RCComputeRootLayout(rootComponent, sizeRange, layoutCache);
} else {
layoutResult = {CKComputeComponentLayout(rootComponent, sizeRange, sizeRange.max), nil};
}
auto layoutLookup = CKComponentRootLayout::ComponentLayoutCache {};
layoutResult.layout.enumerateLayouts([&](const auto &l){
if ([l.component isKindOfClass:[CKComponent class]] && ((CKComponent *)l.component).controller) {
layoutLookup[l.component] = l;
}
});
const auto componentsByPredicate = buildComponentsByPredicateMap(layoutResult.layout, CKComponentAnimationPredicates());
const auto rootLayout = CKComponentRootLayout {
layoutResult,
layoutLookup,
componentsByPredicate,
};
CKDetectDuplicateComponent(rootLayout.layout());
CKVerifyTreeNodesToParentLinks(scopeRoot, rootLayout.layout());
[analyticsListener didLayoutComponentTreeWithRootComponent:rootComponent];
return rootLayout;
}
RCLayout CKComputeComponentLayout(id<CKMountable> component,
const CKSizeRange &sizeRange,
const CGSize parentSize)
{
return component ? [component layoutThatFits:sizeRange parentSize:parentSize] : (RCLayout){};
}
void RCLayout::enumerateLayouts(const std::function<void(const RCLayout &)> &f) const
{
f(*this);
if (children == nil) { return; }
for (const auto &child : *children) {
child.layout.enumerateLayouts(f);
}
}
void CKComponentRootLayout::enumerateCachedLayout(void(^ _Nonnull block)(const RCLayout &layout)) const
{
for (const auto &it : _layoutCache) {
block(it.second);
}
}