Add baseline support to ASStackLayoutSpec

This commit is contained in:
ricky cancro 2015-08-12 16:55:12 -07:00 committed by rcancro
parent 80bbf79046
commit f289b3345a
8 changed files with 56 additions and 2 deletions

View File

@ -48,6 +48,8 @@
@synthesize flexBasis = _flexBasis;
@synthesize alignSelf = _alignSelf;
@synthesize preferredFrameSize = _preferredFrameSize;
@synthesize ascender = _ascender;
@synthesize descender = _descender;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
{
@ -157,6 +159,8 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
_flexBasis = ASRelativeDimensionUnconstrained;
_preferredFrameSize = CGSizeZero;
_ascender = 0;
_descender = 0;
}
- (id)init

View File

@ -355,6 +355,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
self.isAccessibilityElement = YES;
}
});
self.ascender = [[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender];
self.descender = [[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender];
}
#pragma mark - Text Layout

View File

@ -24,6 +24,8 @@
@synthesize flexShrink = _flexShrink;
@synthesize flexBasis = _flexBasis;
@synthesize alignSelf = _alignSelf;
@synthesize ascender = _ascender;
@synthesize descender = _descender;
+ (instancetype)new
{

View File

@ -57,6 +57,16 @@
*/
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf;
/**
* @abstract Used for baseline alignment. The distance from the top of the object to its baseline.
*/
@property (nonatomic, readwrite) CGFloat ascender;
/**
* @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom.
*/
@property (nonatomic, readwrite) CGFloat descender;
/**
* @abstract Calculate a layout based on given size range.
*

View File

@ -23,4 +23,8 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
ASStackLayoutAlignSelfCenter,
/** Expand to fill cross axis */
ASStackLayoutAlignSelfStretch,
/** Children align to their first baseline. Only available for horizontal stack nodes */
ASStackLayoutAlignSelfBaselineFirst,
/** Children align to their last baseline. Only available for horizontal stack nodes */
ASStackLayoutAlignSelfBaselineLast,
};

View File

@ -47,6 +47,10 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) {
ASStackLayoutAlignItemsCenter,
/** Expand children to fill cross axis */
ASStackLayoutAlignItemsStretch,
/** Children align to their first baseline. Only available for horizontal stack nodes */
ASStackLayoutAlignItemsBaselineFirst,
/** Children align to their last baseline. Only available for horizontal stack nodes */
ASStackLayoutAlignItemsBaselineLast,
};
typedef struct {

View File

@ -55,6 +55,10 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment,
return ASStackLayoutAlignItemsStart;
case ASStackLayoutAlignSelfStretch:
return ASStackLayoutAlignItemsStretch;
case ASStackLayoutAlignSelfBaselineFirst:
return ASStackLayoutAlignItemsBaselineFirst;
case ASStackLayoutAlignSelfBaselineLast:
return ASStackLayoutAlignItemsBaselineLast;
case ASStackLayoutAlignSelfAuto:
default:
return stackAlignment;

View File

@ -15,9 +15,21 @@
#import "ASStackLayoutSpecUtilities.h"
#import "ASLayoutable.h"
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
const ASStackUnpositionedItem &item) {
const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems);
if (alignItems == ASStackLayoutAlignItemsBaselineFirst) {
return item.child.ascender;
} else if (alignItems == ASStackLayoutAlignItemsBaselineLast) {
return item.layout.size.height + item.child.descender;
}
return 0;
}
static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
const ASStackUnpositionedItem &l,
const CGFloat crossSize)
const CGFloat crossSize,
const CGFloat maxBaseline)
{
switch (alignment(l.child.alignSelf, style.alignItems)) {
case ASStackLayoutAlignItemsEnd:
@ -27,9 +39,14 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
case ASStackLayoutAlignItemsStart:
case ASStackLayoutAlignItemsStretch:
return 0;
case ASStackLayoutAlignItemsBaselineFirst:
return maxBaseline - l.child.ascender;
case ASStackLayoutAlignItemsBaselineLast:
return maxBaseline - baselineForItem(style, l);
}
}
static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style,
const CGFloat offset,
const ASStackUnpositionedLayout &unpositionedLayout,
@ -45,6 +62,12 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max);
const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize);
// Find the maximum height for the baseline
const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){
return baselineForItem(style, a) < baselineForItem(style, b);
});
const CGFloat maxBaseLine = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt);
CGPoint p = directionPoint(style.direction, offset, 0);
BOOL first = YES;
auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{
@ -53,7 +76,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
p = p + directionPoint(style.direction, style.spacing, 0);
}
first = NO;
l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize));
l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine));
p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0);
return l.layout;
});