I'm trying to show some extra symbols next to lines in NSTextView
, based on text attributes.
I have successfully subclassed NSLayoutManager
, but it seems that layout manager can't draw outside the area set by textContainerInset
.
Because my text view can potentially have a very long strings, I'm hoping to keep the drawing connected to displaying glyphs. Is there a way to trick the layout manager to be able to draw inside the content insets — or is there another method I use instead of drawGlyphsForGlyphRange
?
I have tried calling super
before and after drawing, as well as storing and not storing graphics state. I also attempted setDrawsOutsideLineFragment:YES
for the glyphs, but with no luck.
Things like Xcode editor itself uses change markers, so I know that this is somehow doable, but it's very possible I'm looking from the wrong place.
My drawing method, simplified:
- (void)drawGlyphsForGlyphRange:(NSRange)glyphsToShow atPoint:(NSPoint)origin {
[super drawGlyphsForGlyphRange:glyphsToShow atPoint:origin];
NSTextStorage *textStorage = self.textStorage;
NSTextContainer *textContainer = self.textContainers[0];
NSRange glyphRange = glyphsToShow;
NSSize offset = self.textContainers.firstObject.textView.textContainerInset;
while (glyphRange.length > 0) {
NSRange charRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL], attributeCharRange, attributeGlyphRange;
id attribute = [textStorage attribute:@"Revision" atIndex:charRange.location longestEffectiveRange:&attributeCharRange inRange:charRange];
attributeGlyphRange = [self glyphRangeForCharacterRange:attributeCharRange actualCharacterRange:NULL];
attributeGlyphRange = NSIntersectionRange(attributeGlyphRange, glyphRange);
if (attribute != nil) {
[NSGraphicsContext saveGraphicsState];
NSRect boundingRect = [self boundingRectForGlyphRange:attributeGlyphRange
inTextContainer:textContainer];
// Find the top of the revision
NSPoint point = NSMakePoint(offset.width - 20, offset.height + boundingRect.origin.y + 1.0);
NSString *marker = @"*";
[marker drawAtPoint:point withAttributes:@{
NSForegroundColorAttributeName: NSColor.blackColor;
}];
[NSGraphicsContext restoreGraphicsState];
}
glyphRange.length = NSMaxRange(glyphRange) - NSMaxRange(attributeGlyphRange);
glyphRange.location = NSMaxRange(attributeGlyphRange);
}
}