Typesetter - Fragment Generation
Typesetter - Fragment Generation
- Subject: Typesetter - Fragment Generation
- From: Seth Willits <email@hidden>
- Date: Mon, 11 Mar 2013 12:14:58 -0700
I'm subclassing NSATSTypesetter to add custom line wrapping behavior which indents wrapped lines relative to the starting line's indentation (like Xcode).
To do this, I'm overriding - getLineFragmentRect:usedRect:remainingRect:forStartingGlyphAtIndex:... and for wrapped lines am modifying the proposed rect's x origin to add indentation before passing the parameters up to super to let it handle the rest of the fragment generation.
Questions:
1) When I dump rich text into the text view, this method is called *twice* for each starting glyph index, and the proposed rect for the second call is the proposed rect I modify during the first call. For plain text it's only called once for each line. Any explanations for what's going on?
2) Is simply modifying the proposed rect's origin the right approach?
a) Does the proposed rect ever include lineFragmentPadding
b) Is the proposed rect always a bazillion points wide?
Basically, I'm wondering if I need to add to the x value, or if I should simply set it to the indentation I want.
proposedRect.origin.x += indent; Versus proposedRect.origin.x = indent;
I've already noticed a problem with the former when pasting in rich text since for a single line, getLineFragmentRect: is called with the normal proposedRect, I add to it, then it gets calls again with the proposed I passed to super, and I add to it again which of course is wrong (and also leads to some very strange and broken layout). So it appears I must simply *set* the x value, but I don't know if I need to take lineFragment padding and the width into account.
3) Is it always going to be safe to ask the layout manage locationForGlyphAtIndex: for a glyph index < startingGlyphIndex or is there any possibility of an infinite loop?
Here's a simple implementation I'm starting with:
- (void)getLineFragmentRect:(NSRectPointer)lineFragmentRect usedRect:(NSRectPointer)lineFragmentUsedRect remainingRect:(NSRectPointer)remainingRect forStartingGlyphAtIndex:(NSUInteger)startingGlyphIndex proposedRect:(NSRect)proposedRect lineSpacing:(CGFloat)lineSpacing paragraphSpacingBefore:(CGFloat)paragraphSpacingBefore paragraphSpacingAfter:(CGFloat)paragraphSpacingAfter
{
NSString * string = self.layoutManager.textStorage.string;
NSUInteger stringLength = string.length;
NSUInteger characterIndex = [self.layoutManager characterIndexForGlyphAtIndex:startingGlyphIndex];
NSRange lineRange = [string lineRangeForRange:NSMakeRange(characterIndex, 0)];
// Find the line range for the line of text this glyph is a part of
characterIndex = [self.layoutManager characterIndexForGlyphAtIndex:startingGlyphIndex];
lineRange = [string lineRangeForRange:NSMakeRange(characterIndex, 0)];
// If the startingGlyphIndex's character is *after* the beginning of the line
// of text it's on, then the lineFragmentRect being asked for must be part
// of a wrapped line - which we want to indent.
if (characterIndex > lineRange.location) {
NSCharacterSet * cs = [NSCharacterSet whitespaceCharacterSet];
CGFloat indentation = 0.0;
// Find the indentation of the first non-whitespace
// character in the line.
for (NSUInteger i = 0; lineRange.location + i < stringLength; i++) {
unichar c = [string characterAtIndex:lineRange.location + i];
if (![cs characterIsMember:c]) {
NSUInteger glyphIndex = [self.layoutManager glyphIndexForCharacterAtIndex:lineRange.location + i];
indentation = [self.layoutManager locationForGlyphAtIndex:glyphIndex].x;
break;
}
}
// NOTE: Do we need to do anything special for lineFragmentPadding *here*?
// ...
// Increase the indentation from the first line
indentation += 28; // 4 spaces width (to be calculated for real later)
// Modify the proposed rect to create that indentation
CGFloat maxX = NSMaxX(proposedRect);
proposedRect.origin.x = indentation;
proposedRect.size.width = maxX - indentation;
}
[super getLineFragmentRect:lineFragmentRect usedRect:lineFragmentUsedRect remainingRect:remainingRect forStartingGlyphAtIndex:startingGlyphIndex proposedRect:proposedRect lineSpacing:lineSpacing paragraphSpacingBefore:paragraphSpacingBefore paragraphSpacingAfter:paragraphSpacingAfter];
}
--
Seth Willits
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden