• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: How is 'truncate last visible line' accomplished?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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

References: 
 >How is 'truncate last visible line' accomplished? (From: Graham Cox <email@hidden>)

  • Prev by Date: How is 'truncate last visible line' accomplished?
  • Next by Date: Re: Calling javascript from Cocoa 'facelessly'
  • Previous by thread: How is 'truncate last visible line' accomplished?
  • Next by thread: Re: How is 'truncate last visible line' accomplished?
  • Index(es):
    • Date
    • Thread