mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-20 21:29:00 +00:00
Cleanup ASTextNode and add locking
For now we use a big recursive lock. This needs to be revisited as we revisit the whole ASDK locking strategy.
This commit is contained in:
parent
22fa715682
commit
3d72a6b7e5
@ -11,22 +11,19 @@
|
|||||||
#import "ASTextNode.h"
|
#import "ASTextNode.h"
|
||||||
#import "ASTextNode+Beta.h"
|
#import "ASTextNode+Beta.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#import <AsyncDisplayKit/_ASDisplayLayer.h>
|
#import <AsyncDisplayKit/_ASDisplayLayer.h>
|
||||||
#import <AsyncDisplayKit/ASAssert.h>
|
|
||||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
|
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
|
||||||
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
|
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
|
|
||||||
#import "ASTextKitCoreTextAdditions.h"
|
#import "ASTextKitCoreTextAdditions.h"
|
||||||
#import "ASTextKitComponents.h"
|
|
||||||
#import "ASTextKitFontSizeAdjuster.h"
|
|
||||||
#import "ASTextKitRenderer.h"
|
|
||||||
#import "ASTextKitRenderer+Positioning.h"
|
#import "ASTextKitRenderer+Positioning.h"
|
||||||
#import "ASTextKitShadower.h"
|
#import "ASTextKitShadower.h"
|
||||||
|
|
||||||
#import "ASInternalHelpers.h"
|
#import "ASInternalHelpers.h"
|
||||||
#import "ASEqualityHelpers.h"
|
|
||||||
#import "ASLayout.h"
|
#import "ASLayout.h"
|
||||||
|
|
||||||
static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15;
|
static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15;
|
||||||
@ -38,15 +35,13 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
@interface ASTextNodeDrawParameters : NSObject
|
@interface ASTextNodeDrawParameters : NSObject
|
||||||
|
|
||||||
@property (nonatomic, assign, readonly) CGRect bounds;
|
@property (nonatomic, assign, readonly) CGRect bounds;
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) UIColor *backgroundColor;
|
@property (nonatomic, strong, readonly) UIColor *backgroundColor;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASTextNodeDrawParameters
|
@implementation ASTextNodeDrawParameters
|
||||||
|
|
||||||
- (instancetype)initWithBounds:(CGRect)bounds
|
- (instancetype)initWithBounds:(CGRect)bounds backgroundColor:(UIColor *)backgroundColor
|
||||||
backgroundColor:(UIColor *)backgroundColor
|
|
||||||
{
|
{
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_bounds = bounds;
|
_bounds = bounds;
|
||||||
@ -76,7 +71,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
NSRange _highlightRange;
|
NSRange _highlightRange;
|
||||||
ASHighlightOverlayLayer *_activeHighlightLayer;
|
ASHighlightOverlayLayer *_activeHighlightLayer;
|
||||||
|
|
||||||
ASDN::Mutex _rendererLock;
|
std::recursive_mutex _textLock;
|
||||||
|
|
||||||
CGSize _constrainedSize;
|
CGSize _constrainedSize;
|
||||||
|
|
||||||
@ -160,6 +155,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||||
NSString *truncationString = [_composedTruncationText string];
|
NSString *truncationString = [_composedTruncationText string];
|
||||||
if (plainString.length > 50)
|
if (plainString.length > 50)
|
||||||
@ -227,7 +224,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds
|
- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_rendererLock);
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (_renderer == nil) {
|
if (_renderer == nil) {
|
||||||
CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size;
|
CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size;
|
||||||
_renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes]
|
_renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes]
|
||||||
@ -250,9 +248,28 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)_invalidateRendererIfNeeded
|
||||||
|
{
|
||||||
|
[self _invalidateRendererIfNeededForBoundsSize:self.threadSafeBounds.size];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_invalidateRendererIfNeededForBoundsSize:(CGSize)boundsSize
|
||||||
|
{
|
||||||
|
if ([self _needInvalidateRendererForBoundsSize:boundsSize]) {
|
||||||
|
// Our bounds of frame have changed to a size that is not identical to our constraining size,
|
||||||
|
// so our previous layout information is invalid, and TextKit may draw at the
|
||||||
|
// incorrect origin.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
_constrainedSize = CGSizeMake(-INFINITY, -INFINITY);
|
||||||
|
}
|
||||||
|
[self _invalidateRenderer];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)_invalidateRenderer
|
- (void)_invalidateRenderer
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_rendererLock);
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (_renderer) {
|
if (_renderer) {
|
||||||
// Destruction of the layout managers/containers/text storage is quite
|
// Destruction of the layout managers/containers/text storage is quite
|
||||||
@ -267,27 +284,13 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_invalidateRendererIfNeeded
|
|
||||||
{
|
|
||||||
[self _invalidateRendererIfNeededForBoundsSize:self.threadSafeBounds.size];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_invalidateRendererIfNeededForBoundsSize:(CGSize)boundsSize
|
|
||||||
{
|
|
||||||
if ([self _needInvalidateRendererForBoundsSize:boundsSize]) {
|
|
||||||
// Our bounds of frame have changed to a size that is not identical to our constraining size,
|
|
||||||
// so our previous layout information is invalid, and TextKit may draw at the
|
|
||||||
// incorrect origin.
|
|
||||||
_constrainedSize = CGSizeMake(-INFINITY, -INFINITY);
|
|
||||||
[self _invalidateRenderer];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Layout and Sizing
|
#pragma mark - Layout and Sizing
|
||||||
|
|
||||||
- (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize
|
- (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize
|
||||||
{
|
{
|
||||||
if (!_renderer) {
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
|
if (_renderer == nil) {
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,6 +325,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
[super calculatedLayoutDidChange];
|
[super calculatedLayoutDidChange];
|
||||||
|
|
||||||
ASLayout *layout = self.calculatedLayout;
|
ASLayout *layout = self.calculatedLayout;
|
||||||
|
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
if (layout != nil) {
|
if (layout != nil) {
|
||||||
_constrainedSize = layout.size;
|
_constrainedSize = layout.size;
|
||||||
_renderer.constrainedSize = layout.size;
|
_renderer.constrainedSize = layout.size;
|
||||||
@ -333,6 +338,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width);
|
ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width);
|
||||||
ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height);
|
ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height);
|
||||||
|
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
_constrainedSize = constrainedSize;
|
_constrainedSize = constrainedSize;
|
||||||
|
|
||||||
// Instead of invalidating the renderer, in case this is a new call with a different constrained size,
|
// Instead of invalidating the renderer, in case this is a new call with a different constrained size,
|
||||||
@ -341,7 +348,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
[self setNeedsDisplay];
|
[self setNeedsDisplay];
|
||||||
|
|
||||||
CGSize size = [[self _renderer] size];
|
CGSize size = [self _renderer].size;
|
||||||
if (_attributedText.length > 0) {
|
if (_attributedText.length > 0) {
|
||||||
CGFloat screenScale = ASScreenScale();
|
CGFloat screenScale = ASScreenScale();
|
||||||
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||||
@ -359,6 +366,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)setAttributedText:(NSAttributedString *)attributedText
|
- (void)setAttributedText:(NSAttributedString *)attributedText
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (attributedText == nil) {
|
if (attributedText == nil) {
|
||||||
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
|
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
|
||||||
}
|
}
|
||||||
@ -388,16 +397,18 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
[self setNeedsDisplay];
|
[self setNeedsDisplay];
|
||||||
|
|
||||||
self.accessibilityLabel = _attributedText.string;
|
|
||||||
|
|
||||||
// We're an accessibility element by default if there is a string.
|
// Accessiblity
|
||||||
self.isAccessibilityElement = _attributedText.length != 0;
|
self.accessibilityLabel = _attributedText.string;
|
||||||
|
self.isAccessibilityElement = (_attributedText.length != 0); // We're an accessibility element by default if there is a string.
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Text Layout
|
#pragma mark - Text Layout
|
||||||
|
|
||||||
- (void)setExclusionPaths:(NSArray *)exclusionPaths
|
- (void)setExclusionPaths:(NSArray *)exclusionPaths
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (ASObjectIsEqual(exclusionPaths, _exclusionPaths)) {
|
if (ASObjectIsEqual(exclusionPaths, _exclusionPaths)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -410,6 +421,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (NSArray *)exclusionPaths
|
- (NSArray *)exclusionPaths
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return _exclusionPaths;
|
return _exclusionPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +430,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
|
- (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
ASDisplayNodeAssert(context, @"This is no good without a context.");
|
ASDisplayNodeAssert(context, @"This is no good without a context.");
|
||||||
|
|
||||||
@ -437,7 +452,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw shadow
|
// Draw shadow
|
||||||
[[renderer shadower] setShadowInContext:context];
|
[renderer.shadower setShadowInContext:context];
|
||||||
|
|
||||||
// Draw text
|
// Draw text
|
||||||
bounds.origin = textOrigin;
|
bounds.origin = textOrigin;
|
||||||
@ -470,6 +485,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut
|
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut
|
||||||
forHighlighting:(BOOL)highlighting
|
forHighlighting:(BOOL)highlighting
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
ASTextKitRenderer *renderer = [self _renderer];
|
ASTextKitRenderer *renderer = [self _renderer];
|
||||||
NSRange visibleRange = renderer.firstVisibleRange;
|
NSRange visibleRange = renderer.firstVisibleRange;
|
||||||
NSAttributedString *attributedString = _attributedText;
|
NSAttributedString *attributedString = _attributedText;
|
||||||
@ -562,6 +579,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (gestureRecognizer == _longPressGestureRecognizer) {
|
if (gestureRecognizer == _longPressGestureRecognizer) {
|
||||||
// Don't allow long press on truncation message
|
// Don't allow long press on truncation message
|
||||||
if ([self _pendingTruncationTap]) {
|
if ([self _pendingTruncationTap]) {
|
||||||
@ -593,6 +612,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (NSRange)highlightRange
|
- (NSRange)highlightRange
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
return _highlightRange;
|
return _highlightRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,6 +624,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated
|
- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
[self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated];
|
[self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,7 +695,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count];
|
NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count];
|
||||||
for (NSValue *rectValue in highlightRects) {
|
for (NSValue *rectValue in highlightRects) {
|
||||||
UIEdgeInsets shadowPadding = _renderer.shadower.shadowPadding;
|
UIEdgeInsets shadowPadding = _renderer.shadower.shadowPadding;
|
||||||
CGRect rendererRect = [[self class] _adjustRendererRect:rectValue.CGRectValue forShadowPadding:shadowPadding];
|
CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.CGRectValue, shadowPadding);
|
||||||
CGRect highlightedRect = [self.layer convertRect:rendererRect toLayer:highlightTargetLayer];
|
CGRect highlightedRect = [self.layer convertRect:rendererRect toLayer:highlightTargetLayer];
|
||||||
|
|
||||||
// We set our overlay layer's frame to the bounds of the highlight target layer.
|
// We set our overlay layer's frame to the bounds of the highlight target layer.
|
||||||
@ -709,6 +732,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)_clearHighlightIfNecessary
|
- (void)_clearHighlightIfNecessary
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if ([self _pendingLinkTap] || [self _pendingTruncationTap]) {
|
if ([self _pendingLinkTap] || [self _pendingTruncationTap]) {
|
||||||
[self setHighlightRange:NSMakeRange(0, 0) animated:YES];
|
[self setHighlightRange:NSMakeRange(0, 0) animated:YES];
|
||||||
}
|
}
|
||||||
@ -726,29 +751,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
#pragma mark - Text rects
|
#pragma mark - Text rects
|
||||||
|
|
||||||
+ (CGRect)_adjustRendererRect:(CGRect)rendererRect forShadowPadding:(UIEdgeInsets)shadowPadding
|
static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UIEdgeInsets shadowPadding) {
|
||||||
{
|
|
||||||
rendererRect.origin.x -= shadowPadding.left;
|
rendererRect.origin.x -= shadowPadding.left;
|
||||||
rendererRect.origin.y -= shadowPadding.top;
|
rendererRect.origin.y -= shadowPadding.top;
|
||||||
return rendererRect;
|
return rendererRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption
|
|
||||||
{
|
|
||||||
NSArray *rects = [[self _renderer] rectsForTextRange:textRange measureOption:measureOption];
|
|
||||||
NSMutableArray *adjustedRects = [NSMutableArray array];
|
|
||||||
|
|
||||||
for (NSValue *rectValue in rects) {
|
|
||||||
CGRect rect = [rectValue CGRectValue];
|
|
||||||
rect = [self.class _adjustRendererRect:rect forShadowPadding:self.shadowPadding];
|
|
||||||
|
|
||||||
NSValue *adjustedRectValue = [NSValue valueWithCGRect:rect];
|
|
||||||
[adjustedRects addObject:adjustedRectValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
return adjustedRects;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)rectsForTextRange:(NSRange)textRange
|
- (NSArray *)rectsForTextRange:(NSRange)textRange
|
||||||
{
|
{
|
||||||
return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionCapHeight];
|
return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionCapHeight];
|
||||||
@ -759,22 +767,46 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionBlock];
|
return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionBlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
|
NSArray *rects = [[self _renderer] rectsForTextRange:textRange measureOption:measureOption];
|
||||||
|
NSMutableArray *adjustedRects = [NSMutableArray array];
|
||||||
|
|
||||||
|
for (NSValue *rectValue in rects) {
|
||||||
|
CGRect rect = [rectValue CGRectValue];
|
||||||
|
rect = ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding);
|
||||||
|
|
||||||
|
NSValue *adjustedRectValue = [NSValue valueWithCGRect:rect];
|
||||||
|
[adjustedRects addObject:adjustedRectValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustedRects;
|
||||||
|
}
|
||||||
|
|
||||||
- (CGRect)trailingRect
|
- (CGRect)trailingRect
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
CGRect rect = [[self _renderer] trailingRect];
|
CGRect rect = [[self _renderer] trailingRect];
|
||||||
return [self.class _adjustRendererRect:rect forShadowPadding:self.shadowPadding];
|
return ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGRect)frameForTextRange:(NSRange)textRange
|
- (CGRect)frameForTextRange:(NSRange)textRange
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
CGRect frame = [[self _renderer] frameForTextRange:textRange];
|
CGRect frame = [[self _renderer] frameForTextRange:textRange];
|
||||||
return [self.class _adjustRendererRect:frame forShadowPadding:self.shadowPadding];
|
return ASTextNodeAdjustRenderRectForShadowPadding(frame, self.shadowPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Placeholders
|
#pragma mark - Placeholders
|
||||||
|
|
||||||
- (void)setPlaceholderColor:(UIColor *)placeholderColor
|
- (void)setPlaceholderColor:(UIColor *)placeholderColor
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
_placeholderColor = placeholderColor;
|
_placeholderColor = placeholderColor;
|
||||||
|
|
||||||
// prevent placeholders if we don't have a color
|
// prevent placeholders if we don't have a color
|
||||||
@ -790,6 +822,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
UIGraphicsBeginImageContext(size);
|
UIGraphicsBeginImageContext(size);
|
||||||
[self.placeholderColor setFill];
|
[self.placeholderColor setFill];
|
||||||
|
|
||||||
@ -816,8 +850,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
#pragma mark - Touch Handling
|
#pragma mark - Touch Handling
|
||||||
|
|
||||||
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (!_passthroughNonlinkTouches) {
|
if (!_passthroughNonlinkTouches) {
|
||||||
return [super pointInside:point withEvent:event];
|
return [super pointInside:point withEvent:event];
|
||||||
}
|
}
|
||||||
@ -846,9 +882,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
[super touchesBegan:touches withEvent:event];
|
|
||||||
|
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
[super touchesBegan:touches withEvent:event];
|
||||||
|
|
||||||
CGPoint point = [[touches anyObject] locationInView:self.view];
|
CGPoint point = [[touches anyObject] locationInView:self.view];
|
||||||
|
|
||||||
@ -878,6 +913,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
[super touchesCancelled:touches withEvent:event];
|
[super touchesCancelled:touches withEvent:event];
|
||||||
|
|
||||||
[self _clearHighlightIfNecessary];
|
[self _clearHighlightIfNecessary];
|
||||||
@ -885,6 +921,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
[super touchesEnded:touches withEvent:event];
|
[super touchesEnded:touches withEvent:event];
|
||||||
|
|
||||||
if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) {
|
if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) {
|
||||||
@ -903,6 +940,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
[super touchesMoved:touches withEvent:event];
|
[super touchesMoved:touches withEvent:event];
|
||||||
|
|
||||||
UITouch *touch = [touches anyObject];
|
UITouch *touch = [touches anyObject];
|
||||||
@ -928,6 +966,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
|
- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
// Respond to long-press when it begins, not when it ends.
|
// Respond to long-press when it begins, not when it ends.
|
||||||
if (longPressRecognizer.state == UIGestureRecognizerStateBegan) {
|
if (longPressRecognizer.state == UIGestureRecognizerStateBegan) {
|
||||||
if ([self.delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) {
|
if ([self.delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) {
|
||||||
@ -939,11 +979,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (BOOL)_pendingLinkTap
|
- (BOOL)_pendingLinkTap
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil;
|
return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_pendingTruncationTap
|
- (BOOL)_pendingTruncationTap
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName];
|
return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -951,11 +995,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (CGColorRef)shadowColor
|
- (CGColorRef)shadowColor
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return _shadowColor;
|
return _shadowColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setShadowColor:(CGColorRef)shadowColor
|
- (void)setShadowColor:(CGColorRef)shadowColor
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (_shadowColor != shadowColor) {
|
if (_shadowColor != shadowColor) {
|
||||||
if (shadowColor != NULL) {
|
if (shadowColor != NULL) {
|
||||||
CGColorRetain(shadowColor);
|
CGColorRetain(shadowColor);
|
||||||
@ -968,11 +1016,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (CGSize)shadowOffset
|
- (CGSize)shadowOffset
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return _shadowOffset;
|
return _shadowOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setShadowOffset:(CGSize)shadowOffset
|
- (void)setShadowOffset:(CGSize)shadowOffset
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) {
|
if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) {
|
||||||
_shadowOffset = shadowOffset;
|
_shadowOffset = shadowOffset;
|
||||||
[self _invalidateRenderer];
|
[self _invalidateRenderer];
|
||||||
@ -982,11 +1034,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (CGFloat)shadowOpacity
|
- (CGFloat)shadowOpacity
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return _shadowOpacity;
|
return _shadowOpacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setShadowOpacity:(CGFloat)shadowOpacity
|
- (void)setShadowOpacity:(CGFloat)shadowOpacity
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (_shadowOpacity != shadowOpacity) {
|
if (_shadowOpacity != shadowOpacity) {
|
||||||
_shadowOpacity = shadowOpacity;
|
_shadowOpacity = shadowOpacity;
|
||||||
[self _invalidateRenderer];
|
[self _invalidateRenderer];
|
||||||
@ -996,11 +1052,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (CGFloat)shadowRadius
|
- (CGFloat)shadowRadius
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return _shadowRadius;
|
return _shadowRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setShadowRadius:(CGFloat)shadowRadius
|
- (void)setShadowRadius:(CGFloat)shadowRadius
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (_shadowRadius != shadowRadius) {
|
if (_shadowRadius != shadowRadius) {
|
||||||
_shadowRadius = shadowRadius;
|
_shadowRadius = shadowRadius;
|
||||||
[self _invalidateRenderer];
|
[self _invalidateRenderer];
|
||||||
@ -1015,6 +1075,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
|
|
||||||
- (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer
|
- (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return renderer.shadower.shadowPadding;
|
return renderer.shadower.shadowPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,6 +1094,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
|
|
||||||
- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText
|
- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) {
|
if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1042,6 +1106,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
|
|
||||||
- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage
|
- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) {
|
if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1052,6 +1118,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
|
|
||||||
- (void)setTruncationMode:(NSLineBreakMode)truncationMode
|
- (void)setTruncationMode:(NSLineBreakMode)truncationMode
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if (_truncationMode != truncationMode) {
|
if (_truncationMode != truncationMode) {
|
||||||
_truncationMode = truncationMode;
|
_truncationMode = truncationMode;
|
||||||
[self _invalidateRenderer];
|
[self _invalidateRenderer];
|
||||||
@ -1061,12 +1129,16 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
|
|
||||||
- (BOOL)isTruncated
|
- (BOOL)isTruncated
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
ASTextKitRenderer *renderer = [self _renderer];
|
ASTextKitRenderer *renderer = [self _renderer];
|
||||||
return renderer.firstVisibleRange.length < _attributedText.length;
|
return renderer.firstVisibleRange.length < _attributedText.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors
|
- (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors] == NO) {
|
if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors] == NO) {
|
||||||
_pointSizeScaleFactors = pointSizeScaleFactors;
|
_pointSizeScaleFactors = pointSizeScaleFactors;
|
||||||
[self _invalidateRenderer];
|
[self _invalidateRenderer];
|
||||||
@ -1075,15 +1147,19 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
|
|
||||||
- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines
|
- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines
|
||||||
{
|
{
|
||||||
if (_maximumNumberOfLines != maximumNumberOfLines) {
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
_maximumNumberOfLines = maximumNumberOfLines;
|
|
||||||
[self _invalidateRenderer];
|
if (_maximumNumberOfLines != maximumNumberOfLines) {
|
||||||
[self setNeedsDisplay];
|
_maximumNumberOfLines = maximumNumberOfLines;
|
||||||
}
|
[self _invalidateRenderer];
|
||||||
|
[self setNeedsDisplay];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)lineCount
|
- (NSUInteger)lineCount
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
return [[self _renderer] lineCount];
|
return [[self _renderer] lineCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1091,6 +1167,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
|
|
||||||
- (void)_updateComposedTruncationText
|
- (void)_updateComposedTruncationText
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
_composedTruncationText = [self _prepareTruncationStringForDrawing:[self _composedTruncationText]];
|
_composedTruncationText = [self _prepareTruncationStringForDrawing:[self _composedTruncationText]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1107,6 +1185,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
*/
|
*/
|
||||||
- (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange
|
- (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
// Check if we even have an additional truncation message.
|
// Check if we even have an additional truncation message.
|
||||||
if (!_additionalTruncationMessage) {
|
if (!_additionalTruncationMessage) {
|
||||||
return NSMakeRange(NSNotFound, 0);
|
return NSMakeRange(NSNotFound, 0);
|
||||||
@ -1118,8 +1198,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length;
|
NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length;
|
||||||
// We get the location of the truncation token, then add the length of the
|
// We get the location of the truncation token, then add the length of the
|
||||||
// truncation attributed string +1 for the space between.
|
// truncation attributed string +1 for the space between.
|
||||||
NSRange range = NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength);
|
return NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength);
|
||||||
return range;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1129,6 +1208,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
*/
|
*/
|
||||||
- (NSAttributedString *)_composedTruncationText
|
- (NSAttributedString *)_composedTruncationText
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
//If we have neither return the default
|
//If we have neither return the default
|
||||||
if (!_additionalTruncationMessage && !_truncationAttributedText) {
|
if (!_additionalTruncationMessage && !_truncationAttributedText) {
|
||||||
return _composedTruncationText;
|
return _composedTruncationText;
|
||||||
@ -1157,6 +1238,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
|||||||
*/
|
*/
|
||||||
- (NSAttributedString *)_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString
|
- (NSAttributedString *)_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> l(_textLock);
|
||||||
|
|
||||||
truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString);
|
truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString);
|
||||||
NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy];
|
NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy];
|
||||||
// Grab the attributes from the full string
|
// Grab the attributes from the full string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user