mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Shuffle & Cleanup Stuff (#3080)
This commit is contained in:
131
Source/TextKit/ASTextNodeWordKerner.m
Normal file
131
Source/TextKit/ASTextNodeWordKerner.m
Normal file
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// ASTextNodeWordKerner.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASTextNodeWordKerner.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASTextNodeTypes.h>
|
||||
|
||||
@implementation ASTextNodeWordKerner
|
||||
|
||||
#pragma mark - NSLayoutManager Delegate
|
||||
- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)properties characterIndexes:(const NSUInteger *)characterIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange
|
||||
{
|
||||
NSUInteger glyphCount = glyphRange.length;
|
||||
NSGlyphProperty *newGlyphProperties = NULL;
|
||||
|
||||
BOOL usesWordKerning = NO;
|
||||
|
||||
// If our typing attributes specify word kerning, specify the spaces as whitespace control characters so we can customize their width.
|
||||
// Are any of the characters spaces?
|
||||
NSString *textStorageString = layoutManager.textStorage.string;
|
||||
for (NSUInteger arrayIndex = 0; arrayIndex < glyphCount; arrayIndex++) {
|
||||
NSUInteger characterIndex = characterIndexes[arrayIndex];
|
||||
if ([textStorageString characterAtIndex:characterIndex] != ' ')
|
||||
continue;
|
||||
|
||||
// If we've set the whitespace control character for this space already, we have nothing to do.
|
||||
if (properties[arrayIndex] == NSGlyphPropertyControlCharacter) {
|
||||
usesWordKerning = YES;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new glyph properties, if necessary.
|
||||
if (!newGlyphProperties) {
|
||||
newGlyphProperties = (NSGlyphProperty *)malloc(sizeof(NSGlyphProperty) * glyphCount);
|
||||
memcpy(newGlyphProperties, properties, (sizeof(NSGlyphProperty) * glyphCount));
|
||||
}
|
||||
|
||||
// It's a space. Make it a whitespace control character.
|
||||
newGlyphProperties[arrayIndex] = NSGlyphPropertyControlCharacter;
|
||||
}
|
||||
|
||||
// If we don't have any custom glyph properties, return 0 to indicate to the layout manager that it should use the standard glyphs+properties.
|
||||
if (!newGlyphProperties) {
|
||||
if (usesWordKerning) {
|
||||
// If the text does use word kerning we have to make sure we return the correct glyphCount, or the layout manager will just use the default properties and ignore our kerning.
|
||||
[layoutManager setGlyphs:glyphs properties:properties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange];
|
||||
return glyphCount;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, use our custom glyph properties.
|
||||
[layoutManager setGlyphs:glyphs properties:newGlyphProperties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange];
|
||||
free(newGlyphProperties);
|
||||
|
||||
return glyphCount;
|
||||
}
|
||||
|
||||
- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)defaultAction forControlCharacterAtIndex:(NSUInteger)characterIndex
|
||||
{
|
||||
// If it's a space character and we have custom word kerning, use the whitespace action control character.
|
||||
if ([layoutManager.textStorage.string characterAtIndex:characterIndex] == ' ')
|
||||
return NSControlCharacterActionWhitespace;
|
||||
|
||||
return defaultAction;
|
||||
}
|
||||
|
||||
- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)characterIndex
|
||||
{
|
||||
CGFloat wordKernedSpaceWidth = [self _wordKernedSpaceWidthForCharacterAtIndex:characterIndex atGlyphPosition:glyphPosition forTextContainer:textContainer layoutManager:layoutManager];
|
||||
return CGRectMake(glyphPosition.x, glyphPosition.y, wordKernedSpaceWidth, CGRectGetHeight(proposedRect));
|
||||
}
|
||||
|
||||
- (CGFloat)_wordKernedSpaceWidthForCharacterAtIndex:(NSUInteger)characterIndex atGlyphPosition:(CGPoint)glyphPosition forTextContainer:(NSTextContainer *)textContainer layoutManager:(NSLayoutManager *)layoutManager
|
||||
{
|
||||
// We use a map table for pointer equality and non-copying keys.
|
||||
static NSMapTable *spaceSizes;
|
||||
// NSMapTable is a defined thread unsafe class, so we need to synchronize
|
||||
// access in a light manner. So we use dispatch_sync on this queue for all
|
||||
// access to the map table.
|
||||
static dispatch_queue_t mapQueue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
spaceSizes = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:1];
|
||||
mapQueue = dispatch_queue_create("org.AsyncDisplayKit.wordKerningQueue", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
CGFloat ordinarySpaceWidth;
|
||||
UIFont *font = [layoutManager.textStorage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL];
|
||||
CGFloat wordKerning = [[layoutManager.textStorage attribute:ASTextNodeWordKerningAttributeName atIndex:characterIndex effectiveRange:NULL] floatValue];
|
||||
__block NSNumber *ordinarySpaceSizeValue;
|
||||
dispatch_sync(mapQueue, ^{
|
||||
ordinarySpaceSizeValue = [spaceSizes objectForKey:font];
|
||||
});
|
||||
if (ordinarySpaceSizeValue == nil) {
|
||||
ordinarySpaceWidth = [@" " sizeWithAttributes:@{ NSFontAttributeName : font }].width;
|
||||
dispatch_async(mapQueue, ^{
|
||||
[spaceSizes setObject:@(ordinarySpaceWidth) forKey:font];
|
||||
});
|
||||
} else {
|
||||
ordinarySpaceWidth = [ordinarySpaceSizeValue floatValue];
|
||||
}
|
||||
|
||||
CGFloat totalKernedWidth = (ordinarySpaceWidth + wordKerning);
|
||||
|
||||
// TextKit normally handles whitespace by increasing the advance of the previous glyph, rather than displaying an
|
||||
// actual glyph for the whitespace itself. However, in order to implement word kerning, we explicitly require a
|
||||
// discrete glyph whose bounding box we can specify. The problem is that TextKit does not know this glyph is
|
||||
// invisible. From TextKit's perspective, this whitespace glyph is a glyph that MUST be displayed. Thus when it
|
||||
// comes to determining linebreaks, the width of this trailing whitespace glyph is considered. This causes
|
||||
// our text to wrap sooner than it otherwise would, as room is allocated at the end of each line for a glyph that
|
||||
// isn't actually visible. To implement our desired behavior, we check to see if the current whitespace glyph
|
||||
// would break to the next line. If it breaks to the next line, then this constitutes trailing whitespace, and
|
||||
// we specify enough room to fill up the remainder of the line, but nothing more.
|
||||
if (glyphPosition.x + totalKernedWidth > textContainer.size.width) {
|
||||
return (textContainer.size.width - glyphPosition.x);
|
||||
}
|
||||
|
||||
return totalKernedWidth;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user