/core/externals/update-engine/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. #import "GTMFadeTruncatingTextFieldCell.h"
  18. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  19. @implementation GTMFadeTruncatingTextFieldCell
  20. - (void)awakeFromNib {
  21. // Force to clipping
  22. [self setLineBreakMode:NSLineBreakByClipping];
  23. }
  24. - (id)initTextCell:(NSString *)aString {
  25. self = [super initTextCell:aString];
  26. if (self) {
  27. // Force to clipping
  28. [self setLineBreakMode:NSLineBreakByClipping];
  29. }
  30. return self;
  31. }
  32. - (void)drawTextGradientPart:(NSAttributedString *)attributedString
  33. titleRect:(NSRect)titleRect
  34. backgroundRect:(NSRect)backgroundRect
  35. clipRect:(NSRect)clipRect
  36. mask:(NSGradient *)mask
  37. fadeToRight:(BOOL)fadeToRight {
  38. // Draw the gradient part with a transparency layer. This makes the text look
  39. // suboptimal, but since it fades out, that's ok.
  40. [NSGraphicsContext saveGraphicsState];
  41. [NSBezierPath clipRect:backgroundRect];
  42. CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
  43. CGContextBeginTransparencyLayerWithRect(context,
  44. NSRectToCGRect(backgroundRect), 0);
  45. if ([self drawsBackground]) {
  46. [[self backgroundColor] set];
  47. NSRectFillUsingOperation(backgroundRect,
  48. NSCompositeSourceOver);
  49. }
  50. [NSGraphicsContext saveGraphicsState];
  51. [NSBezierPath clipRect:clipRect];
  52. [attributedString drawInRect:titleRect];
  53. [NSGraphicsContext restoreGraphicsState];
  54. NSPoint startPoint;
  55. NSPoint endPoint;
  56. if (fadeToRight) {
  57. startPoint = backgroundRect.origin;
  58. endPoint = NSMakePoint(NSMaxX(backgroundRect), NSMinY(backgroundRect));
  59. } else {
  60. startPoint = NSMakePoint(NSMaxX(backgroundRect), NSMinY(backgroundRect));
  61. endPoint = backgroundRect.origin;
  62. }
  63. // Draw the gradient mask
  64. CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
  65. [mask drawFromPoint:startPoint
  66. toPoint:endPoint
  67. options:fadeToRight ? NSGradientDrawsBeforeStartingLocation :
  68. NSGradientDrawsAfterEndingLocation];
  69. CGContextEndTransparencyLayer(context);
  70. [NSGraphicsContext restoreGraphicsState];
  71. }
  72. - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
  73. NSRect titleRect = [self titleRectForBounds:cellFrame];
  74. // For some reason the title rect is too far to the left.
  75. titleRect.origin.x += 2;
  76. titleRect.size.width -= 2;
  77. NSAttributedString *attributedString = [self attributedStringValue];
  78. NSSize stringSize = [attributedString size];
  79. // Don't complicate drawing unless we need to clip
  80. if (stringSize.width <= NSWidth(titleRect)) {
  81. [super drawInteriorWithFrame:cellFrame inView:controlView];
  82. return;
  83. }
  84. // Clip the string by drawing it to the left by |offsetX|.
  85. CGFloat offsetX = 0;
  86. switch (truncateMode_) {
  87. case GTMFadeTruncatingTail:
  88. break;
  89. case GTMFadeTruncatingHead:
  90. offsetX = stringSize.width - titleRect.size.width;
  91. break;
  92. case GTMFadeTruncatingHeadAndTail: {
  93. if (desiredCharactersToTruncateFromHead_ > 0) {
  94. NSAttributedString *clippedHeadString =
  95. [attributedString attributedSubstringFromRange:
  96. NSMakeRange(0, desiredCharactersToTruncateFromHead_)];
  97. NSSize clippedHeadSize = [clippedHeadString size];
  98. // This is the offset at which we start drawing. This causes the
  99. // beginning of the string to get clipped.
  100. offsetX = clippedHeadSize.width;
  101. // Due to the fade effect the first character is hard to see.
  102. // We want to make sure the first character starting at
  103. // |desiredCharactersToTruncateFromHead_| is readable so we reduce
  104. // the offset by a little bit.
  105. offsetX = MAX(0, offsetX - stringSize.height);
  106. // If the offset is so large that there's empty space at the tail
  107. // then reduce the offset so we can use up the empty space.
  108. CGFloat delta = stringSize.width - titleRect.size.width;
  109. if (offsetX > delta)
  110. offsetX = delta;
  111. } else {
  112. // Center the string and clip equal portions of the head and tail.
  113. offsetX = round((stringSize.width - titleRect.size.width) / 2.0);
  114. }
  115. break;
  116. }
  117. }
  118. NSRect offsetTitleRect = titleRect;
  119. offsetTitleRect.origin.x -= offsetX;
  120. offsetTitleRect.size.width += offsetX;
  121. BOOL isTruncatingHead = offsetX > 0;
  122. BOOL isTruncatingTail = (stringSize.width - titleRect.size.width) > offsetX;
  123. // Gradient is about twice our line height long
  124. CGFloat gradientWidth = MIN(stringSize.height * 2, round(NSWidth(cellFrame) / 4));
  125. // Head, solid, and tail rects for drawing the background.
  126. NSRect solidBackgroundPart = [self drawingRectForBounds:cellFrame];
  127. NSRect headBackgroundPart = NSZeroRect;
  128. NSRect tailBackgroundPart = NSZeroRect;
  129. if (isTruncatingHead)
  130. NSDivideRect(solidBackgroundPart, &headBackgroundPart, &solidBackgroundPart,
  131. gradientWidth, NSMinXEdge);
  132. if (isTruncatingTail)
  133. NSDivideRect(solidBackgroundPart, &tailBackgroundPart, &solidBackgroundPart,
  134. gradientWidth, NSMaxXEdge);
  135. // Head, solid and tail rects for clipping the title. This is slightly
  136. // smaller than the background rects.
  137. NSRect solidTitleClipPart = titleRect;
  138. NSRect headTitleClipPart = NSZeroRect;
  139. NSRect tailTitleClipPart = NSZeroRect;
  140. if (isTruncatingHead) {
  141. CGFloat width = NSMinX(solidBackgroundPart) - NSMinX(solidTitleClipPart);
  142. NSDivideRect(solidTitleClipPart, &headTitleClipPart, &solidTitleClipPart,
  143. width, NSMinXEdge);
  144. }
  145. if (isTruncatingTail) {
  146. CGFloat width = NSMaxX(solidTitleClipPart) - NSMaxX(solidBackgroundPart);
  147. NSDivideRect(solidTitleClipPart, &tailTitleClipPart, &solidTitleClipPart,
  148. width, NSMaxXEdge);
  149. }
  150. // Draw non-gradient part without transparency layer, as light text on a dark
  151. // background looks bad with a gradient layer.
  152. [NSGraphicsContext saveGraphicsState];
  153. if ([self drawsBackground]) {
  154. [[self backgroundColor] set];
  155. NSRectFillUsingOperation(solidBackgroundPart, NSCompositeSourceOver);
  156. }
  157. // We draw the text ourselves because [super drawInteriorWithFrame:inView:]
  158. // doesn't draw correctly if the cell draws its own background.
  159. [NSBezierPath clipRect:solidTitleClipPart];
  160. [attributedString drawInRect:offsetTitleRect];
  161. [NSGraphicsContext restoreGraphicsState];
  162. NSColor *startColor = [self textColor];;
  163. NSColor *endColor = [startColor colorWithAlphaComponent:0.0];
  164. NSGradient *mask = [[NSGradient alloc] initWithStartingColor:startColor
  165. endingColor:endColor];
  166. if (isTruncatingHead)
  167. [self drawTextGradientPart:attributedString
  168. titleRect:offsetTitleRect
  169. backgroundRect:headBackgroundPart
  170. clipRect:headTitleClipPart
  171. mask:mask
  172. fadeToRight:NO];
  173. if (isTruncatingTail)
  174. [self drawTextGradientPart:attributedString
  175. titleRect:offsetTitleRect
  176. backgroundRect:tailBackgroundPart
  177. clipRect:tailTitleClipPart
  178. mask:mask
  179. fadeToRight:YES];
  180. [mask release];
  181. }
  182. - (void)setTruncateMode:(GTMFadeTruncateMode)mode {
  183. if (truncateMode_ != mode) {
  184. truncateMode_ = mode;
  185. [[self controlView] setNeedsDisplay:YES];
  186. }
  187. }
  188. - (GTMFadeTruncateMode)truncateMode {
  189. return truncateMode_;
  190. }
  191. - (void)setDesiredCharactersToTruncateFromHead:(NSUInteger)length {
  192. if (desiredCharactersToTruncateFromHead_ != length) {
  193. desiredCharactersToTruncateFromHead_ = length;
  194. [[self controlView] setNeedsDisplay:YES];
  195. }
  196. }
  197. - (NSUInteger)desiredCharactersToTruncateFromHead {
  198. return desiredCharactersToTruncateFromHead_;
  199. }
  200. // The faded ends of the cell are not opaque.
  201. - (BOOL)isOpaque {
  202. return NO;
  203. }
  204. @end
  205. #endif