From fb696efd169db17e8169d0d0cfd1caf0728c0722 Mon Sep 17 00:00:00 2001 From: "gtm.daemon" Date: Fri, 18 Mar 2011 21:00:21 +0000 Subject: [Author: sail] Currently GTMFadeTruncatingTextFieldCell only supports truncating the tail of a string. It should also allow the head of a string to be truncated. I'd like to use this in Chromium. See http://crbug.com/69304 R=thomasvl APPROVED=thomasvl DELTA=238 (197 added, 17 deleted, 24 changed) --- AppKit/GTMFadeTruncatingTextFieldCell.m | 173 ++++++++++++++++++++++++++------ 1 file changed, 143 insertions(+), 30 deletions(-) (limited to 'AppKit/GTMFadeTruncatingTextFieldCell.m') diff --git a/AppKit/GTMFadeTruncatingTextFieldCell.m b/AppKit/GTMFadeTruncatingTextFieldCell.m index 0e7344c..ec266a2 100644 --- a/AppKit/GTMFadeTruncatingTextFieldCell.m +++ b/AppKit/GTMFadeTruncatingTextFieldCell.m @@ -34,54 +34,167 @@ return self; } +- (void)drawTextGradientPart:(NSAttributedString *)attributedString + titleRect:(NSRect)titleRect + backgroundRect:(NSRect)backgroundRect + clipRect:(NSRect)clipRect + mask:(NSGradient *)mask + fadeToRight:(BOOL)fadeToRight { + // Draw the gradient part with a transparency layer. This makes the text look + // suboptimal, but since it fades out, that's ok. + [[NSGraphicsContext currentContext] saveGraphicsState]; + [NSBezierPath clipRect:clipRect]; + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; + CGContextBeginTransparencyLayerWithRect(context, + NSRectToCGRect(clipRect), 0); + + if ([self drawsBackground]) { + [[self backgroundColor] set]; + NSRectFillUsingOperation([self titleRectForBounds:backgroundRect], + NSCompositeSourceOver); + } + [attributedString drawInRect:titleRect]; + + NSPoint startPoint; + NSPoint endPoint; + if (fadeToRight) { + startPoint = clipRect.origin; + endPoint = NSMakePoint(NSMaxX(clipRect), NSMinY(clipRect)); + } else { + startPoint = NSMakePoint(NSMaxX(clipRect), NSMinY(clipRect)); + endPoint = clipRect.origin; + } + + // Draw the gradient mask + CGContextSetBlendMode(context, kCGBlendModeDestinationIn); + [mask drawFromPoint:startPoint + toPoint:endPoint + options:fadeToRight ? NSGradientDrawsBeforeStartingLocation : + NSGradientDrawsAfterEndingLocation]; + + CGContextEndTransparencyLayer(context); + [[NSGraphicsContext currentContext] restoreGraphicsState]; +} + - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { - NSSize size = [[self attributedStringValue] size]; + NSRect titleRect = [self titleRectForBounds:cellFrame]; + // For some reason the title rect is too far to the left. + titleRect.origin.x += 2; + titleRect.size.width -= 2; + + NSAttributedString *attributedString = [self attributedStringValue]; + NSSize size = [attributedString size]; // Don't complicate drawing unless we need to clip - if (size.width <= NSWidth(cellFrame)) { + if (size.width <= NSWidth(titleRect)) { [super drawInteriorWithFrame:cellFrame inView:controlView]; return; } + // Clip the string by drawing it to the left by |offsetX|. + CGFloat offsetX = 0; + switch (truncateMode_) { + case GTMFadeTruncatingTail: + break; + case GTMFadeTruncatingHead: + offsetX = size.width - titleRect.size.width; + break; + case GTMFadeTruncatingHeadAndTail: { + if (desiredCharactersToTruncateFromHead_ > 0) { + NSAttributedString *clippedHeadString = + [attributedString attributedSubstringFromRange: + NSMakeRange(0, desiredCharactersToTruncateFromHead_)]; + NSSize clippedHeadSize = [clippedHeadString size]; + + // Clip the desired portion from the beginning of the string. + offsetX = clippedHeadSize.width; + + CGFloat delta = size.width - titleRect.size.width; + if (offsetX > delta) + offsetX = delta; + } else { + // Center the string and clip equal portions of the head and tail. + offsetX = round((size.width - titleRect.size.width) / 2.0); + } + break; + } + } + + NSRect offsetTitleRect = titleRect; + offsetTitleRect.origin.x -= offsetX; + offsetTitleRect.size.width += offsetX; + BOOL isTruncatingHead = offsetX > 0; + BOOL isTruncatingTail = (size.width - titleRect.size.width) > offsetX; + // Gradient is about twice our line height long - CGFloat gradientWidth = MIN(size.height * 2, NSWidth(cellFrame) / 4); + CGFloat gradientWidth = MIN(size.height * 2, round(NSWidth(cellFrame) / 4)); + NSRect solidPart = cellFrame; + NSRect headGradientPart = NSZeroRect; + NSRect tailGradientPart = NSZeroRect; + if (isTruncatingHead) + NSDivideRect(solidPart, &headGradientPart, &solidPart, + gradientWidth, NSMinXEdge); + if (isTruncatingTail) + NSDivideRect(solidPart, &tailGradientPart, &solidPart, + gradientWidth, NSMaxXEdge); - NSRect solidPart, gradientPart; - NSDivideRect(cellFrame, &gradientPart, &solidPart, gradientWidth, NSMaxXEdge); - // Draw non-gradient part without transparency layer, as light text on a dark // background looks bad with a gradient layer. + NSRect backgroundRect = [self drawingRectForBounds:cellFrame]; [[NSGraphicsContext currentContext] saveGraphicsState]; [NSBezierPath clipRect:solidPart]; - [super drawInteriorWithFrame:cellFrame inView:controlView]; + if ([self drawsBackground]) { + [[self backgroundColor] set]; + NSRectFillUsingOperation(backgroundRect, NSCompositeSourceOver); + } + // We draw the text ourselves because [super drawInteriorWithFrame:inView:] + // doesn't draw correctly if the cell draws its own background. + [attributedString drawInRect:offsetTitleRect]; [[NSGraphicsContext currentContext] restoreGraphicsState]; - // Draw the gradient part with a transparency layer. This makes the text look - // suboptimal, but since it fades out, that's ok. - [[NSGraphicsContext currentContext] saveGraphicsState]; - [NSBezierPath clipRect:gradientPart]; - CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; - CGContextBeginTransparencyLayerWithRect(context, - NSRectToCGRect(gradientPart), 0); - - [super drawInteriorWithFrame:cellFrame inView:controlView]; + NSColor *startColor = [self textColor];; + NSColor *endColor = [startColor colorWithAlphaComponent:0.0]; + NSGradient *mask = [[NSGradient alloc] initWithStartingColor:startColor + endingColor:endColor]; - // TODO(alcor): switch this to GTMLinearRGBShading if we ever need on 10.4 - NSColor *color = [self textColor]; - NSColor *alphaColor = [color colorWithAlphaComponent:0.0]; - NSGradient *mask = [[NSGradient alloc] initWithStartingColor:color - endingColor:alphaColor]; + if (isTruncatingHead) + [self drawTextGradientPart:attributedString + titleRect:offsetTitleRect + backgroundRect:backgroundRect + clipRect:headGradientPart + mask:mask + fadeToRight:NO]; + if (isTruncatingTail) + [self drawTextGradientPart:attributedString + titleRect:offsetTitleRect + backgroundRect:backgroundRect + clipRect:tailGradientPart + mask:mask + fadeToRight:YES]; - // Draw the gradient mask - CGContextSetBlendMode(context, kCGBlendModeDestinationIn); - [mask drawFromPoint:NSMakePoint(NSMaxX(cellFrame) - gradientWidth, - NSMinY(cellFrame)) - toPoint:NSMakePoint(NSMaxX(cellFrame), - NSMinY(cellFrame)) - options:NSGradientDrawsBeforeStartingLocation]; [mask release]; - CGContextEndTransparencyLayer(context); - [[NSGraphicsContext currentContext] restoreGraphicsState]; +} + +- (void)setTruncateMode:(GTMFadeTruncateMode)mode { + if (truncateMode_ != mode) { + truncateMode_ = mode; + [[self controlView] setNeedsDisplay:YES]; + } +} + +- (GTMFadeTruncateMode)truncateMode { + return truncateMode_; +} + +- (void)setDesiredCharactersToTruncateFromHead:(NSUInteger)length { + if (desiredCharactersToTruncateFromHead_ != length) { + desiredCharactersToTruncateFromHead_ = length; + [[self controlView] setNeedsDisplay:YES]; + } +} + +- (NSUInteger)desiredCharactersToTruncateFromHead { + return desiredCharactersToTruncateFromHead_; } @end -- cgit v1.2.3