PageRenderTime 60ms CodeModel.GetById 10ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 1ms

/core/externals/google-toolbox-for-mac/AppKit/GTMFadeTruncatingTextFieldCell.m

http://macfuse.googlecode.com/
Objective C | 237 lines | 163 code | 32 blank | 42 comment | 20 complexity | 77b63fd25b1c16df7cf8c28e0bb47e8e MD5 | raw file
  1//  GTMFadeTruncatingTextFieldCell.m
  2//
  3//  Copyright 2009 Google Inc.
  4//
  5//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  6//  use this file except in compliance with the License.  You may obtain a copy
  7//  of the License at
  8//
  9//  http://www.apache.org/licenses/LICENSE-2.0
 10//
 11//  Unless required by applicable law or agreed to in writing, software
 12//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 13//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 14//  License for the specific language governing permissions and limitations under
 15//  the License.
 16//
 17
 18#import "GTMFadeTruncatingTextFieldCell.h"
 19
 20#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
 21
 22@implementation GTMFadeTruncatingTextFieldCell
 23- (void)awakeFromNib {
 24  // Force to clipping
 25  [self setLineBreakMode:NSLineBreakByClipping];
 26}
 27
 28- (id)initTextCell:(NSString *)aString {
 29  self = [super initTextCell:aString];
 30  if (self) {
 31    // Force to clipping
 32    [self setLineBreakMode:NSLineBreakByClipping];
 33  }
 34  return self;
 35}
 36
 37- (void)drawTextGradientPart:(NSAttributedString *)attributedString
 38                   titleRect:(NSRect)titleRect
 39              backgroundRect:(NSRect)backgroundRect
 40                    clipRect:(NSRect)clipRect
 41                        mask:(NSGradient *)mask
 42                 fadeToRight:(BOOL)fadeToRight {
 43  // Draw the gradient part with a transparency layer. This makes the text look
 44  // suboptimal, but since it fades out, that's ok.
 45  [NSGraphicsContext saveGraphicsState];
 46  [NSBezierPath clipRect:backgroundRect];
 47  CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
 48  CGContextBeginTransparencyLayerWithRect(context,
 49                                          NSRectToCGRect(backgroundRect), 0);
 50
 51  if ([self drawsBackground]) {
 52    [[self backgroundColor] set];
 53    NSRectFillUsingOperation(backgroundRect,
 54                             NSCompositeSourceOver);
 55  }
 56
 57  [NSGraphicsContext saveGraphicsState];
 58  [NSBezierPath clipRect:clipRect];
 59  [attributedString drawInRect:titleRect];
 60  [NSGraphicsContext restoreGraphicsState];
 61
 62  NSPoint startPoint;
 63  NSPoint endPoint;
 64  if (fadeToRight) {
 65    startPoint = backgroundRect.origin;
 66    endPoint = NSMakePoint(NSMaxX(backgroundRect), NSMinY(backgroundRect));
 67  } else {
 68    startPoint = NSMakePoint(NSMaxX(backgroundRect), NSMinY(backgroundRect));
 69    endPoint = backgroundRect.origin;
 70  }
 71
 72  // Draw the gradient mask
 73  CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
 74  [mask drawFromPoint:startPoint
 75              toPoint:endPoint
 76              options:fadeToRight ? NSGradientDrawsBeforeStartingLocation :
 77                                    NSGradientDrawsAfterEndingLocation];
 78
 79  CGContextEndTransparencyLayer(context);
 80  [NSGraphicsContext restoreGraphicsState];
 81}
 82
 83- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
 84  NSRect titleRect = [self titleRectForBounds:cellFrame];
 85  // For some reason the title rect is too far to the left.
 86  titleRect.origin.x += 2;
 87  titleRect.size.width -= 2;
 88
 89  NSAttributedString *attributedString = [self attributedStringValue];
 90  NSSize stringSize = [attributedString size];
 91
 92  // Don't complicate drawing unless we need to clip
 93  if (stringSize.width <= NSWidth(titleRect)) {
 94    [super drawInteriorWithFrame:cellFrame inView:controlView];
 95    return;
 96  }
 97
 98  // Clip the string by drawing it to the left by |offsetX|.
 99  CGFloat offsetX = 0;
100  switch (truncateMode_) {
101    case GTMFadeTruncatingTail:
102      break;
103    case GTMFadeTruncatingHead:
104      offsetX = stringSize.width - titleRect.size.width;
105      break;
106    case GTMFadeTruncatingHeadAndTail: {
107      if (desiredCharactersToTruncateFromHead_ > 0) {
108        NSAttributedString *clippedHeadString =
109            [attributedString attributedSubstringFromRange:
110                NSMakeRange(0, desiredCharactersToTruncateFromHead_)];
111        NSSize clippedHeadSize = [clippedHeadString size];
112
113        // This is the offset at which we start drawing. This causes the
114        // beginning of the string to get clipped.
115        offsetX = clippedHeadSize.width;
116
117        // Due to the fade effect the first character is hard to see.
118        // We want to make sure the first character starting at
119        // |desiredCharactersToTruncateFromHead_| is readable so we reduce
120        // the offset by a little bit.
121        offsetX = MAX(0, offsetX - stringSize.height);
122
123        // If the offset is so large that there's empty space at the tail
124        // then reduce the offset so we can use up the empty space.
125        CGFloat delta = stringSize.width - titleRect.size.width;
126        if (offsetX > delta)
127          offsetX = delta;
128      } else {
129        // Center the string and clip equal portions of the head and tail.
130        offsetX = round((stringSize.width - titleRect.size.width) / 2.0);
131      }
132      break;
133    }
134  }
135
136  NSRect offsetTitleRect = titleRect;
137  offsetTitleRect.origin.x -= offsetX;
138  offsetTitleRect.size.width += offsetX;
139  BOOL isTruncatingHead = offsetX > 0;
140  BOOL isTruncatingTail = (stringSize.width - titleRect.size.width) > offsetX;
141
142  // Gradient is about twice our line height long
143  CGFloat gradientWidth = MIN(stringSize.height * 2, round(NSWidth(cellFrame) / 4));
144
145  // Head, solid, and tail rects for drawing the background.
146  NSRect solidBackgroundPart = [self drawingRectForBounds:cellFrame];
147  NSRect headBackgroundPart = NSZeroRect;
148  NSRect tailBackgroundPart = NSZeroRect;
149  if (isTruncatingHead)
150    NSDivideRect(solidBackgroundPart, &headBackgroundPart, &solidBackgroundPart,
151                 gradientWidth, NSMinXEdge);
152  if (isTruncatingTail)
153    NSDivideRect(solidBackgroundPart, &tailBackgroundPart, &solidBackgroundPart,
154                 gradientWidth, NSMaxXEdge);
155
156  // Head, solid and tail rects for clipping the title. This is slightly
157  // smaller than the background rects.
158  NSRect solidTitleClipPart = titleRect;
159  NSRect headTitleClipPart = NSZeroRect;
160  NSRect tailTitleClipPart = NSZeroRect;
161  if (isTruncatingHead) {
162    CGFloat width = NSMinX(solidBackgroundPart) - NSMinX(solidTitleClipPart);
163    NSDivideRect(solidTitleClipPart, &headTitleClipPart, &solidTitleClipPart,
164                 width, NSMinXEdge);
165  }
166  if (isTruncatingTail) {
167    CGFloat width = NSMaxX(solidTitleClipPart) - NSMaxX(solidBackgroundPart);
168    NSDivideRect(solidTitleClipPart, &tailTitleClipPart, &solidTitleClipPart,
169                 width, NSMaxXEdge);
170  }
171
172  // Draw non-gradient part without transparency layer, as light text on a dark 
173  // background looks bad with a gradient layer.
174  [NSGraphicsContext saveGraphicsState];
175  if ([self drawsBackground]) {
176    [[self backgroundColor] set];
177    NSRectFillUsingOperation(solidBackgroundPart, NSCompositeSourceOver);
178  }
179  // We draw the text ourselves because [super drawInteriorWithFrame:inView:]
180  // doesn't draw correctly if the cell draws its own background.
181  [NSBezierPath clipRect:solidTitleClipPart];
182  [attributedString drawInRect:offsetTitleRect];
183  [NSGraphicsContext restoreGraphicsState];
184
185  NSColor *startColor = [self textColor];;
186  NSColor *endColor = [startColor colorWithAlphaComponent:0.0];
187  NSGradient *mask = [[NSGradient alloc] initWithStartingColor:startColor
188                                                   endingColor:endColor];
189
190  if (isTruncatingHead)
191    [self drawTextGradientPart:attributedString
192                     titleRect:offsetTitleRect
193                backgroundRect:headBackgroundPart
194                      clipRect:headTitleClipPart
195                          mask:mask
196                   fadeToRight:NO];
197  if (isTruncatingTail)
198    [self drawTextGradientPart:attributedString
199                     titleRect:offsetTitleRect
200                backgroundRect:tailBackgroundPart
201                      clipRect:tailTitleClipPart
202                          mask:mask
203                   fadeToRight:YES];
204
205  [mask release];
206}
207
208- (void)setTruncateMode:(GTMFadeTruncateMode)mode {
209  if (truncateMode_ != mode) {
210    truncateMode_ = mode;
211    [[self controlView] setNeedsDisplay:YES];
212  }
213}
214
215- (GTMFadeTruncateMode)truncateMode {
216  return truncateMode_;
217}
218
219- (void)setDesiredCharactersToTruncateFromHead:(NSUInteger)length {
220  if (desiredCharactersToTruncateFromHead_ != length) {
221    desiredCharactersToTruncateFromHead_ = length;
222    [[self controlView] setNeedsDisplay:YES];
223  }
224}
225
226- (NSUInteger)desiredCharactersToTruncateFromHead {
227  return desiredCharactersToTruncateFromHead_;
228}
229
230// The faded ends of the cell are not opaque.
231- (BOOL)isOpaque {
232  return NO;
233}
234
235@end
236
237#endif