-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[ASTextNode2] Add initial implementation for link handling. #396
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -488,7 +488,54 @@ - (id)_linkAttributeValueAtPoint:(CGPoint)point | |
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut | ||
forHighlighting:(BOOL)highlighting | ||
{ | ||
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); | ||
ASDN::MutexLocker l(__instanceLock__); | ||
|
||
ASTextContainer *containerCopy = [_textContainer copy]; | ||
containerCopy.size = self.calculatedSize; | ||
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:containerCopy text:_attributedText]; | ||
NSRange visibleRange = layout.visibleRange; | ||
NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, _attributedText.length)); | ||
|
||
ASTextRange *range = [layout closestTextRangeAtPoint:point]; | ||
|
||
// For now, assume that a tap inside this text, but outside the text range is a tap on the | ||
// truncation token. | ||
if (![layout textRangeAtPoint:point]) { | ||
*inAdditionalTruncationMessageOut = YES; | ||
return nil; | ||
} | ||
|
||
NSRange effectiveRange = NSMakeRange(0, 0); | ||
for (NSString *attributeName in self.linkAttributeNames) { | ||
id value = [self.attributedText attribute:attributeName atIndex:range.start.offset longestEffectiveRange:&effectiveRange inRange:clampedRange]; | ||
NSString *name = attributeName; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: remove extra local variable or make the difference between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Doing this required adding a semi-ugly __strong to the iteration variable declaration, as the compiler does generate an error for writing to that variable without it. |
||
if (value == nil || name == nil) { | ||
// Didn't find anything | ||
continue; | ||
} | ||
|
||
// If highlighting, check with delegate first. If not implemented, assume YES. | ||
if (highlighting | ||
&& [_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)] | ||
&& ![_delegate textNode:(ASTextNode *)self shouldHighlightLinkAttribute:name value:value atPoint:point]) { | ||
value = nil; | ||
name = nil; | ||
} | ||
|
||
if (value != nil || name != nil) { | ||
*rangeOut = effectiveRange; | ||
if (NSMaxRange(*rangeOut) > NSMaxRange(visibleRange)) { | ||
(*rangeOut).length = MAX(NSMaxRange(visibleRange) - (*rangeOut).location, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it reasonable for this just to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good suggestion, done. |
||
} | ||
|
||
if (attributeNameOut != NULL) { | ||
*attributeNameOut = name; | ||
} | ||
|
||
return value; | ||
} | ||
} | ||
|
||
return nil; | ||
} | ||
|
||
|
@@ -560,8 +607,13 @@ - (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated | |
|
||
- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated | ||
{ | ||
// Set these so that link tapping works. | ||
_highlightedLinkAttributeName = highlightedAttributeName; | ||
_highlightedLinkAttributeValue = highlightedAttributeValue; | ||
|
||
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); | ||
// Much of the code from original ASTextNode is probably usable here. | ||
|
||
return; | ||
} | ||
|
||
|
@@ -665,7 +717,37 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event | |
ASDisplayNodeAssertMainThread(); | ||
|
||
[super touchesBegan:touches withEvent:event]; | ||
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); | ||
|
||
CGPoint point = [[touches anyObject] locationInView:self.view]; | ||
|
||
NSRange range = NSMakeRange(0, 0); | ||
NSString *linkAttributeName = nil; | ||
BOOL inAdditionalTruncationMessage = NO; | ||
|
||
id linkAttributeValue = [self _linkAttributeValueAtPoint:point | ||
attributeName:&linkAttributeName | ||
range:&range | ||
inAdditionalTruncationMessage:&inAdditionalTruncationMessage | ||
forHighlighting:YES]; | ||
|
||
NSUInteger lastCharIndex = NSIntegerMax; | ||
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); | ||
|
||
if (inAdditionalTruncationMessage) { | ||
NSRange visibleRange = NSMakeRange(0, 0); | ||
{ | ||
ASDN::MutexLocker l(__instanceLock__); | ||
ASTextContainer *containerCopy = [_textContainer copy]; | ||
containerCopy.size = self.calculatedSize; | ||
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:containerCopy text:_attributedText]; | ||
visibleRange = layout.visibleRange; | ||
} | ||
NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange]; | ||
[self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES]; | ||
} else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { | ||
[self _setHighlightRange:range forAttributeName:linkAttributeName value:linkAttributeValue animated:YES]; | ||
} | ||
|
||
return; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's required for us to copy the container here (or below at line 740). Except in extreme edge cases,
_textContainer
will be at the displayed size. Is this just extra safety, or was this necessary to make it work?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Adlai-Holler unfortunately, without these, link handling does not work. I could try removing just one or the other, but instead I think it is going to require a more detailed look at what is going on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK would you be willing to add a comment linking to this discussion?