Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for basic ASTextNode2 link highlighting #1518

Merged
merged 6 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/ASTextNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,7 @@ - (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated
- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated
{
ASDisplayNodeAssertMainThread();
ASLockScopeSelf();

_highlightedLinkAttributeName = highlightedAttributeName;
_highlightedLinkAttributeValue = highlightedAttributeValue;
Expand Down Expand Up @@ -855,7 +856,6 @@ - (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)
}

if (highlightTargetLayer != nil) {
ASLockScopeSelf();
maicki marked this conversation as resolved.
Show resolved Hide resolved
ASTextKitRenderer *renderer = [self _locked_renderer];

NSArray *highlightRects = [renderer rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock];
Expand Down
113 changes: 109 additions & 4 deletions Source/ASTextNode2.mm
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ @implementation ASTextCacheValue
return layout;
}

static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15;
static const NSTimeInterval ASTextNodeHighlightFadeInDuration = 0.1;
static const CGFloat ASTextNodeHighlightLightOpacity = 0.11;
static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22;
static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute";
Expand Down Expand Up @@ -778,17 +780,114 @@ - (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated

- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated
{
ASDisplayNodeAssertMainThread();
ASLockScopeSelf(); // Protect usage of _highlight* ivars.

// Set these so that link tapping works.
_highlightedLinkAttributeName = highlightedAttributeName;
_highlightedLinkAttributeValue = highlightedAttributeValue;
_highlightRange = highlightRange;

AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
// Much of the code from original ASTextNode is probably usable here.
if (!NSEqualRanges(highlightRange, _highlightRange) && ((0 != highlightRange.length) || (0 != _highlightRange.length))) {

return;
_highlightRange = highlightRange;

if (_activeHighlightLayer) {
if (animated) {
__weak CALayer *weakHighlightLayer = _activeHighlightLayer;
_activeHighlightLayer = nil;

weakHighlightLayer.opacity = 0.0;

CFTimeInterval beginTime = CACurrentMediaTime();
CABasicAnimation *possibleFadeIn = (CABasicAnimation *)[weakHighlightLayer animationForKey:@"opacity"];
if (possibleFadeIn) {
// Calculate when we should begin fading out based on the end of the fade in animation,
// Also check to make sure that the new begin time hasn't already passed
CGFloat newBeginTime = (possibleFadeIn.beginTime + possibleFadeIn.duration);
if (newBeginTime > beginTime) {
beginTime = newBeginTime;
}
}

CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOut.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
fadeOut.fromValue = possibleFadeIn.toValue ? : @(((CALayer *)weakHighlightLayer.presentationLayer).opacity);
fadeOut.toValue = @0.0;
fadeOut.fillMode = kCAFillModeBoth;
fadeOut.duration = ASTextNodeHighlightFadeOutDuration;
fadeOut.beginTime = beginTime;

dispatch_block_t prev = [CATransaction completionBlock];
[CATransaction setCompletionBlock:^{
[weakHighlightLayer removeFromSuperlayer];
}];

[weakHighlightLayer addAnimation:fadeOut forKey:fadeOut.keyPath];

[CATransaction setCompletionBlock:prev];
maicki marked this conversation as resolved.
Show resolved Hide resolved

} else {
[_activeHighlightLayer removeFromSuperlayer];
_activeHighlightLayer = nil;
}
}
if (0 != highlightRange.length) {
// Find layer in hierarchy that allows us to draw highlighting on.
CALayer *highlightTargetLayer = self.layer;
while (highlightTargetLayer != nil) {
if (highlightTargetLayer.as_allowsHighlightDrawing) {
break;
}
highlightTargetLayer = highlightTargetLayer.superlayer;
}

if (highlightTargetLayer != nil) {
// TODO: The copy and application of size shouldn't be required, but it is currently.
// See discussion in https://github.com/TextureGroup/Texture/pull/396
ASTextContainer *textContainerCopy = [_textContainer copy];
textContainerCopy.size = self.calculatedSize;
ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(textContainerCopy, _attributedText);

NSArray<ASTextSelectionRect *> *highlightRects = [layout selectionRectsWithoutStartAndEndForRange:[ASTextRange rangeWithRange:highlightRange]];
NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count];

CALayer *layer = self.layer;
UIEdgeInsets shadowPadding = self.shadowPadding;
for (ASTextSelectionRect *rectValue in highlightRects) {
// Adjust shadow padding
CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.rect, shadowPadding);
CGRect highlightedRect = [layer convertRect:rendererRect toLayer:highlightTargetLayer];

// We set our overlay layer's frame to the bounds of the highlight target layer.
// Offset highlight rects to avoid double-counting target layer's bounds.origin.
highlightedRect.origin.x -= highlightTargetLayer.bounds.origin.x;
highlightedRect.origin.y -= highlightTargetLayer.bounds.origin.y;
[converted addObject:[NSValue valueWithCGRect:highlightedRect]];
}

ASHighlightOverlayLayer *overlayLayer = [[ASHighlightOverlayLayer alloc] initWithRects:converted];
overlayLayer.highlightColor = [[self class] _highlightColorForStyle:self.highlightStyle];
overlayLayer.frame = highlightTargetLayer.bounds;
overlayLayer.masksToBounds = NO;
overlayLayer.opacity = [[self class] _highlightOpacityForStyle:self.highlightStyle];
[highlightTargetLayer addSublayer:overlayLayer];

if (animated) {
CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeIn.fromValue = @0.0;
fadeIn.toValue = @(overlayLayer.opacity);
fadeIn.duration = ASTextNodeHighlightFadeInDuration;
fadeIn.beginTime = CACurrentMediaTime();

[overlayLayer addAnimation:fadeIn forKey:fadeIn.keyPath];
}

[overlayLayer setNeedsDisplay];

_activeHighlightLayer = overlayLayer;
}
}
}
}

- (void)_clearHighlightIfNecessary
Expand All @@ -812,6 +911,12 @@ + (CGFloat)_highlightOpacityForStyle:(ASTextNodeHighlightStyle)style

#pragma mark - Text rects

static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UIEdgeInsets shadowPadding) {
rendererRect.origin.x -= shadowPadding.left;
rendererRect.origin.y -= shadowPadding.top;
return rendererRect;
}

- (NSArray *)rectsForTextRange:(NSRange)textRange
{
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
Expand Down
4 changes: 3 additions & 1 deletion Source/Private/TextExperiment/Component/ASTextLayout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2146,7 +2146,9 @@ - (NSArray *)selectionRectsForRange:(ASTextRange *)range {
if (isVertical) {
topRect.rect = CGRectMake(startLine.left, topOffset, startLine.width, (_container.path ? startLine.bottom : _container.size.height - _container.insets.bottom) - topOffset);
} else {
topRect.rect = CGRectMake(topOffset, startLine.top, (_container.path ? startLine.right : _container.size.width - _container.insets.right) - topOffset, startLine.height);
// TODO: Fixes highlighting first row only to the end of the text and not highlight
// the while line to the end. Needs to brought over to multiline support
topRect.rect = CGRectMake(topOffset, startLine.top, (_container.path ? startLine.right : _container.size.width - _container.insets.right) - topOffset - (_container.size.width - _container.insets.right - startLine.right), startLine.height);
}
}
[rects addObject:topRect];
Expand Down
Binary file modified ..._10/ASTextNode2SnapshotTests/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.