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 (Cocoa-dev@lists.apple.com)

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 arch...@mail-archive.com

Reply via email to