Re: How is 'truncate last visible line' accomplished?
Re: How is 'truncate last visible line' accomplished?
- Subject: Re: How is 'truncate last visible line' accomplished?
- From: Graham Cox <email@hidden>
- Date: Sat, 3 Jul 2010 23:15:23 +1000
On 03/07/2010, at 1:49 PM, Graham Cox wrote:
> How is 'truncate last visible line' accomplished for wrapped text?
>
> I have a custom cell that I would like to have this behaviour in. Most of the system controls/cells support this but it's not clear how it's done for custom cells. I thought it would be a paragraph style attribute but I don't see anything there.
The simple solution seems to be to use [NSAttributedString drawWithRect:options:]
Unfortunately this is too high-level.
I'm trying to emulate something like the way the Finder highlights icon text labels. To do this I need to get the used line fragments for all the lines drawn (so I can use these to come up with a suitable path to fill). I can use NSLayoutManager to do this, no real problem. Where I am running into trouble though is trying to implement the truncation of the last visible line. I can detect the last visible line easily enough when I lay out the text, but I can't get the layout manager to regenerate that line of text. I'm mutating the paragraph style for the remaining characters and applying it as an attribute to the remainder of the text. However I've tried everything I can think of to invalidate the layout of that line to no avail. (I've also verified that the paragraph style is really there and not nil).
Can anyone point me in the right direction for how to get the last line laid out again?
My code at the moment looks like:
- (void) drawTitle:(NSAttributedString*) title inRect:(NSRect) titleRect highlighted:(BOOL) highlighted
{
// draws the title, and optionally the background highlight region. This is performed by handling the layout of the text at
// a relatively low level so that the text layout can be optimised for the highlight as well.
NSLayoutManager* lm = [[self class] sharedLabelledCellLayoutManager];
NSTextStorage* ts = [lm textStorage];
NSTextContainer* tc = [[lm textContainers] lastObject];
[tc setContainerSize:titleRect.size];
[ts setAttributedString:title];
NSRange glyphRange = [lm glyphRangeForCharacterRange:NSMakeRange( 0, [title length]) actualCharacterRange:NULL];
NSUInteger glyphIndex, firstGlyph = glyphRange.location, maxGlyph = NSMaxRange( glyphRange ), rectCount = 0;
NSRect lineFragment;
NSRect rects[16];
if( highlighted )
{
// calculate the highlight region from the used line fragment rectangles
glyphIndex = firstGlyph;
while( glyphIndex < maxGlyph && rectCount < 16 )
{
lineFragment = [lm lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:&glyphRange];
lineFragment.origin.x += titleRect.origin.x;
lineFragment.origin.y += titleRect.origin.y;
rects[rectCount++] = lineFragment;
glyphIndex = NSMaxRange( glyphRange );
}
// TODO: generate a fancy path
NSBezierPath* hp = [[self class] bezierPathForHighlightingLineFragmentRects:rects count:rectCount];
[[NSColor blackColor] set];
[hp stroke];
}
// draw the text
BOOL isLastVisibleLine = NO;
NSUInteger lineCount = 0;
glyphIndex = firstGlyph;
while( glyphIndex < maxGlyph )
{
[lm lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:&glyphRange];
isLastVisibleLine = ++lineCount >= [self numberOfTextLines];
// for last visible line, apply a paragraph attribute to truncate the text at the end of the line if needed (i.e. remainder doesn't fit)
if( isLastVisibleLine && NSMaxRange( glyphRange ) < maxGlyph )
{
// need to truncate line
NSRange charRange = [lm characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];
NSMutableDictionary* attrs = [[ts attributesAtIndex:charRange.location effectiveRange:NULL] mutableCopy];
NSMutableParagraphStyle* ps = [[attrs objectForKey:NSParagraphStyleAttributeName] mutableCopy];
[ps setLineBreakMode:NSLineBreakByTruncatingTail];
[ps setAlignment:NSLeftTextAlignment];
[attrs setObject:ps forKey:NSParagraphStyleAttributeName];
[ps release];
// apply to remainder of text
charRange.length = [title length] - charRange.location;
[ts beginEditing];
[ts setAttributes:attrs range:charRange];
[ts invalidateAttributesInRange:charRange];
[ts endEditing];
[attrs release];
// try to force layout again for the modified line (doesn't work)
[lm invalidateLayoutForCharacterRange:charRange actualCharacterRange:NULL];
[lm lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:&glyphRange];
}
[lm drawGlyphsForGlyphRange:glyphRange atPoint:titleRect.origin];
if( isLastVisibleLine )
break; // lay out no more
glyphIndex = NSMaxRange( glyphRange );
}
}
_______________________________________________
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