Skip to content

Commit

Permalink
Add support for basic ASTextNode2 link highlighting (facebookarchive#…
Browse files Browse the repository at this point in the history
…1518)

* Add support for basic ASTextNode2 link highlighting

* Add approach to fix line highlighting

* Enable proper highlighting for first row

* Fix snapshot highlighting test for ASTextNode2

* Update screenshot again

* Address comments
  • Loading branch information
maicki authored and Adlai-Holler committed Jun 3, 2019
1 parent 1b4b6a3 commit ab8ea06
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Source/ASTextNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,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 @@ -825,7 +826,6 @@ - (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)
}

if (highlightTargetLayer != nil) {
ASLockScopeSelf();
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];

} 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ab8ea06

Please sign in to comment.