forked from TextureGroup/Texture
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathASTLayoutFixture.mm
139 lines (118 loc) · 4.17 KB
/
ASTLayoutFixture.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//
// ASTLayoutFixture.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "ASTLayoutFixture.h"
@interface ASTLayoutFixture ()
/// The size ranges against which nodes are expected to be measured.
@property (nonatomic, readonly) NSMapTable<ASDisplayNode *, NSMutableArray<NSValue *> *> *sizeRanges;
/// The overridden returned sizes for nodes where you want to trigger multipass layout.
@property (nonatomic, readonly) NSMapTable<ASDisplayNode *, NSValue *> *returnedSizes;
@end
@implementation ASTLayoutFixture
- (instancetype)init
{
if (self = [super init]) {
_sizeRanges = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality valueOptions:NSMapTableStrongMemory];
_layoutSpecBlocks = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality valueOptions:NSMapTableStrongMemory];
_returnedSizes = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality valueOptions:NSMapTableStrongMemory];
}
return self;
}
- (void)addSizeRange:(ASSizeRange)sizeRange forNode:(ASLayoutTestNode *)node
{
auto ranges = [_sizeRanges objectForKey:node];
if (ranges == nil) {
ranges = [[NSMutableArray alloc] init];
[_sizeRanges setObject:ranges forKey:node];
}
[ranges addObject:[NSValue valueWithBytes:&sizeRange objCType:@encode(ASSizeRange)]];
}
- (void)setReturnedSize:(CGSize)size forNode:(ASLayoutTestNode *)node
{
[_returnedSizes setObject:[NSValue valueWithCGSize:size] forKey:node];
}
- (ASSizeRange)firstSizeRangeForNode:(ASLayoutTestNode *)node
{
const auto val = [_sizeRanges objectForKey:node].firstObject;
ASSizeRange r;
[val getValue:&r];
return r;
}
- (void)withSizeRangesForAllNodesUsingBlock:(void (^)(ASLayoutTestNode * _Nonnull, ASSizeRange))block
{
for (ASLayoutTestNode *node in self.allNodes) {
[self withSizeRangesForNode:node block:^(ASSizeRange sizeRange) {
block(node, sizeRange);
}];
}
}
- (void)withSizeRangesForNode:(ASLayoutTestNode *)node block:(void (^)(ASSizeRange))block
{
for (NSValue *value in [_sizeRanges objectForKey:node]) {
ASSizeRange r;
[value getValue:&r];
block(r);
}
}
- (ASLayout *)layoutForNode:(ASLayoutTestNode *)node
{
NSMutableArray *allLayouts = [NSMutableArray array];
[ASTLayoutFixture collectAllLayoutsFromLayout:self.layout array:allLayouts];
for (ASLayout *layout in allLayouts) {
if (layout.layoutElement == node) {
return layout;
}
}
return nil;
}
/// A very dumb tree iteration approach. NSEnumerator or something would be way better.
+ (void)collectAllLayoutsFromLayout:(ASLayout *)layout array:(NSMutableArray<ASLayout *> *)array
{
[array addObject:layout];
for (ASLayout *sublayout in layout.sublayouts) {
[self collectAllLayoutsFromLayout:sublayout array:array];
}
}
- (ASLayoutTestNode *)rootNode
{
return (ASLayoutTestNode *)self.layout.layoutElement;
}
- (NSSet<ASLayoutTestNode *> *)allNodes
{
const auto allLayouts = [NSMutableArray array];
[ASTLayoutFixture collectAllLayoutsFromLayout:self.layout array:allLayouts];
return [NSSet setWithArray:[allLayouts valueForKey:@"layoutElement"]];
}
- (void)apply
{
// Update layoutSpecBlock for parent nodes, set automatic subnode management
for (ASDisplayNode *node in _layoutSpecBlocks) {
const auto block = [_layoutSpecBlocks objectForKey:node];
if (node.layoutSpecBlock != block) {
node.automaticallyManagesSubnodes = YES;
node.layoutSpecBlock = block;
[node setNeedsLayout];
}
}
[self setTestSizesOfLeafNodesInLayout:self.layout];
}
/// Go through the given layout, and for all the leaf nodes, set their preferredSize
/// to the layout size if needed, then call -setNeedsLayout
- (void)setTestSizesOfLeafNodesInLayout:(ASLayout *)layout
{
const auto node = (ASLayoutTestNode *)layout.layoutElement;
if (layout.sublayouts.count == 0) {
const auto override = [self.returnedSizes objectForKey:node];
node.testSize = override ? override.CGSizeValue : layout.size;
} else {
node.testSize = CGSizeZero;
for (ASLayout *sublayout in layout.sublayouts) {
[self setTestSizesOfLeafNodesInLayout:sublayout];
}
}
}
@end