[ASStackLayoutSpec] Performance improvements (#2470)

* Initial commit for ASStackLayoutSpec improvements

* Remove the lock in ASStackLayoutSpec and make the ASStackLayoutSpecStyle const

I think we don't need lock here as the style already has a lock while we set the value

* Add ASStackLayoutSpecItem  that replaces layout specific items

* Prevent baseline pass if not needed

* Update comments
This commit is contained in:
Michael Schneider
2016-10-30 12:07:18 -07:00
committed by GitHub
parent ca203158da
commit 611894329a
7 changed files with 185 additions and 140 deletions

View File

@@ -13,6 +13,8 @@
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASLayoutElement.h"
#import "ASLayoutElementStylePrivate.h"
#import "ASLayoutSpecUtilities.h" #import "ASLayoutSpecUtilities.h"
#import "ASStackBaselinePositionedLayout.h" #import "ASStackBaselinePositionedLayout.h"
#import "ASThread.h" #import "ASThread.h"
@@ -120,43 +122,65 @@
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{ {
std::vector<id<ASLayoutElement>> stackChildren; NSArray *children = self.children;
for (id<ASLayoutElement> child in self.children) { if (children.count == 0) {
stackChildren.push_back(child);
}
if (stackChildren.empty()) {
return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min]; return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min];
} }
// Accessing the style and size property is pretty costly we create layout spec children we use to figure
// out the layout for each child
const auto stackChildren = AS::map(children, [&](const id<ASLayoutElement> child) -> ASStackLayoutSpecChild {
ASLayoutElementStyle *style = child.style;
return {child, style, style.size};
});
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement}; const ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement};
BOOL needsBaselinePass = _baselineRelativeArrangement || _alignItems == ASStackLayoutAlignItemsBaselineFirst || _alignItems == ASStackLayoutAlignItemsBaselineLast;
// First pass is to get the children into a positioned state
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize); const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize);
const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize);
CGSize finalSize = CGSizeZero; // Figure out if a baseline pass is really needed
NSArray *sublayouts = nil; const BOOL directionIsVertical = (style.direction == ASStackLayoutDirectionVertical);
const BOOL needsBaselineAlignment = ASStackBaselinePositionedLayout::needsBaselineAlignment(style);
const BOOL needsBaselinePositioning = (directionIsVertical == NO || needsBaselineAlignment == YES);
// regardless of whether or not this stack aligns to baseline, we should let ASStackBaselinePositionedLayout::compute find the max ascender NSMutableArray *sublayouts = [NSMutableArray array];
// and min descender in case this spec is a child in another spec that wants to align to a baseline. CGSize finalSize = CGSizeZero;
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize); if (needsBaselinePositioning) {
if (self.direction == ASStackLayoutDirectionVertical) { // All horizontal stacks, regardless of whether or not they are baseline aligned, should go through a baseline
ASDN::MutexLocker l(__instanceLock__); // computation. They could be used in another horizontal stack that is baseline aligned and will need to have
self.style.ascender = stackChildren.front().style.ascender; // computed the proper ascender/descender.
self.style.descender = stackChildren.back().style.descender;
} else { // Vertical stacks do not need to go through this computation since we can easily compute ascender/descender by
ASDN::MutexLocker l(__instanceLock__); // looking at their first/last child's ascender/descender.
self.style.ascender = baselinePositionedLayout.ascender; const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
self.style.descender = baselinePositionedLayout.descender;
if (directionIsVertical == NO) {
self.style.ascender = baselinePositionedLayout.ascender;
self.style.descender = baselinePositionedLayout.descender;
}
if (needsBaselineAlignment == YES) {
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize);
for (const auto &l : baselinePositionedLayout.items) {
[sublayouts addObject:l.layout];
}
}
} }
if (needsBaselinePass) { if (directionIsVertical == YES) {
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); self.style.ascender = stackChildren.front().style.ascender;
sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; self.style.descender = stackChildren.back().style.descender;
} else { }
if (needsBaselineAlignment == NO) {
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize);
sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()];
for (const auto &l : positionedLayout.items) {
[sublayouts addObject:l.layout];
}
} }
return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts];

View File

@@ -13,13 +13,15 @@
#import "ASStackPositionedLayout.h" #import "ASStackPositionedLayout.h"
struct ASStackBaselinePositionedLayout { struct ASStackBaselinePositionedLayout {
const std::vector<ASLayout *> sublayouts; const std::vector<ASStackLayoutSpecItem> items;
const CGFloat crossSize; const CGFloat crossSize;
const CGFloat ascender; const CGFloat ascender;
const CGFloat descender; const CGFloat descender;
/** Given a positioned layout, computes each child position using baseline alignment. */ /** Given a positioned layout, computes each child position using baseline alignment. */
static ASStackBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout, static ASStackBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &constrainedSize); const ASSizeRange &constrainedSize);
static BOOL needsBaselineAlignment(const ASStackLayoutSpecStyle &style);
}; };

View File

@@ -13,31 +13,31 @@
#import "ASLayoutSpecUtilities.h" #import "ASLayoutSpecUtilities.h"
#import "ASLayoutSpec+Subclasses.h" #import "ASLayoutSpec+Subclasses.h"
#import "ASLayoutElement.h"
#import "ASLayoutElementStylePrivate.h"
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
const ASLayout *layout) { const ASStackLayoutSpecItem &l)
{
__weak id<ASLayoutElement> child = layout.layoutElement;
switch (style.alignItems) { switch (style.alignItems) {
case ASStackLayoutAlignItemsBaselineFirst: case ASStackLayoutAlignItemsBaselineFirst:
return child.style.ascender; return l.child.style.ascender;
case ASStackLayoutAlignItemsBaselineLast: case ASStackLayoutAlignItemsBaselineLast:
return layout.size.height + child.style.descender; return l.layout.size.height + l.child.style.descender;
default: default:
return 0; return 0;
} }
} }
static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style, static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
const ASLayout *l, const ASStackLayoutSpecItem &l,
const CGFloat maxAscender, const CGFloat maxAscender,
const CGFloat maxBaseline) const CGFloat maxBaseline)
{ {
if (style.direction == ASStackLayoutDirectionHorizontal) { if (style.direction == ASStackLayoutDirectionHorizontal) {
__weak id<ASLayoutElement> child = l.layoutElement;
switch (style.alignItems) { switch (style.alignItems) {
case ASStackLayoutAlignItemsBaselineFirst: case ASStackLayoutAlignItemsBaselineFirst:
return maxAscender - child.style.ascender; return maxAscender - l.child.style.ascender;
case ASStackLayoutAlignItemsBaselineLast: case ASStackLayoutAlignItemsBaselineLast:
return maxBaseline - baselineForItem(style, l); return maxBaseline - baselineForItem(style, l);
@@ -48,19 +48,28 @@ static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
return 0; return 0;
} }
static CGFloat maxDimensionForLayout(const ASLayout *l, static CGFloat maxDimensionForItem(const ASStackLayoutSpecItem &l,
const ASStackLayoutSpecStyle &style) const ASStackLayoutSpecStyle &style)
{ {
CGFloat maxDimension = crossDimension(style.direction, l.size); CGFloat maxDimension = crossDimension(style.direction, l.layout.size);
style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.position.x : maxDimension += l.position.y; style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.layout.position.x : maxDimension += l.layout.position.y;
return maxDimension; return maxDimension;
} }
BOOL ASStackBaselinePositionedLayout::needsBaselineAlignment(const ASStackLayoutSpecStyle &style)
{
return style.baselineRelativeArrangement ||
style.alignItems == ASStackLayoutAlignItemsBaselineFirst ||
style.alignItems == ASStackLayoutAlignItemsBaselineLast;
}
ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &constrainedSize) const ASSizeRange &constrainedSize)
{ {
/* Step 1: Look at each child and determine the distance from the top of the child node it's baseline. const auto stackedChildren = positionedLayout.items;
/* Step 1: Look at each child and determine the distance from the top of the child node to its baseline.
For example, let's say we have the following two text nodes and want to align them to the first baseline: For example, let's say we have the following two text nodes and want to align them to the first baseline:
Hello! Why, hello there! How Hello! Why, hello there! How
@@ -74,10 +83,10 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
the font's descender (it's negative). In the case of the first node, which is only 1 line, this should be the same value as the ascender. the font's descender (it's negative). In the case of the first node, which is only 1 line, this should be the same value as the ascender.
The second node, however, has a larger height and there will have a larger baseline offset. The second node, however, has a larger height and there will have a larger baseline offset.
*/ */
const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ const auto baselineIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b){
return baselineForItem(style, a) < baselineForItem(style, b); return baselineForItem(style, a) < baselineForItem(style, b);
}); });
const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(style, *baselineIt); const CGFloat maxBaseline = baselineIt == stackedChildren.end() ? 0 : baselineForItem(style, *baselineIt);
/* /*
Step 2: Find the max ascender for all of the children. Step 2: Find the max ascender for all of the children.
@@ -89,10 +98,10 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
Note: if we are aligning to the last baseline, then we don't need this value in our computation. However, we do want Note: if we are aligning to the last baseline, then we don't need this value in our computation. However, we do want
our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec.
*/ */
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ const auto ascenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b){
return a.layoutElement.style.ascender < b.layoutElement.style.ascender; return a.child.style.ascender < b.child.style.ascender;
}); });
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutElement.style.ascender; const CGFloat maxAscender = ascenderIt == stackedChildren.end() ? 0 : (*ascenderIt).child.style.ascender;
/* /*
Step 3: Take each child and update its layout position based on the baseline offset. Step 3: Take each child and update its layout position based on the baseline offset.
@@ -102,39 +111,34 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
spacing between the two nodes is from the baseline, not the bounding box. spacing between the two nodes is from the baseline, not the bounding box.
*/ */
std::vector<ASLayout *> stackedChildren;
// Only change positions of layouts this stackSpec is aligning to a baseline. Otherwise we are only here to // Only change positions of layouts this stackSpec is aligning to a baseline. Otherwise we are only here to
// compute the min/max descender/ascender for this stack spec. // compute the min/max descender/ascender for this stack spec.
if (style.baselineRelativeArrangement || style.alignItems == ASStackLayoutAlignItemsBaselineFirst || style.alignItems == ASStackLayoutAlignItemsBaselineLast) { if (ASStackBaselinePositionedLayout::needsBaselineAlignment(style)) {
// Adjust the positioned layout items to be positioned based on the baseline
CGPoint p = CGPointZero; CGPoint p = CGPointZero;
BOOL first = YES; BOOL first = YES;
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
__weak id<ASLayoutElement> child = l.layoutElement; for (const ASStackLayoutSpecItem &l : stackedChildren) {
p = p + directionPoint(style.direction, child.style.spacingBefore, 0); ASLayoutElementStyle *layoutElementStyle = l.child.style;
if (first) {
// if this is the first item use the previously computed start point p = p + directionPoint(style.direction, layoutElementStyle.spacingBefore, 0);
p = l.position;
} else { // if this is the first item use the previously computed start point otherwise add the stack spacing
// otherwise add the stack spacing p = first ? l.layout.position : p + directionPoint(style.direction, style.spacing, 0);
p = p + directionPoint(style.direction, style.spacing, 0);
}
first = NO; first = NO;
// Find the difference between this node's baseline and the max baseline of all the children. Add this difference to the child's y position. // Find the difference between this node's baseline and the max baseline of all the children. Add this difference to the child's y position.
l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline)); l.layout.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline));
// If we are a vertical stack, add the item's descender (it is negative) to the offset for the next node. This will ensure we are spacing // If we are a vertical stack, add the item's descender (it is negative) to the offset for the next node. This will ensure we are spacing
// node from baselines and not bounding boxes. // node from baselines and not bounding boxes.
CGFloat spacingAfterBaseline = 0; CGFloat spacingAfterBaseline = 0;
if (style.direction == ASStackLayoutDirectionVertical) { if (style.direction == ASStackLayoutDirectionVertical) {
spacingAfterBaseline = child.style.descender; spacingAfterBaseline = layoutElementStyle.descender;
} }
p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.style.spacingAfter + spacingAfterBaseline, 0); p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutElementStyle.spacingAfter + spacingAfterBaseline, 0);
}
return l;
});
} else {
stackedChildren = positionedLayout.sublayouts;
} }
/* /*
@@ -147,10 +151,10 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
*/ */
const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(),
[&](ASLayout *a, ASLayout *b) { [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b) {
return maxDimensionForLayout(a, style) < maxDimensionForLayout(b, style); return maxDimensionForItem(a, style) < maxDimensionForItem(b, style);
}); });
const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForLayout(*it, style); const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForItem(*it, style);
const auto minCrossSize = crossDimension(style.direction, constrainedSize.min); const auto minCrossSize = crossDimension(style.direction, constrainedSize.min);
const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max); const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max);
const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize);
@@ -159,10 +163,10 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements
ASLayoutElement and needs an ascender and descender to lay itself out properly. ASLayoutElement and needs an ascender and descender to lay itself out properly.
*/ */
const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b){
return a.position.y + a.size.height < b.position.y + b.size.height; return a.layout.position.y + a.layout.size.height < b.layout.position.y + b.layout.size.height;
}); });
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutElement.style.descender; const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).child.style.descender;
return {stackedChildren, crossSize, maxAscender, minDescender}; return {std::move(stackedChildren), crossSize, maxAscender, minDescender};
} }

View File

@@ -14,7 +14,7 @@
/** Represents a set of laid out and positioned stack layout children. */ /** Represents a set of laid out and positioned stack layout children. */
struct ASStackPositionedLayout { struct ASStackPositionedLayout {
const std::vector<ASLayout *> sublayouts; const std::vector<ASStackLayoutSpecItem> items;
const CGFloat crossSize; const CGFloat crossSize;
/** Given an unpositioned layout, computes the positions each child should be placed at. */ /** Given an unpositioned layout, computes the positions each child should be placed at. */

View File

@@ -17,7 +17,7 @@
#import "ASLayoutSpec+Subclasses.h" #import "ASLayoutSpec+Subclasses.h"
static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
const ASStackUnpositionedItem &l, const ASStackLayoutSpecItem &l,
const CGFloat crossSize) const CGFloat crossSize)
{ {
switch (alignment(l.child.style.alignSelf, style.alignItems)) { switch (alignment(l.child.style.alignSelf, style.alignItems)) {
@@ -50,9 +50,10 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
const ASStackUnpositionedLayout &unpositionedLayout, const ASStackUnpositionedLayout &unpositionedLayout,
const ASSizeRange &constrainedSize) const ASSizeRange &constrainedSize)
{ {
// The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below). // The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below).
const auto it = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), const auto it = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(),
[&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b){
return compareCrossDimension(style.direction, a.layout.size, b.layout.size); return compareCrossDimension(style.direction, a.layout.size, b.layout.size);
}); });
const auto largestChildCrossSize = it == unpositionedLayout.items.end() ? 0 : crossDimension(style.direction, it->layout.size); const auto largestChildCrossSize = it == unpositionedLayout.items.end() ? 0 : crossDimension(style.direction, it->layout.size);
@@ -64,9 +65,11 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
BOOL first = YES; BOOL first = YES;
const auto lastChild = unpositionedLayout.items.back().child; const auto lastChild = unpositionedLayout.items.back().child;
CGFloat offset = 0; CGFloat offset = 0;
auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ // Adjust the position of the unpositioned layouts to be positioned
offset = (l.child == lastChild) ? lastChildOffset : 0; const auto stackedChildren = unpositionedLayout.items;
for (const auto &l : stackedChildren) {
offset = (l.child.element == lastChild.element) ? lastChildOffset : 0;
p = p + directionPoint(style.direction, l.child.style.spacingBefore + offset, 0); p = p + directionPoint(style.direction, l.child.style.spacingBefore + offset, 0);
if (!first) { if (!first) {
p = p + directionPoint(style.direction, style.spacing + extraSpacing, 0); p = p + directionPoint(style.direction, style.spacing + extraSpacing, 0);
@@ -75,9 +78,9 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize));
p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.style.spacingAfter, 0); p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.style.spacingAfter, 0);
return l.layout; }
});
return {stackedChildren, crossSize}; return {std::move(stackedChildren), crossSize};
} }
static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style,
@@ -105,12 +108,15 @@ ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnposition
} }
switch (justifyContent) { switch (justifyContent) {
case ASStackLayoutJustifyContentStart: case ASStackLayoutJustifyContentStart: {
return stackedLayout(style, 0, unpositionedLayout, constrainedSize); return stackedLayout(style, 0, unpositionedLayout, constrainedSize);
case ASStackLayoutJustifyContentCenter: }
case ASStackLayoutJustifyContentCenter: {
return stackedLayout(style, std::floor(violation / 2), unpositionedLayout, constrainedSize); return stackedLayout(style, std::floor(violation / 2), unpositionedLayout, constrainedSize);
case ASStackLayoutJustifyContentEnd: }
case ASStackLayoutJustifyContentEnd: {
return stackedLayout(style, violation, unpositionedLayout, constrainedSize); return stackedLayout(style, violation, unpositionedLayout, constrainedSize);
}
case ASStackLayoutJustifyContentSpaceBetween: { case ASStackLayoutJustifyContentSpaceBetween: {
const auto numOfSpacings = numOfItems - 1; const auto numOfSpacings = numOfItems - 1;
return stackedLayout(style, 0, std::floor(violation / numOfSpacings), std::fmod(violation, numOfSpacings), unpositionedLayout, constrainedSize); return stackedLayout(style, 0, std::floor(violation / numOfSpacings), std::fmod(violation, numOfSpacings), unpositionedLayout, constrainedSize);

View File

@@ -14,24 +14,34 @@
#import "ASStackLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h"
#import "ASStackLayoutSpec.h" #import "ASStackLayoutSpec.h"
struct ASStackUnpositionedItem { struct ASStackLayoutSpecChild {
/** The original source child. */ /** The original source child. */
id<ASLayoutElement> child; id<ASLayoutElement> element;
/** The proposed layout. */ /** Style object of element. */
ASLayoutElementStyle *style;
/** Size object of the element */
ASLayoutElementSize size;
};
struct ASStackLayoutSpecItem {
/** The original source child. */
ASStackLayoutSpecChild child;
/** The proposed layout or nil if no is calculated yet. */
ASLayout *layout; ASLayout *layout;
}; };
/** Represents a set of stack layout children that have their final layout computed, but are not yet positioned. */ /** Represents a set of stack layout children that have their final layout computed, but are not yet positioned. */
struct ASStackUnpositionedLayout { struct ASStackUnpositionedLayout {
/** A set of proposed child layouts, not yet positioned. */ /** A set of proposed child layouts, not yet positioned. */
const std::vector<ASStackUnpositionedItem> items; const std::vector<ASStackLayoutSpecItem> items;
/** The total size of the children in the stack dimension, including all spacing. */ /** The total size of the children in the stack dimension, including all spacing. */
const CGFloat stackDimensionSum; const CGFloat stackDimensionSum;
/** The amount by which stackDimensionSum violates constraints. If positive, less than min; negative, greater than max. */ /** The amount by which stackDimensionSum violates constraints. If positive, less than min; negative, greater than max. */
const CGFloat violation; const CGFloat violation;
/** Given a set of children, computes the unpositioned layouts for those children. */ /** Given a set of children, computes the unpositioned layouts for those children. */
static ASStackUnpositionedLayout compute(const std::vector<id<ASLayoutElement>> &children, static ASStackUnpositionedLayout compute(const std::vector<ASStackLayoutSpecChild> &children,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange); const ASSizeRange &sizeRange);
}; };

View File

@@ -17,12 +17,11 @@
#import "ASLayoutElementStylePrivate.h" #import "ASLayoutElementStylePrivate.h"
static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecStyle &style, static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecStyle &style,
const id<ASLayoutElement>child, const ASStackLayoutSpecChild &child,
const CGFloat stackMax, const CGFloat stackMax,
const CGFloat crossMax) const CGFloat crossMax)
{ {
// stretched children may have a cross direction max that is smaller than the minimum size constraint of the parent. // stretched children may have a cross direction max that is smaller than the minimum size constraint of the parent.
const CGFloat computedMax = (style.direction == ASStackLayoutDirectionVertical ? const CGFloat computedMax = (style.direction == ASStackLayoutDirectionVertical ?
ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).max.width : ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).max.width :
ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).max.height); ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).max.height);
@@ -30,7 +29,7 @@ static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecSt
} }
static CGFloat resolveCrossDimensionMinForStretchChild(const ASStackLayoutSpecStyle &style, static CGFloat resolveCrossDimensionMinForStretchChild(const ASStackLayoutSpecStyle &style,
const id<ASLayoutElement>child, const ASStackLayoutSpecChild &child,
const CGFloat stackMax, const CGFloat stackMax,
const CGFloat crossMin) const CGFloat crossMin)
{ {
@@ -44,8 +43,8 @@ static CGFloat resolveCrossDimensionMinForStretchChild(const ASStackLayoutSpecSt
/** /**
Sizes the child given the parameters specified, and returns the computed layout. Sizes the child given the parameters specified, and returns the computed layout.
*/ */
static ASLayout *crossChildLayout(const id<ASLayoutElement> child, static ASLayout *crossChildLayout(const ASStackLayoutSpecChild &child,
const ASStackLayoutSpecStyle style, const ASStackLayoutSpecStyle &style,
const CGFloat stackMin, const CGFloat stackMin,
const CGFloat stackMax, const CGFloat stackMax,
const CGFloat crossMin, const CGFloat crossMin,
@@ -54,14 +53,16 @@ static ASLayout *crossChildLayout(const id<ASLayoutElement> child,
{ {
const ASStackLayoutAlignItems alignItems = alignment(child.style.alignSelf, style.alignItems); const ASStackLayoutAlignItems alignItems = alignment(child.style.alignSelf, style.alignItems);
// stretched children will have a cross dimension of at least crossMin // stretched children will have a cross dimension of at least crossMin
const CGFloat childCrossMin = (alignItems == ASStackLayoutAlignItemsStretch ? resolveCrossDimensionMinForStretchChild(style, child, stackMax, crossMin) : 0); const CGFloat childCrossMin = (alignItems == ASStackLayoutAlignItemsStretch ?
resolveCrossDimensionMinForStretchChild(style, child, stackMax, crossMin) :
0);
const CGFloat childCrossMax = (alignItems == ASStackLayoutAlignItemsStretch ? const CGFloat childCrossMax = (alignItems == ASStackLayoutAlignItemsStretch ?
resolveCrossDimensionMaxForStretchChild(style, child, stackMax, crossMax) : resolveCrossDimensionMaxForStretchChild(style, child, stackMax, crossMax) :
crossMax); crossMax);
const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, childCrossMax); const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, childCrossMax);
ASLayout *layout = [child layoutThatFits:childSizeRange parentSize:size]; ASLayout *layout = [child.element layoutThatFits:childSizeRange parentSize:size];
ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child); ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child.element);
return layout ? : [ASLayout layoutWithLayoutElement:child size:{0, 0}]; return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}];
} }
/** /**
@@ -96,13 +97,13 @@ static ASLayout *crossChildLayout(const id<ASLayoutElement> child,
@param layouts pre-computed child layouts; modified in-place as needed @param layouts pre-computed child layouts; modified in-place as needed
@param style the layout style of the overall stack layout @param style the layout style of the overall stack layout
*/ */
static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedItem> &layouts, static void stretchChildrenAlongCrossDimension(std::vector<ASStackLayoutSpecItem> &layouts,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const CGSize size) const CGSize size)
{ {
// Find the maximum cross dimension size among child layouts // Find the maximum cross dimension size among child layouts
const auto it = std::max_element(layouts.begin(), layouts.end(), const auto it = std::max_element(layouts.begin(), layouts.end(),
[&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b) { [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b) {
return compareCrossDimension(style.direction, a.layout.size, b.layout.size); return compareCrossDimension(style.direction, a.layout.size, b.layout.size);
}); });
@@ -129,18 +130,18 @@ static const CGFloat kViolationEpsilon = 0.01;
Returns a lambda that computes the relevant flex factor based on the given violation. Returns a lambda that computes the relevant flex factor based on the given violation.
@param violation The amount that the stack layout violates its size range. See header for sign interpretation. @param violation The amount that the stack layout violates its size range. See header for sign interpretation.
*/ */
static std::function<CGFloat(const ASStackUnpositionedItem &)> flexFactorInViolationDirection(const CGFloat violation) static std::function<CGFloat(const ASStackLayoutSpecItem &)> flexFactorInViolationDirection(const CGFloat violation)
{ {
if (std::fabs(violation) < kViolationEpsilon) { if (std::fabs(violation) < kViolationEpsilon) {
return [](const ASStackUnpositionedItem &item) { return 0.0; }; return [](const ASStackLayoutSpecItem &item) { return 0.0; };
} else if (violation > 0) { } else if (violation > 0) {
return [](const ASStackUnpositionedItem &item) { return item.child.style.flexGrow; }; return [](const ASStackLayoutSpecItem &item) { return item.child.style.flexGrow; };
} else { } else {
return [](const ASStackUnpositionedItem &item) { return item.child.style.flexShrink; }; return [](const ASStackLayoutSpecItem &item) { return item.child.style.flexShrink; };
} }
} }
static inline CGFloat scaledFlexShrinkFactor(const ASStackUnpositionedItem &item, static inline CGFloat scaledFlexShrinkFactor(const ASStackLayoutSpecItem &item,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const CGFloat flexFactorSum) const CGFloat flexFactorSum)
{ {
@@ -155,15 +156,15 @@ static inline CGFloat scaledFlexShrinkFactor(const ASStackUnpositionedItem &item
@param flexFactorSum The sum of each item's flex factor as determined by the provided violation. @param flexFactorSum The sum of each item's flex factor as determined by the provided violation.
@return A lambda capable of computing the flex shrink adjustment, if any, for a particular item. @return A lambda capable of computing the flex shrink adjustment, if any, for a particular item.
*/ */
static std::function<CGFloat(const ASStackUnpositionedItem &)> flexShrinkAdjustment(const std::vector<ASStackUnpositionedItem> &items, static std::function<CGFloat(const ASStackLayoutSpecItem &)> flexShrinkAdjustment(const std::vector<ASStackLayoutSpecItem> &items,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const CGFloat violation, const CGFloat violation,
const CGFloat flexFactorSum) const CGFloat flexFactorSum)
{ {
const CGFloat scaledFlexShrinkFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackUnpositionedItem &item) { const CGFloat scaledFlexShrinkFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
return x + scaledFlexShrinkFactor(item, style, flexFactorSum); return x + scaledFlexShrinkFactor(item, style, flexFactorSum);
}); });
return [style, scaledFlexShrinkFactorSum, violation, flexFactorSum](const ASStackUnpositionedItem &item) { return [style, scaledFlexShrinkFactorSum, violation, flexFactorSum](const ASStackLayoutSpecItem &item) {
const CGFloat scaledFlexShrinkFactorRatio = scaledFlexShrinkFactor(item, style, flexFactorSum) / scaledFlexShrinkFactorSum; const CGFloat scaledFlexShrinkFactorRatio = scaledFlexShrinkFactor(item, style, flexFactorSum) / scaledFlexShrinkFactorSum;
// The item should shrink proportionally to the scaled flex shrink factor ratio computed above. // The item should shrink proportionally to the scaled flex shrink factor ratio computed above.
// Unlike the flex grow adjustment the flex shrink adjustment needs to take the size of each item into account. // Unlike the flex grow adjustment the flex shrink adjustment needs to take the size of each item into account.
@@ -178,12 +179,12 @@ static std::function<CGFloat(const ASStackUnpositionedItem &)> flexShrinkAdjustm
@param flexFactorSum The sum of each item's flex factor as determined by the provided violation. @param flexFactorSum The sum of each item's flex factor as determined by the provided violation.
@return A lambda capable of computing the flex grow adjustment, if any, for a particular item. @return A lambda capable of computing the flex grow adjustment, if any, for a particular item.
*/ */
static std::function<CGFloat(const ASStackUnpositionedItem &)> flexGrowAdjustment(const std::vector<ASStackUnpositionedItem> &items, static std::function<CGFloat(const ASStackLayoutSpecItem &)> flexGrowAdjustment(const std::vector<ASStackLayoutSpecItem> &items,
const CGFloat violation, const CGFloat violation,
const CGFloat flexFactorSum) const CGFloat flexFactorSum)
{ {
// To compute the flex grow adjustment distribute the violation proportionally based on each item's flex grow factor. // To compute the flex grow adjustment distribute the violation proportionally based on each item's flex grow factor.
return [violation, flexFactorSum](const ASStackUnpositionedItem &item) { return [violation, flexFactorSum](const ASStackLayoutSpecItem &item) {
return std::floor(violation * (item.child.style.flexGrow / flexFactorSum)); return std::floor(violation * (item.child.style.flexGrow / flexFactorSum));
}; };
} }
@@ -196,7 +197,7 @@ static std::function<CGFloat(const ASStackUnpositionedItem &)> flexGrowAdjustmen
@param flexFactorSum The sum of each item's flex factor as determined by the provided violation. @param flexFactorSum The sum of each item's flex factor as determined by the provided violation.
@return A lambda capable of computing the flex adjustment for a particular item. @return A lambda capable of computing the flex adjustment for a particular item.
*/ */
static std::function<CGFloat(const ASStackUnpositionedItem &)> flexAdjustmentInViolationDirection(const std::vector<ASStackUnpositionedItem> &items, static std::function<CGFloat(const ASStackLayoutSpecItem &)> flexAdjustmentInViolationDirection(const std::vector<ASStackLayoutSpecItem> &items,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const CGFloat violation, const CGFloat violation,
const CGFloat flexFactorSum) const CGFloat flexFactorSum)
@@ -208,7 +209,7 @@ static std::function<CGFloat(const ASStackUnpositionedItem &)> flexAdjustmentInV
} }
} }
ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASLayoutElement> child) ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(const ASStackLayoutSpecChild &child)
{ {
return child.style.flexGrow > 0 && child.style.flexShrink > 0; return child.style.flexGrow > 0 && child.style.flexShrink > 0;
} }
@@ -217,15 +218,14 @@ ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASLayoutElement> child)
The flexible children may have been left not laid out in the initial layout pass, so we may have to go through and size The flexible children may have been left not laid out in the initial layout pass, so we may have to go through and size
these children at zero size so that the children layouts are at least present. these children at zero size so that the children layouts are at least present.
*/ */
static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem> &items, static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackLayoutSpecItem> &items,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange, const ASSizeRange &sizeRange,
const CGSize size) const CGSize size)
{ {
for (ASStackUnpositionedItem &item : items) { for (ASStackLayoutSpecItem &item : items) {
const id<ASLayoutElement> child = item.child; if (isFlexibleInBothDirections(item.child)) {
if (isFlexibleInBothDirections(child)) { item.layout = crossChildLayout(item.child,
item.layout = crossChildLayout(child,
style, style,
0, 0,
0, 0,
@@ -250,21 +250,20 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem
@param children unpositioned layouts for the children of the stack spec @param children unpositioned layouts for the children of the stack spec
@param style the layout style of the overall stack layout @param style the layout style of the overall stack layout
*/ */
static CGFloat computeStackDimensionSum(const std::vector<ASStackUnpositionedItem> &children, static CGFloat computeStackDimensionSum(const std::vector<ASStackLayoutSpecItem> &children,
const ASStackLayoutSpecStyle &style) const ASStackLayoutSpecStyle &style)
{ {
// Sum up the childrens' spacing // Sum up the childrens' spacing
const CGFloat childSpacingSum = std::accumulate(children.begin(), children.end(), const CGFloat childSpacingSum = std::accumulate(children.begin(), children.end(),
// Start from default spacing between each child: // Start from default spacing between each child:
children.empty() ? 0 : style.spacing * (children.size() - 1), children.empty() ? 0 : style.spacing * (children.size() - 1),
[&](CGFloat x, const ASStackUnpositionedItem &l) { [&](CGFloat x, const ASStackLayoutSpecItem &l) {
const id<ASLayoutElement> child = l.child; return x + l.child.style.spacingBefore + l.child.style.spacingAfter;
return x + child.style.spacingBefore + child.style.spacingAfter;
}); });
// Sum up the childrens' dimensions (including spacing) in the stack direction. // Sum up the childrens' dimensions (including spacing) in the stack direction.
const CGFloat childStackDimensionSum = std::accumulate(children.begin(), children.end(), childSpacingSum, const CGFloat childStackDimensionSum = std::accumulate(children.begin(), children.end(), childSpacingSum,
[&](CGFloat x, const ASStackUnpositionedItem &l) { [&](CGFloat x, const ASStackLayoutSpecItem &l) {
return x + stackDimension(style.direction, l.layout.size); return x + stackDimension(style.direction, l.layout.size);
}); });
return childStackDimensionSum; return childStackDimensionSum;
@@ -319,7 +318,7 @@ static CGFloat computeViolation(const CGFloat stackDimensionSum,
If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific
number then we may avoid the first "intrinsic" size calculation. number then we may avoid the first "intrinsic" size calculation.
*/ */
ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutElement>> &children, ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<ASStackLayoutSpecChild> &children,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange) const ASSizeRange &sizeRange)
{ {
@@ -342,17 +341,17 @@ ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutEleme
@param sizeRange the range of allowable sizes for the stack layout component @param sizeRange the range of allowable sizes for the stack layout component
@param size Size of the stack layout component. May be undefined in either or both directions. @param size Size of the stack layout component. May be undefined in either or both directions.
*/ */
static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem> &items, static void flexChildrenAlongStackDimension(std::vector<ASStackLayoutSpecItem> &items,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange, const ASSizeRange &sizeRange,
const CGSize size, const CGSize size,
const BOOL useOptimizedFlexing) const BOOL useOptimizedFlexing)
{ {
const CGFloat violation = computeViolation(computeStackDimensionSum(items, style), style, sizeRange); const CGFloat violation = computeViolation(computeStackDimensionSum(items, style), style, sizeRange);
std::function<CGFloat(const ASStackUnpositionedItem &)> flexFactor = flexFactorInViolationDirection(violation); std::function<CGFloat(const ASStackLayoutSpecItem &)> flexFactor = flexFactorInViolationDirection(violation);
// The flex factor sum is needed to determine if flexing is necessary. // The flex factor sum is needed to determine if flexing is necessary.
// This value is also needed if the violation is positive and flexible children need to grow, so keep it around. // This value is also needed if the violation is positive and flexible children need to grow, so keep it around.
const CGFloat flexFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackUnpositionedItem &item) { const CGFloat flexFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
return x + flexFactor(item); return x + flexFactor(item);
}); });
// If no children are able to flex then there is nothing left to do. Bail. // If no children are able to flex then there is nothing left to do. Bail.
@@ -363,17 +362,17 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
} }
return; return;
} }
std::function<CGFloat(const ASStackUnpositionedItem &)> flexAdjustment = flexAdjustmentInViolationDirection(items, std::function<CGFloat(const ASStackLayoutSpecItem &)> flexAdjustment = flexAdjustmentInViolationDirection(items,
style, style,
violation, violation,
flexFactorSum); flexFactorSum);
// Compute any remaining violation to the first flexible child. // Compute any remaining violation to the first flexible child.
const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackUnpositionedItem &item) { const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
return x - flexAdjustment(item); return x - flexAdjustment(item);
}); });
BOOL isFirstFlex = YES; BOOL isFirstFlex = YES;
for (ASStackUnpositionedItem &item : items) { for (ASStackLayoutSpecItem &item : items) {
const CGFloat currentFlexAdjustment = flexAdjustment(item); const CGFloat currentFlexAdjustment = flexAdjustment(item);
// Children are consider inflexible if they do not need to make a flex adjustment. // Children are consider inflexible if they do not need to make a flex adjustment.
if (currentFlexAdjustment != 0) { if (currentFlexAdjustment != 0) {
@@ -396,7 +395,7 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and
stretched. stretched.
*/ */
static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<id<ASLayoutElement>> &children, static std::vector<ASStackLayoutSpecItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<ASStackLayoutSpecChild> &children,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange, const ASSizeRange &sizeRange,
const CGSize size, const CGSize size,
@@ -404,9 +403,9 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
{ {
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
return AS::map(children, [&](id<ASLayoutElement> child) -> ASStackUnpositionedItem { return AS::map(children, [&](const ASStackLayoutSpecChild &child) -> ASStackLayoutSpecItem {
if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
return { child, [ASLayout layoutWithLayoutElement:child size:{0, 0}] }; return {child, [ASLayout layoutWithLayoutElement:child.element size:{0, 0}]};
} else { } else {
return { return {
child, child,
@@ -422,7 +421,7 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
}); });
} }
ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<id<ASLayoutElement>> &children, ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<ASStackLayoutSpecChild> &children,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange) const ASSizeRange &sizeRange)
{ {
@@ -439,7 +438,7 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
// We do a first pass of all the children, generating an unpositioned layout for each with an unbounded range along // We do a first pass of all the children, generating an unpositioned layout for each with an unbounded range along
// the stack dimension. This allows us to compute the "intrinsic" size of each child and find the available violation // the stack dimension. This allows us to compute the "intrinsic" size of each child and find the available violation
// which determines whether we must grow or shrink the flexible children. // which determines whether we must grow or shrink the flexible children.
std::vector<ASStackUnpositionedItem> items = layoutChildrenAlongUnconstrainedStackDimension(children, std::vector<ASStackLayoutSpecItem> items = layoutChildrenAlongUnconstrainedStackDimension(children,
style, style,
sizeRange, sizeRange,
size, size,
@@ -449,5 +448,5 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
stretchChildrenAlongCrossDimension(items, style, size); stretchChildrenAlongCrossDimension(items, style, size);
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style); const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
return {items, stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)}; return {std::move(items), stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)};
} }