From 7f6f2fed11aa009bb78629400b5bd7db8bed4ea7 Mon Sep 17 00:00:00 2001 From: rcancro Date: Thu, 28 Jan 2016 08:38:22 -0800 Subject: [PATCH 1/2] Added ASTextNodeWordKerner support to ASTextNode * Added a member to `ASTextKitAttributes` that is an optional delegate to the struct's NSLayoutManager. * Changed ASTextNode to set this delegate to an instance of ASTextNodeWordKerner. * Updated init method of `ASTextKitContext` to take an optional NSLayoutManager delegate * Added the files in TextKit folder to the public headers (so we can include ASTextNodeTypes.h) --- AsyncDisplayKit.podspec | 1 + AsyncDisplayKit.xcodeproj/project.pbxproj | 2 +- AsyncDisplayKit/ASTextNode.mm | 10 +++++++++- AsyncDisplayKit/TextKit/ASTextKitAttributes.h | 5 +++++ AsyncDisplayKit/TextKit/ASTextKitContext.h | 3 ++- AsyncDisplayKit/TextKit/ASTextKitContext.mm | 2 ++ AsyncDisplayKit/TextKit/ASTextKitRenderer.mm | 3 ++- .../TextKit/ASTextKitTailTruncater.mm | 4 ++-- AsyncDisplayKitTests/ASTextKitTruncationTests.mm | 16 +++++++++++----- examples/Kittens/Sample/KittenNode.mm | 4 +++- 10 files changed, 38 insertions(+), 12 deletions(-) diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index dfc8aad047..974b390886 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -13,6 +13,7 @@ Pod::Spec.new do |spec| 'AsyncDisplayKit/*.h', 'AsyncDisplayKit/Details/**/*.h', 'AsyncDisplayKit/Layout/*.h', + 'AsyncDisplayKit/TextKit/*.h', 'Base/*.h' ] diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index a7d2732172..eed741c815 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -137,7 +137,7 @@ 254C6B731BF94DF4003EC431 /* ASTextKitCoreTextAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754BB1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h */; }; 254C6B741BF94DF4003EC431 /* ASTextNodeWordKerner.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754B91BEE458E00737CA5 /* ASTextNodeWordKerner.h */; }; 254C6B751BF94DF4003EC431 /* ASTextKitHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754BA1BEE458E00737CA5 /* ASTextKitHelpers.h */; }; - 254C6B761BF94DF4003EC431 /* ASTextNodeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754BC1BEE458E00737CA5 /* ASTextNodeTypes.h */; }; + 254C6B761BF94DF4003EC431 /* ASTextNodeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754BC1BEE458E00737CA5 /* ASTextNodeTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 254C6B771BF94DF4003EC431 /* ASTextKitAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754951BEE44CD00737CA5 /* ASTextKitAttributes.h */; }; 254C6B781BF94DF4003EC431 /* ASTextKitContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754961BEE44CD00737CA5 /* ASTextKitContext.h */; }; 254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754981BEE44CD00737CA5 /* ASTextKitEntityAttribute.h */; }; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 8dbea6eb57..80e4f547da 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -21,6 +21,7 @@ #import "ASTextKitRenderer.h" #import "ASTextKitRenderer+Positioning.h" #import "ASTextKitShadower.h" +#import "ASTextNodeWordKerner.h" #import "ASInternalHelpers.h" #import "ASEqualityHelpers.h" @@ -74,7 +75,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation @end -@interface ASTextNode () +@interface ASTextNode () @end @@ -100,6 +101,10 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation ASTextKitRenderer *_renderer; UILongPressGestureRecognizer *_longPressGestureRecognizer; + + // Forwards NSLayoutManagerDelegate methods related to word kerning + ASTextNodeWordKerner *_wordKerner; + } @dynamic placeholderEnabled; @@ -143,6 +148,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // on the special placeholder behavior of ASTextNode. _placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); _placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0); + + _wordKerner = [[ASTextNodeWordKerner alloc] init]; } return self; @@ -257,6 +264,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; .maximumNumberOfLines = _maximumNumberOfLines, .exclusionPaths = _exclusionPaths, .minimumScaleFactor = _minimumScaleFactor, + .layoutManagerDelegate = _wordKerner, }; } diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h index 44835eee6d..d7bf44fef2 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h +++ b/AsyncDisplayKit/TextKit/ASTextKitAttributes.h @@ -89,6 +89,11 @@ struct ASTextKitAttributes { A pointer to a function that that returns a custom layout manager subclass. If nil, defaults to NSLayoutManager. */ NSLayoutManager *(*layoutManagerFactory)(void); + + /** + An optional delegate for the NSLayoutManager + */ + id layoutManagerDelegate; /** We provide an explicit copy function so we can use aggregate initializer syntax while providing copy semantics for diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.h b/AsyncDisplayKit/TextKit/ASTextKitContext.h index d9e6642f61..9fe6ab6af1 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.h +++ b/AsyncDisplayKit/TextKit/ASTextKitContext.h @@ -28,7 +28,8 @@ maximumNumberOfLines:(NSUInteger)maximumNumberOfLines exclusionPaths:(NSArray *)exclusionPaths constrainedSize:(CGSize)constrainedSize - layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory; + layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory + layoutManagerDelegate:(id)layoutManagerDelegate; @property (nonatomic, assign, readwrite) CGSize constrainedSize; diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.mm b/AsyncDisplayKit/TextKit/ASTextKitContext.mm index 2b682f9f26..f04d0ccf7f 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitContext.mm @@ -28,6 +28,7 @@ exclusionPaths:(NSArray *)exclusionPaths constrainedSize:(CGSize)constrainedSize layoutManagerFactory:(NSLayoutManager*(*)(void))layoutManagerFactory + layoutManagerDelegate:(id)layoutManagerDelegate { if (self = [super init]) { // Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock. @@ -37,6 +38,7 @@ _textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]); _layoutManager = layoutManagerFactory ? layoutManagerFactory() : [[NSLayoutManager alloc] init]; _layoutManager.usesFontLeading = NO; + _layoutManager.delegate = layoutManagerDelegate; [_textStorage addLayoutManager:_layoutManager]; _textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize]; // We want the text laid out up to the very edges of the container. diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm index 90527cc6f9..93d2a3d565 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm @@ -101,7 +101,8 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() maximumNumberOfLines:attributes.maximumNumberOfLines exclusionPaths:attributes.exclusionPaths constrainedSize:shadowConstrainedSize - layoutManagerFactory:attributes.layoutManagerFactory]; + layoutManagerFactory:attributes.layoutManagerFactory + layoutManagerDelegate:attributes.layoutManagerDelegate]; [self truncater]; } diff --git a/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm b/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm index 7617bfe82e..0c9870b3ce 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm @@ -72,8 +72,8 @@ maximumNumberOfLines:1 exclusionPaths:nil constrainedSize:constrainedRect.size - layoutManagerFactory:nil]; - + layoutManagerFactory:nil + layoutManagerDelegate:nil]; __block CGRect truncationUsedRect; [truncationContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *truncationLayoutManager, NSTextStorage *truncationTextStorage, NSTextContainer *truncationTextContainer) { diff --git a/AsyncDisplayKitTests/ASTextKitTruncationTests.mm b/AsyncDisplayKitTests/ASTextKitTruncationTests.mm index fc7e47f31f..2bd0ba330a 100644 --- a/AsyncDisplayKitTests/ASTextKitTruncationTests.mm +++ b/AsyncDisplayKitTests/ASTextKitTruncationTests.mm @@ -42,7 +42,8 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil]; + layoutManagerFactory:nil + layoutManagerDelegate:nil]; __block NSRange textKitVisibleRange; [context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { textKitVisibleRange = [layoutManager characterRangeForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] @@ -63,7 +64,8 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil]; + layoutManagerFactory:nil + layoutManagerDelegate:nil]; ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@""]]; @@ -85,7 +87,8 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil]; + layoutManagerFactory:nil + layoutManagerDelegate:nil]; ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; @@ -108,7 +111,8 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil]; + layoutManagerFactory:nil + layoutManagerDelegate:nil]; ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; @@ -132,7 +136,9 @@ maximumNumberOfLines:0 exclusionPaths:nil constrainedSize:constrainedSize - layoutManagerFactory:nil]; + layoutManagerFactory:nil + layoutManagerDelegate:nil]; + XCTAssertNoThrow([[ASTextKitTailTruncater alloc] initWithContext:context truncationAttributedString:[self _simpleTruncationAttributedString] avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]); diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 5c28fc440c..347460df50 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -16,6 +16,7 @@ #import #import +#import static const CGFloat kImageSize = 80.0f; static const CGFloat kOuterPadding = 16.0f; @@ -128,7 +129,8 @@ static const CGFloat kInnerPadding = 10.0f; style.hyphenationFactor = 1.0; return @{ NSFontAttributeName: font, - NSParagraphStyleAttributeName: style }; + NSParagraphStyleAttributeName: style, + ASTextNodeWordKerningAttributeName : @.5}; } #if UseAutomaticLayout From 715a678829cca323f4091f03e18fd8ad726f05e1 Mon Sep 17 00:00:00 2001 From: rcancro Date: Thu, 28 Jan 2016 15:18:45 -0800 Subject: [PATCH 2/2] scott's comments --- AsyncDisplayKit/ASTextNode.mm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 80e4f547da..39441ed1c2 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -102,7 +102,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation UILongPressGestureRecognizer *_longPressGestureRecognizer; - // Forwards NSLayoutManagerDelegate methods related to word kerning + ASDN::Mutex _wordKernerLock; ASTextNodeWordKerner *_wordKerner; } @@ -148,8 +148,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; // on the special placeholder behavior of ASTextNode. _placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); _placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0); - - _wordKerner = [[ASTextNodeWordKerner alloc] init]; } return self; @@ -264,7 +262,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; .maximumNumberOfLines = _maximumNumberOfLines, .exclusionPaths = _exclusionPaths, .minimumScaleFactor = _minimumScaleFactor, - .layoutManagerDelegate = _wordKerner, + .layoutManagerDelegate = [self _wordKerner], }; } @@ -300,6 +298,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } +- (ASTextNodeWordKerner *)_wordKerner +{ + ASDN::MutexLocker l(_wordKernerLock); + if (_wordKerner == nil) { + _wordKerner = [[ASTextNodeWordKerner alloc] init]; + } + return _wordKerner; +} + #pragma mark - Layout and Sizing - (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize