Cursor tracking rects
Cursor tracking rects
- Subject: Cursor tracking rects
- From: Bill Bumgarner <email@hidden>
- Date: Fri, 28 Dec 2001 19:28:23 -0500
[Sorry this isn't in the appropriate thread... I can't remember what it
was called].
J o a r's code snippet will only work if the entire contents of the text
view are visible. If the text view is in a scroll view, it will set up
tracking rects for links that are not currently visible-- something that
is expressly forbidden according to the documentation.
So, if the text view is in a scroll view or is otherwise clipped, you need
to ensure that the tracking rects are only created for currently visible
links. Since the tracking rects are recalculated anytime the text object'
s layout changes-- including scroll events-- it is also helpful to only
scan the range of characters that are currently visible. For a
particularly long hunk of text, recalculating the cursor rects for the
entire body of text when only 2% is visible is both time consuming and
extremely ineffecient.
Figuring out the range of characters currently visible was slightly
non-trivial due to the complexity of the underlying Text subsystem (not a
complaint-- the fact that the Text subsystem can handle layout of text
across multiple containers and views is damned impressive).
The standard 'finger cursor' can be found in the HTMLDisplay framework's
resources in /System/Library/PrivateFrameworks.
Thanks to J o a r for posting the original hunk of code. Xmanview now
properly changes the cursor when it is over a link-- well, almost. I'm
having a hard time triggering the initial calculation of cursor rects
after the first display. Not sure why.
b.bum
Resulting code is enclosed -- most identical to J o a r's.
- (id)initWithFrame:(NSRect)frame; // DI
{
if (self = [super initWithFrame: frame]) {
NSImage *cursorImage;
cursorImage = [NSImage imageNamed:@"fingerCursor"];
linkCursor = [[NSCursor alloc] initWithImage:cursorImage
hotSpot:NSMakePoint(0.0,0.0)]; // linkCursor is an iVar
}
return self;
}
- (void) dealloc
{
[linkCursor release]; linkCursor = nil;
[super dealloc];
}
- (void)setCursorRectsForLinks
{
NSTextStorage *attrString = [self textStorage];
NSLayoutManager *layoutManager = [self layoutManager];
unsigned loc;
unsigned end;
NSRect visibleRect = [self visibleRect];
NSRange glyphRange;
NSRange characterRange;
glyphRange = [layoutManager glyphRangeForBoundingRect: visibleRect
inTextContainer: [self textContainer]];
characterRange = [layoutManager characterRangeForGlyphRange:
glyphRange actualGlyphRange: NULL];
loc = characterRange.location;
end = loc + characterRange.length;
while (loc < end) {
NSRange linkRange;
id tmp = [attrString attribute: NSLinkAttributeName
atIndex: loc
longestEffectiveRange: &linkRange
inRange: NSMakeRange(loc, end-loc)];
if (tmp != nil) {
NSRect linkRect = [[self layoutManager]
boundingRectForGlyphRange: linkRange
inTextContainer: [self textContainer]];
if (NSIntersectsRect(visibleRect, linkRect)) {
[self addCursorRect:linkRect cursor:linkCursor];
}
loc = NSMaxRange(linkRange);
} else {
loc++;
}
}
}
- (void)resetCursorRects
{
[super resetCursorRects];
[self setCursorRectsForLinks];
}