mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Implement horizontal and vertical alignments for stack layout
This commit is contained in:
@@ -67,3 +67,27 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
|
||||
/** Expand to fill cross axis */
|
||||
ASStackLayoutAlignSelfStretch,
|
||||
};
|
||||
|
||||
/** Orientation of children along horizontal axis */
|
||||
typedef NS_ENUM(NSUInteger, ASHorizontalAlignment) {
|
||||
/** No alignment specified. Default value */
|
||||
ASHorizontalAlignmentNone,
|
||||
/** Left aligned */
|
||||
ASAlignmentLeft,
|
||||
/** Center aligned */
|
||||
ASAlignmentMiddle,
|
||||
/** Right aligned */
|
||||
ASAlignmentRight,
|
||||
};
|
||||
|
||||
/** Orientation of children along vertical axis */
|
||||
typedef NS_ENUM(NSUInteger, ASVerticalAlignment) {
|
||||
/** No alignment specified. Default value */
|
||||
ASVerticalAlignmentNone,
|
||||
/** Top aligned */
|
||||
ASAlignmentTop,
|
||||
/** Center aligned */
|
||||
ASAlignmentCenter,
|
||||
/** Bottom aligned */
|
||||
ASAlignmentBottom,
|
||||
};
|
||||
|
||||
@@ -35,10 +35,25 @@
|
||||
*/
|
||||
@interface ASStackLayoutSpec : ASLayoutSpec
|
||||
|
||||
/** Specifies the direction children are stacked in. */
|
||||
/**
|
||||
Specifies the direction children are stacked in. If horizontalAlignment and verticalAlignment were set,
|
||||
they will be resolved again, causing justifyContent and alignItems to be updated accordingly
|
||||
*/
|
||||
@property (nonatomic, assign) ASStackLayoutDirection direction;
|
||||
/** The amount of space between each child. */
|
||||
@property (nonatomic, assign) CGFloat spacing;
|
||||
/**
|
||||
Specifies how children are aligned horizontally. Depends on the stack direction, setting the alignment causes either
|
||||
justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes.
|
||||
Thus, it is preferred to those properties
|
||||
*/
|
||||
@property (nonatomic, assign) ASHorizontalAlignment horizontalAlignment;
|
||||
/**
|
||||
Specifies how children are aligned vertically. Depends on the stack direction, setting the alignment causes either
|
||||
justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes.
|
||||
Thus, it is preferred to those properties
|
||||
*/
|
||||
@property (nonatomic, assign) ASVerticalAlignment verticalAlignment;
|
||||
/** The amount of space between each child. */
|
||||
@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent;
|
||||
/** Orientation of children along cross axis */
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
@implementation ASStackLayoutSpec
|
||||
{
|
||||
ASDN::RecursiveMutex _propertyLock;
|
||||
ASDN::RecursiveMutex _propertyLock;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@@ -58,8 +58,10 @@
|
||||
return nil;
|
||||
}
|
||||
_direction = direction;
|
||||
_alignItems = alignItems;
|
||||
_spacing = spacing;
|
||||
_horizontalAlignment = ASHorizontalAlignmentNone;
|
||||
_verticalAlignment = ASVerticalAlignmentNone;
|
||||
_alignItems = alignItems;
|
||||
_justifyContent = justifyContent;
|
||||
|
||||
[self setChildren:children];
|
||||
@@ -69,18 +71,44 @@
|
||||
- (void)setDirection:(ASStackLayoutDirection)direction
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
_direction = direction;
|
||||
if (_direction != direction) {
|
||||
_direction = direction;
|
||||
[self resolveHorizontalAlignment];
|
||||
[self resolveVerticalAlignment];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHorizontalAlignment:(ASHorizontalAlignment)horizontalAlignment
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
if (_horizontalAlignment != horizontalAlignment) {
|
||||
_horizontalAlignment = horizontalAlignment;
|
||||
[self resolveHorizontalAlignment];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setVerticalAlignment:(ASVerticalAlignment)verticalAlignment
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
if (_verticalAlignment != verticalAlignment) {
|
||||
_verticalAlignment = verticalAlignment;
|
||||
[self resolveVerticalAlignment];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
ASDisplayNodeAssert(_horizontalAlignment == ASHorizontalAlignmentNone, @"Cannot set this property directly because horizontalAlignment is being used");
|
||||
ASDisplayNodeAssert(_verticalAlignment == ASVerticalAlignmentNone, @"Cannot set this property directly because verticalAlignment is being used");
|
||||
_alignItems = alignItems;
|
||||
}
|
||||
|
||||
- (void)setJustifyContent:(ASStackLayoutJustifyContent)justifyContent
|
||||
{
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
ASDisplayNodeAssert(_horizontalAlignment == ASHorizontalAlignmentNone, @"Cannot set this property directly because horizontalAlignment is being used");
|
||||
ASDisplayNodeAssert(_verticalAlignment == ASVerticalAlignmentNone, @"Cannot set this property directly because verticalAlignment is being used");
|
||||
_justifyContent = justifyContent;
|
||||
}
|
||||
|
||||
@@ -149,6 +177,24 @@
|
||||
sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
- (void)resolveHorizontalAlignment
|
||||
{
|
||||
if (_direction == ASStackLayoutDirectionHorizontal) {
|
||||
_justifyContent = justifyContent(_horizontalAlignment, _justifyContent);
|
||||
} else {
|
||||
_alignItems = alignment(_horizontalAlignment, _alignItems);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resolveVerticalAlignment
|
||||
{
|
||||
if (_direction == ASStackLayoutDirectionHorizontal) {
|
||||
_alignItems = alignment(_verticalAlignment, _alignItems);
|
||||
} else {
|
||||
_justifyContent = justifyContent(_verticalAlignment, _justifyContent);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASStackLayoutSpec (Debugging)
|
||||
|
||||
@@ -68,3 +68,63 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment,
|
||||
return stackAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
inline ASStackLayoutAlignItems alignment(ASHorizontalAlignment alignment, ASStackLayoutAlignItems defaultAlignment)
|
||||
{
|
||||
switch (alignment) {
|
||||
case ASAlignmentLeft:
|
||||
return ASStackLayoutAlignItemsStart;
|
||||
case ASAlignmentMiddle:
|
||||
return ASStackLayoutAlignItemsCenter;
|
||||
case ASAlignmentRight:
|
||||
return ASStackLayoutAlignItemsEnd;
|
||||
case ASHorizontalAlignmentNone:
|
||||
default:
|
||||
return defaultAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
inline ASStackLayoutAlignItems alignment(ASVerticalAlignment alignment, ASStackLayoutAlignItems defaultAlignment)
|
||||
{
|
||||
switch (alignment) {
|
||||
case ASAlignmentTop:
|
||||
return ASStackLayoutAlignItemsStart;
|
||||
case ASAlignmentCenter:
|
||||
return ASStackLayoutAlignItemsCenter;
|
||||
case ASAlignmentBottom:
|
||||
return ASStackLayoutAlignItemsEnd;
|
||||
case ASVerticalAlignmentNone:
|
||||
default:
|
||||
return defaultAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
inline ASStackLayoutJustifyContent justifyContent(ASHorizontalAlignment alignment, ASStackLayoutJustifyContent defaultJustifyContent)
|
||||
{
|
||||
switch (alignment) {
|
||||
case ASAlignmentLeft:
|
||||
return ASStackLayoutJustifyContentStart;
|
||||
case ASAlignmentMiddle:
|
||||
return ASStackLayoutJustifyContentCenter;
|
||||
case ASAlignmentRight:
|
||||
return ASStackLayoutJustifyContentEnd;
|
||||
case ASHorizontalAlignmentNone:
|
||||
default:
|
||||
return defaultJustifyContent;
|
||||
}
|
||||
}
|
||||
|
||||
inline ASStackLayoutJustifyContent justifyContent(ASVerticalAlignment alignment, ASStackLayoutJustifyContent defaultJustifyContent)
|
||||
{
|
||||
switch (alignment) {
|
||||
case ASAlignmentTop:
|
||||
return ASStackLayoutJustifyContentStart;
|
||||
case ASAlignmentCenter:
|
||||
return ASStackLayoutJustifyContentCenter;
|
||||
case ASAlignmentBottom:
|
||||
return ASStackLayoutJustifyContentEnd;
|
||||
case ASVerticalAlignmentNone:
|
||||
default:
|
||||
return defaultJustifyContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
self.recordMode = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
static NSArray *defaultSubnodes()
|
||||
{
|
||||
return defaultSubnodesWithSameSize(CGSizeZero, NO);
|
||||
@@ -63,6 +65,24 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier];
|
||||
}
|
||||
|
||||
- (void)testStackLayoutSpecWithDirection:(ASStackLayoutDirection)direction
|
||||
itemsHorizontalAlignment:(ASHorizontalAlignment)horizontalAlignment
|
||||
itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
|
||||
ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init];
|
||||
stackLayoutSpec.direction = direction;
|
||||
stackLayoutSpec.children = subnodes;
|
||||
[stackLayoutSpec setHorizontalAlignment:horizontalAlignment];
|
||||
[stackLayoutSpec setVerticalAlignment:verticalAlignment];
|
||||
|
||||
CGSize exactSize = CGSizeMake(200, 200);
|
||||
static ASSizeRange kSize = ASSizeRangeMake(exactSize, exactSize);
|
||||
[self testStackLayoutSpec:stackLayoutSpec sizeRange:kSize subnodes:subnodes identifier:identifier];
|
||||
}
|
||||
|
||||
- (void)testStackLayoutSpecWithStyle:(ASStackLayoutSpecStyle)style
|
||||
sizeRange:(ASSizeRange)sizeRange
|
||||
subnodes:(NSArray *)subnodes
|
||||
@@ -76,13 +96,23 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
sizeRange:(ASSizeRange)sizeRange
|
||||
subnodes:(NSArray *)subnodes
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
ASStackLayoutSpec *stackLayoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction
|
||||
spacing:style.spacing
|
||||
justifyContent:style.justifyContent
|
||||
alignItems:style.alignItems
|
||||
children:children];
|
||||
[self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
|
||||
}
|
||||
|
||||
- (void)testStackLayoutSpec:(ASStackLayoutSpec *)stackLayoutSpec
|
||||
sizeRange:(ASSizeRange)sizeRange
|
||||
subnodes:(NSArray *)subnodes
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]);
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction spacing:style.spacing justifyContent:style.justifyContent alignItems:style.alignItems children:children]
|
||||
background:backgroundNode];
|
||||
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:stackLayoutSpec background:backgroundNode];
|
||||
|
||||
NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode];
|
||||
[newSubnodes addObjectsFromArray:subnodes];
|
||||
@@ -90,6 +120,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
[self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:newSubnodes identifier:identifier];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)testUnderflowBehaviors
|
||||
{
|
||||
// width 300px; height 0-300px
|
||||
@@ -522,4 +554,54 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil];
|
||||
}
|
||||
|
||||
- (void)testHorizontalAndVerticalAlignments
|
||||
{
|
||||
[self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASAlignmentLeft itemsVerticalAlignment:ASAlignmentTop identifier:@"horizontalTopLeft"];
|
||||
[self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASAlignmentMiddle itemsVerticalAlignment:ASAlignmentCenter identifier:@"horizontalCenter"];
|
||||
[self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASAlignmentRight itemsVerticalAlignment:ASAlignmentBottom identifier:@"horizontalBottomRight"];
|
||||
[self testStackLayoutSpecWithDirection:ASStackLayoutDirectionVertical itemsHorizontalAlignment:ASAlignmentLeft itemsVerticalAlignment:ASAlignmentTop identifier:@"verticalTopLeft"];
|
||||
[self testStackLayoutSpecWithDirection:ASStackLayoutDirectionVertical itemsHorizontalAlignment:ASAlignmentMiddle itemsVerticalAlignment:ASAlignmentCenter identifier:@"verticalCenter"];
|
||||
[self testStackLayoutSpecWithDirection:ASStackLayoutDirectionVertical itemsHorizontalAlignment:ASAlignmentRight itemsVerticalAlignment:ASAlignmentBottom identifier:@"verticalBottomRight"];
|
||||
}
|
||||
|
||||
- (void)testDirectionChangeAfterSettingHorizontalAndVerticalAlignments
|
||||
{
|
||||
ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; // Default direction is horizontal
|
||||
stackLayoutSpec.horizontalAlignment = ASAlignmentRight;
|
||||
stackLayoutSpec.verticalAlignment = ASAlignmentCenter;
|
||||
XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsCenter);
|
||||
XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentEnd);
|
||||
|
||||
stackLayoutSpec.direction = ASStackLayoutDirectionVertical;
|
||||
XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsEnd);
|
||||
XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentCenter);
|
||||
}
|
||||
|
||||
- (void)testAlignItemsAndJustifyContentRestrictionsIfHorizontalAndVerticalAlignmentsAreUsed
|
||||
{
|
||||
ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init];
|
||||
|
||||
// No assertions should be thrown here because alignments are not used
|
||||
stackLayoutSpec.alignItems = ASStackLayoutAlignItemsEnd;
|
||||
stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentEnd;
|
||||
|
||||
// Set alignments and assert that assertions are thrown
|
||||
stackLayoutSpec.horizontalAlignment = ASAlignmentMiddle;
|
||||
stackLayoutSpec.verticalAlignment = ASAlignmentCenter;
|
||||
XCTAssertThrows(stackLayoutSpec.alignItems = ASStackLayoutAlignItemsEnd);
|
||||
XCTAssertThrows(stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentEnd);
|
||||
|
||||
// Unset alignments. alignItems and justifyContent should not be changed
|
||||
stackLayoutSpec.horizontalAlignment = ASHorizontalAlignmentNone;
|
||||
stackLayoutSpec.verticalAlignment = ASVerticalAlignmentNone;
|
||||
XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsCenter);
|
||||
XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentCenter);
|
||||
|
||||
// Now that alignments are none, setting alignItems and justifyContent should be allowed again
|
||||
stackLayoutSpec.alignItems = ASStackLayoutAlignItemsEnd;
|
||||
stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentEnd;
|
||||
XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsEnd);
|
||||
XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentEnd);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Reference in New Issue
Block a user