mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Fixes to baseline stack alignment
1) Set the ascender/descender of an ASTextNode when the attributeString is set. Previously ascender/descender were only being computed in `setValuesFromLayoutable` and only when the attribute string was not nil. May make sense to remove the computation from `setValuesFromLayoutable` entirely. 2) Remove ability to allow different children of a stack spec to aling to different baselines. This wasn't working before and I'm not convinced it is possible to do properly/useful enough to invest the time. 3) Have all stack spec run `ASStackBaselinePositionedLayout::compute` to compute the stack's ascender and descender. Even if the stack isn't aligning its children to a baseline, the stack itself may be a child of another stack that IS aligning to a baseline.
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
#import <AsyncDisplayKit/ASTextNodeTextKitHelpers.h>
|
#import <AsyncDisplayKit/ASTextNodeTextKitHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
|
|
||||||
|
#import "ASInternalHelpers.h"
|
||||||
#import "ASTextNodeRenderer.h"
|
#import "ASTextNodeRenderer.h"
|
||||||
#import "ASTextNodeShadower.h"
|
#import "ASTextNodeShadower.h"
|
||||||
#import "ASEqualityHelpers.h"
|
#import "ASEqualityHelpers.h"
|
||||||
@@ -344,6 +345,12 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
self.isAccessibilityElement = YES;
|
self.isAccessibilityElement = YES;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (attributedString.length > 0) {
|
||||||
|
CGFloat screenScale = ASScreenScale();
|
||||||
|
self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||||
|
self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Text Layout
|
#pragma mark - Text Layout
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) {
|
|||||||
ASStackLayoutAlignItemsCenter,
|
ASStackLayoutAlignItemsCenter,
|
||||||
/** Expand children to fill cross axis */
|
/** Expand children to fill cross axis */
|
||||||
ASStackLayoutAlignItemsStretch,
|
ASStackLayoutAlignItemsStretch,
|
||||||
/** Children align to their first baseline. Only available for horizontal stack nodes */
|
/** Children align to their first baseline. Only available for horizontal stack spec */
|
||||||
ASStackLayoutAlignItemsBaselineFirst,
|
ASStackLayoutAlignItemsBaselineFirst,
|
||||||
/** Children align to their last baseline. Only available for horizontal stack nodes */
|
/** Children align to their last baseline. Only available for horizontal stack spec */
|
||||||
ASStackLayoutAlignItemsBaselineLast,
|
ASStackLayoutAlignItemsBaselineLast,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,8 +66,4 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
|
|||||||
ASStackLayoutAlignSelfCenter,
|
ASStackLayoutAlignSelfCenter,
|
||||||
/** Expand to fill cross axis */
|
/** Expand to fill cross axis */
|
||||||
ASStackLayoutAlignSelfStretch,
|
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,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -101,7 +101,6 @@
|
|||||||
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
|
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
|
||||||
for (id<ASLayoutable> child in self.children) {
|
for (id<ASLayoutable> child in self.children) {
|
||||||
stackChildren.push_back(child);
|
stackChildren.push_back(child);
|
||||||
needsBaselinePass |= child.alignSelf == ASStackLayoutAlignSelfBaselineFirst || child.alignSelf == ASStackLayoutAlignSelfBaselineLast;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize);
|
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize);
|
||||||
@@ -109,12 +108,21 @@
|
|||||||
|
|
||||||
CGSize finalSize = CGSizeZero;
|
CGSize finalSize = CGSizeZero;
|
||||||
NSArray *sublayouts = nil;
|
NSArray *sublayouts = nil;
|
||||||
if (needsBaselinePass) {
|
|
||||||
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
|
// regardless of whether or not this stack aligns to baseline, we should let ASStackBaselinePositionedLayout::compute find the max ascender
|
||||||
|
// and min descender in case this spec is a child in another spec that wants to align to a baseline.
|
||||||
|
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
|
||||||
|
if (self.direction == ASStackLayoutDirectionVertical) {
|
||||||
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
|
self.ascender = [[self.children firstObject] ascender];
|
||||||
|
self.descender = [[self.children lastObject] descender];
|
||||||
|
} else {
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
self.ascender = baselinePositionedLayout.ascender;
|
self.ascender = baselinePositionedLayout.ascender;
|
||||||
self.descender = baselinePositionedLayout.descender;
|
self.descender = baselinePositionedLayout.descender;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsBaselinePass) {
|
||||||
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize);
|
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize);
|
||||||
sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()];
|
sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
|
|||||||
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){
|
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){
|
||||||
return a.layoutableObject.ascender < b.layoutableObject.ascender;
|
return a.layoutableObject.ascender < b.layoutableObject.ascender;
|
||||||
});
|
});
|
||||||
const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender;
|
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.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.
|
||||||
@@ -103,33 +103,40 @@ 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.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
CGPoint p = CGPointZero;
|
std::vector<ASLayout *> stackedChildren;
|
||||||
BOOL first = YES;
|
// Only change positions of layouts this stackSpec is aligning to a baseline. Otherwise we are only here to
|
||||||
auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
|
// compute the min/max descender/ascender for this stack spec.
|
||||||
__weak id<ASLayoutable> child = l.layoutableObject;
|
if (style.baselineRelativeArrangement || style.alignItems == ASStackLayoutAlignItemsBaselineFirst || style.alignItems == ASStackLayoutAlignItemsBaselineLast) {
|
||||||
p = p + directionPoint(style.direction, child.spacingBefore, 0);
|
CGPoint p = CGPointZero;
|
||||||
if (first) {
|
BOOL first = YES;
|
||||||
// if this is the first item use the previously computed start point
|
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
|
||||||
p = l.position;
|
__weak id<ASLayoutable> child = l.layoutableObject;
|
||||||
} else {
|
p = p + directionPoint(style.direction, child.spacingBefore, 0);
|
||||||
// otherwise add the stack spacing
|
if (first) {
|
||||||
p = p + directionPoint(style.direction, style.spacing, 0);
|
// if this is the first item use the previously computed start point
|
||||||
}
|
p = l.position;
|
||||||
first = NO;
|
} else {
|
||||||
|
// otherwise add the stack spacing
|
||||||
// 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.
|
p = p + directionPoint(style.direction, style.spacing, 0);
|
||||||
l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline));
|
}
|
||||||
|
first = NO;
|
||||||
// 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.
|
// 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.
|
||||||
CGFloat spacingAfterBaseline = 0;
|
l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline));
|
||||||
if (style.direction == ASStackLayoutDirectionVertical) {
|
|
||||||
spacingAfterBaseline = child.descender;
|
// 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.
|
||||||
p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
|
CGFloat spacingAfterBaseline = 0;
|
||||||
|
if (style.direction == ASStackLayoutDirectionVertical) {
|
||||||
return l;
|
spacingAfterBaseline = child.descender;
|
||||||
});
|
}
|
||||||
|
p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
|
||||||
|
|
||||||
|
return l;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stackedChildren = positionedLayout.sublayouts;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Step 4: Since we have been mucking with positions, there is a chance that our cross size has changed. Imagine a node with a font size of 40
|
Step 4: Since we have been mucking with positions, there is a chance that our cross size has changed. Imagine a node with a font size of 40
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment,
|
|||||||
return ASStackLayoutAlignItemsStart;
|
return ASStackLayoutAlignItemsStart;
|
||||||
case ASStackLayoutAlignSelfStretch:
|
case ASStackLayoutAlignSelfStretch:
|
||||||
return ASStackLayoutAlignItemsStretch;
|
return ASStackLayoutAlignItemsStretch;
|
||||||
case ASStackLayoutAlignSelfBaselineFirst:
|
|
||||||
return ASStackLayoutAlignItemsBaselineFirst;
|
|
||||||
case ASStackLayoutAlignSelfBaselineLast:
|
|
||||||
return ASStackLayoutAlignItemsBaselineLast;
|
|
||||||
case ASStackLayoutAlignSelfAuto:
|
case ASStackLayoutAlignSelfAuto:
|
||||||
default:
|
default:
|
||||||
return stackAlignment;
|
return stackAlignment;
|
||||||
|
|||||||
Reference in New Issue
Block a user