mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
[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:
committed by
GitHub
parent
ca203158da
commit
611894329a
@@ -13,31 +13,31 @@
|
||||
#import "ASLayoutSpecUtilities.h"
|
||||
#import "ASLayoutSpec+Subclasses.h"
|
||||
|
||||
#import "ASLayoutElement.h"
|
||||
#import "ASLayoutElementStylePrivate.h"
|
||||
|
||||
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
|
||||
const ASLayout *layout) {
|
||||
|
||||
__weak id<ASLayoutElement> child = layout.layoutElement;
|
||||
const ASStackLayoutSpecItem &l)
|
||||
{
|
||||
switch (style.alignItems) {
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
return child.style.ascender;
|
||||
return l.child.style.ascender;
|
||||
case ASStackLayoutAlignItemsBaselineLast:
|
||||
return layout.size.height + child.style.descender;
|
||||
return l.layout.size.height + l.child.style.descender;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
|
||||
const ASLayout *l,
|
||||
const ASStackLayoutSpecItem &l,
|
||||
const CGFloat maxAscender,
|
||||
const CGFloat maxBaseline)
|
||||
{
|
||||
if (style.direction == ASStackLayoutDirectionHorizontal) {
|
||||
__weak id<ASLayoutElement> child = l.layoutElement;
|
||||
switch (style.alignItems) {
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
return maxAscender - child.style.ascender;
|
||||
return maxAscender - l.child.style.ascender;
|
||||
case ASStackLayoutAlignItemsBaselineLast:
|
||||
return maxBaseline - baselineForItem(style, l);
|
||||
|
||||
@@ -48,19 +48,28 @@ static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CGFloat maxDimensionForLayout(const ASLayout *l,
|
||||
const ASStackLayoutSpecStyle &style)
|
||||
static CGFloat maxDimensionForItem(const ASStackLayoutSpecItem &l,
|
||||
const ASStackLayoutSpecStyle &style)
|
||||
{
|
||||
CGFloat maxDimension = crossDimension(style.direction, l.size);
|
||||
style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.position.x : maxDimension += l.position.y;
|
||||
CGFloat maxDimension = crossDimension(style.direction, l.layout.size);
|
||||
style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.layout.position.x : maxDimension += l.layout.position.y;
|
||||
return maxDimension;
|
||||
}
|
||||
|
||||
BOOL ASStackBaselinePositionedLayout::needsBaselineAlignment(const ASStackLayoutSpecStyle &style)
|
||||
{
|
||||
return style.baselineRelativeArrangement ||
|
||||
style.alignItems == ASStackLayoutAlignItemsBaselineFirst ||
|
||||
style.alignItems == ASStackLayoutAlignItemsBaselineLast;
|
||||
}
|
||||
|
||||
ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
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:
|
||||
|
||||
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 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);
|
||||
});
|
||||
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.
|
||||
@@ -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
|
||||
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){
|
||||
return a.layoutElement.style.ascender < b.layoutElement.style.ascender;
|
||||
const auto ascenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b){
|
||||
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.
|
||||
@@ -102,39 +111,34 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
|
||||
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
|
||||
// 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;
|
||||
BOOL first = YES;
|
||||
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
|
||||
__weak id<ASLayoutElement> child = l.layoutElement;
|
||||
p = p + directionPoint(style.direction, child.style.spacingBefore, 0);
|
||||
if (first) {
|
||||
// if this is the first item use the previously computed start point
|
||||
p = l.position;
|
||||
} else {
|
||||
// otherwise add the stack spacing
|
||||
p = p + directionPoint(style.direction, style.spacing, 0);
|
||||
}
|
||||
|
||||
for (const ASStackLayoutSpecItem &l : stackedChildren) {
|
||||
ASLayoutElementStyle *layoutElementStyle = l.child.style;
|
||||
|
||||
p = p + directionPoint(style.direction, layoutElementStyle.spacingBefore, 0);
|
||||
|
||||
// if this is the first item use the previously computed start point otherwise add the stack spacing
|
||||
p = first ? l.layout.position : p + directionPoint(style.direction, style.spacing, 0);
|
||||
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.
|
||||
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
|
||||
// node from baselines and not bounding boxes.
|
||||
CGFloat spacingAfterBaseline = 0;
|
||||
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);
|
||||
|
||||
return l;
|
||||
});
|
||||
} else {
|
||||
stackedChildren = positionedLayout.sublayouts;
|
||||
p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutElementStyle.spacingAfter + spacingAfterBaseline, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -147,10 +151,10 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
|
||||
|
||||
*/
|
||||
const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(),
|
||||
[&](ASLayout *a, ASLayout *b) {
|
||||
return maxDimensionForLayout(a, style) < maxDimensionForLayout(b, style);
|
||||
[&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b) {
|
||||
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 maxCrossSize = crossDimension(style.direction, constrainedSize.max);
|
||||
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
|
||||
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){
|
||||
return a.position.y + a.size.height < b.position.y + b.size.height;
|
||||
const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASStackLayoutSpecItem &a, const ASStackLayoutSpecItem &b){
|
||||
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};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user