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 protected])
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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]