OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "base/scoped_nsobject.h" | |
6 #include "chrome/browser/cocoa/gradient_button_cell.h" | 5 #include "chrome/browser/cocoa/gradient_button_cell.h" |
7 #import "third_party/GTM/AppKit/GTMTheme.h" | 6 #import "third_party/GTM/AppKit/GTMTheme.h" |
8 #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" | 7 #import "base/scoped_nsobject.h" |
9 | 8 |
10 @interface GradientButtonCell (Private) | 9 @interface GradientButtonCell (Private) |
11 - (void)sharedInit; | 10 - (void)sharedInit; |
12 - (void)drawUnderlayImageWithFrame:(NSRect)cellFrame | 11 - (void)drawUnderlayImageWithFrame:(NSRect)cellFrame |
13 inView:(NSView*)controlView; | 12 inView:(NSView*)controlView; |
14 @end | 13 @end |
15 | 14 |
16 static const NSTimeInterval kAnimationShowDuration = 0.2; | |
17 static const NSTimeInterval kAnimationHideDuration = 0.4; | |
18 | 15 |
19 @implementation GradientButtonCell | 16 @implementation GradientButtonCell |
20 @synthesize hoverAlpha = hoverAlpha_; | |
21 | |
22 - (void)adjustHoverValue { | |
23 NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; | |
24 | |
25 NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; | |
26 | |
27 CGFloat opacity = [self hoverAlpha]; | |
28 if (isMouseInside_) { | |
29 opacity += elapsed / kAnimationShowDuration; | |
30 } else { | |
31 opacity -= elapsed / kAnimationHideDuration; | |
32 } | |
33 | |
34 if (!isMouseInside_ && opacity < 0) { | |
35 opacity = 0; | |
36 } else if (isMouseInside_ && opacity > 1) { | |
37 opacity = 1; | |
38 } else { | |
39 [self performSelector:_cmd withObject:nil afterDelay:0.02]; | |
40 } | |
41 lastHoverUpdate_ = thisUpdate; | |
42 [self setHoverAlpha:opacity]; | |
43 | |
44 [[self controlView] setNeedsDisplay:YES]; | |
45 } | |
46 | |
47 - (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { | |
48 isMouseInside_ = flag; | |
49 if (animated) { | |
50 lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; | |
51 [self adjustHoverValue]; | |
52 } else { | |
53 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | |
54 [self setHoverAlpha:flag ? 1.0 : 0.0]; | |
55 } | |
56 [[self controlView] setNeedsDisplay:YES]; | |
57 } | |
58 | 17 |
59 // For nib instantiations | 18 // For nib instantiations |
60 - (id)initWithCoder:(NSCoder*)decoder { | 19 - (id)initWithCoder:(NSCoder*)decoder { |
61 if ((self = [super initWithCoder:decoder])) { | 20 if ((self = [super initWithCoder:decoder])) { |
62 [self sharedInit]; | 21 [self sharedInit]; |
63 } | 22 } |
64 return self; | 23 return self; |
65 } | 24 } |
66 | 25 |
67 // For programmatic instantiations | 26 // For programmatic instantiations |
68 - (id)initTextCell:(NSString*)string { | 27 - (id)initTextCell:(NSString*)string { |
69 if ((self = [super initTextCell:string])) { | 28 if ((self = [super initTextCell:string])) { |
70 [self sharedInit]; | 29 [self sharedInit]; |
71 } | 30 } |
72 return self; | 31 return self; |
73 } | 32 } |
74 | 33 |
75 - (NSGradient *)gradientForHoverAlpha:(CGFloat)hoverAlpha | |
76 isThemed:(BOOL)themed { | |
77 CGFloat startAlpha = 0.6 + 0.3 * hoverAlpha; | |
78 CGFloat endAlpha = 0.333 * hoverAlpha; | |
79 | |
80 if (themed) { | |
81 startAlpha = 0.2 + 0.35 * hoverAlpha; | |
82 endAlpha = 0.333 * hoverAlpha; | |
83 } | |
84 | |
85 NSColor* startColor = | |
86 [NSColor colorWithCalibratedWhite:1.0 | |
87 alpha:startAlpha]; | |
88 NSColor* endColor = | |
89 [NSColor colorWithCalibratedWhite:1.0 - 0.15 * hoverAlpha | |
90 alpha:endAlpha]; | |
91 NSGradient *gradient = [[NSGradient alloc] initWithColorsAndLocations: | |
92 startColor, hoverAlpha * 0.33, | |
93 endColor, 1.0, nil]; | |
94 | |
95 return [gradient autorelease]; | |
96 } | |
97 | |
98 | |
99 - (void)sharedInit { | 34 - (void)sharedInit { |
100 shouldTheme_ = YES; | 35 shouldTheme_ = YES; |
101 gradient_.reset([[self gradientForHoverAlpha:0.0 isThemed:NO] retain]); | 36 NSColor* startColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.666]; |
| 37 NSColor* endColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.333]; |
| 38 gradient_.reset([[NSGradient alloc] |
| 39 initWithColorsAndLocations:startColor, 0.33, endColor, 1.0, nil]); |
102 } | 40 } |
103 | 41 |
104 - (void)setShouldTheme:(BOOL)shouldTheme { | 42 - (void)setShouldTheme:(BOOL)shouldTheme { |
105 shouldTheme_ = shouldTheme; | 43 shouldTheme_ = shouldTheme; |
106 } | 44 } |
107 | 45 |
108 - (NSImage*)underlayImage { | 46 - (NSImage*)underlayImage { |
109 return underlayImage_; | 47 return underlayImage_; |
110 } | 48 } |
111 | 49 |
112 - (void)setUnderlayImage:(NSImage*)image { | 50 - (void)setUnderlayImage:(NSImage*)image { |
113 underlayImage_.reset([image retain]); | 51 underlayImage_.reset([image retain]); |
114 | 52 |
115 [[self controlView] setNeedsDisplay:YES]; | 53 [[self controlView] setNeedsDisplay:YES]; |
116 } | 54 } |
117 | 55 |
118 - (NSBackgroundStyle)interiorBackgroundStyle { | 56 - (NSBackgroundStyle)interiorBackgroundStyle { |
119 return [self isHighlighted] ? | 57 return [self isHighlighted] ? |
120 NSBackgroundStyleLowered : NSBackgroundStyleRaised; | 58 NSBackgroundStyleLowered : NSBackgroundStyleRaised; |
121 } | 59 } |
122 | 60 |
123 - (void)mouseEntered:(NSEvent *)theEvent { | 61 - (void)mouseEntered:(NSEvent *)theEvent { |
124 [self setMouseInside:YES animate:YES]; | 62 isMouseInside_ = YES; |
| 63 [[self controlView] setNeedsDisplay:YES]; |
125 } | 64 } |
126 | 65 |
127 - (void)mouseExited:(NSEvent *)theEvent { | 66 - (void)mouseExited:(NSEvent *)theEvent { |
128 [self setMouseInside:NO animate:YES]; | 67 isMouseInside_ = NO; |
| 68 [[self controlView] setNeedsDisplay:YES]; |
129 } | 69 } |
130 | 70 |
131 - (BOOL)isMouseInside { | 71 - (BOOL)isMouseInside { |
132 return trackingArea_ && isMouseInside_; | 72 return trackingArea_ && isMouseInside_; |
133 } | 73 } |
134 | 74 |
135 // Since we have our own drawWithFrame:, we need to also have our own | 75 // Since we have our own drawWithFrame:, we need to also have our own |
136 // logic for determining when the mouse is inside for honoring this | 76 // logic for determining when the mouse is inside for honoring this |
137 // request. | 77 // request. |
138 - (void)setShowsBorderOnlyWhileMouseInside:(BOOL)showOnly { | 78 - (void)setShowsBorderOnlyWhileMouseInside:(BOOL)showOnly { |
(...skipping 16 matching lines...) Expand all Loading... |
155 trackingArea_.reset(nil); | 95 trackingArea_.reset(nil); |
156 isMouseInside_ = NO; | 96 isMouseInside_ = NO; |
157 } | 97 } |
158 } | 98 } |
159 } | 99 } |
160 | 100 |
161 - (void)drawBorderAndFillForTheme:(GTMTheme*)theme | 101 - (void)drawBorderAndFillForTheme:(GTMTheme*)theme |
162 controlView:(NSView*)controlView | 102 controlView:(NSView*)controlView |
163 outerPath:(NSBezierPath*)outerPath | 103 outerPath:(NSBezierPath*)outerPath |
164 innerPath:(NSBezierPath*)innerPath | 104 innerPath:(NSBezierPath*)innerPath |
| 105 showHighlightGradient:(BOOL)showHighlightGradient |
165 showClickedGradient:(BOOL)showClickedGradient | 106 showClickedGradient:(BOOL)showClickedGradient |
166 showHighlightGradient:(BOOL)showHighlightGradient | |
167 hoverAlpha:(CGFloat)hoverAlpha | |
168 active:(BOOL)active | 107 active:(BOOL)active |
169 cellFrame:(NSRect)cellFrame { | 108 cellFrame:(NSRect)cellFrame { |
| 109 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.25] set]; |
| 110 [outerPath stroke]; |
| 111 |
170 NSImage* backgroundImage = | 112 NSImage* backgroundImage = |
171 [theme backgroundImageForStyle:GTMThemeStyleToolBarButton state:YES]; | 113 [theme backgroundImageForStyle:GTMThemeStyleToolBarButton state:YES]; |
172 | 114 |
173 if (backgroundImage) { | 115 if (backgroundImage) { |
174 NSColor* patternColor = [NSColor colorWithPatternImage:backgroundImage]; | 116 NSColor* patternColor = [NSColor colorWithPatternImage:backgroundImage]; |
175 [patternColor set]; | 117 [patternColor set]; |
176 // Set the phase to match window. | 118 // Set the phase to match window. |
177 NSRect trueRect = [controlView convertRectToBase:cellFrame]; | 119 NSRect trueRect = [controlView convertRectToBase:cellFrame]; |
178 [[NSGraphicsContext currentContext] | 120 [[NSGraphicsContext currentContext] |
179 setPatternPhase:NSMakePoint(NSMinX(trueRect), NSMaxY(trueRect))]; | 121 setPatternPhase:NSMakePoint(NSMinX(trueRect), NSMaxY(trueRect))]; |
180 [innerPath fill]; | 122 [innerPath fill]; |
181 } else { | 123 } else { |
182 if (showClickedGradient) { | 124 if (showClickedGradient) { |
183 NSGradient* gradient = | 125 NSGradient* gradient = |
184 [theme gradientForStyle:GTMThemeStyleToolBarButtonPressed | 126 [theme gradientForStyle:GTMThemeStyleToolBarButtonPressed |
185 state:active]; | 127 state:active]; |
186 [gradient drawInBezierPath:innerPath angle:90.0]; | 128 [gradient drawInBezierPath:innerPath angle:90.0]; |
187 } | 129 } |
188 } | 130 } |
189 | 131 |
190 BOOL isCustomTheme = backgroundImage != nil; | 132 if (!showClickedGradient && showHighlightGradient) { |
191 | |
192 if (!showClickedGradient && [self isEnabled]) { | |
193 [NSGraphicsContext saveGraphicsState]; | 133 [NSGraphicsContext saveGraphicsState]; |
194 [innerPath addClip]; | 134 [innerPath addClip]; |
195 | 135 |
196 // Draw the inner glow. | 136 // Draw the inner glow. |
197 if (hoverAlpha > 0) { | 137 [innerPath setLineWidth:2]; |
198 [innerPath setLineWidth:2]; | 138 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.9] setStroke]; |
199 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2 * hoverAlpha] setStroke]; | 139 [innerPath stroke]; |
200 [innerPath stroke]; | 140 |
201 } | 141 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.9] setStroke]; |
| 142 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2] setFill]; |
202 | 143 |
203 // Draw the top inner highlight. | 144 // Draw the top inner highlight. |
204 NSAffineTransform* highlightTransform = [NSAffineTransform transform]; | 145 NSAffineTransform* highlightTransform = [NSAffineTransform transform]; |
205 [highlightTransform translateXBy:1 yBy:1]; | 146 [highlightTransform translateXBy:1 yBy:1]; |
206 scoped_nsobject<NSBezierPath> highlightPath([innerPath copy]); | 147 scoped_nsobject<NSBezierPath> highlightPath([innerPath copy]); |
207 [highlightPath transformUsingAffineTransform:highlightTransform]; | 148 [highlightPath transformUsingAffineTransform:highlightTransform]; |
208 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2] setStroke]; | 149 |
209 [highlightPath stroke]; | 150 [highlightPath stroke]; |
210 | 151 |
211 NSGradient *gradient = nil; | 152 [gradient_ drawInBezierPath:innerPath angle:90.0]; |
212 if (hoverAlpha == 0 && !isCustomTheme) { | |
213 gradient = gradient_; | |
214 } else { | |
215 gradient = [self gradientForHoverAlpha:hoverAlpha isThemed:isCustomTheme]; | |
216 } | |
217 [gradient drawInBezierPath:innerPath angle:90.0]; | |
218 | 153 |
219 [NSGraphicsContext restoreGraphicsState]; | 154 [NSGraphicsContext restoreGraphicsState]; |
220 } | 155 } |
221 | 156 |
222 // Draw the outer stroke | |
223 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton | 157 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton |
224 state:active]; | 158 state:active]; |
225 | |
226 if (showClickedGradient) { | |
227 stroke = [NSColor colorWithCalibratedWhite:0.0 alpha:0.3]; | |
228 } | |
229 [stroke setStroke]; | 159 [stroke setStroke]; |
230 | 160 |
231 [innerPath setLineWidth:1]; | 161 [innerPath setLineWidth:1]; |
232 [innerPath stroke]; | 162 [innerPath stroke]; |
233 } | 163 } |
234 | 164 |
| 165 |
235 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 166 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
236 // Constants from Cole. Will kConstant them once the feedback loop | 167 // Constants from Cole. Will kConstant them once the feedback loop |
237 // is complete. | 168 // is complete. |
238 NSRect drawFrame = NSInsetRect(cellFrame, 1.5, 1.5); | 169 NSRect drawFrame = NSInsetRect(cellFrame, 1.5, 1.5); |
239 NSRect innerFrame = NSInsetRect(cellFrame, 2, 1); | 170 NSRect innerFrame = NSInsetRect(cellFrame, 2, 2); |
240 ButtonType type = [[(NSControl*)controlView cell] tag]; | 171 ButtonType type = [[(NSControl*)controlView cell] tag]; |
241 switch (type) { | 172 switch (type) { |
242 case kMiddleButtonType: | 173 case kMiddleButtonType: |
243 drawFrame.size.width += 20; | 174 drawFrame.size.width += 20; |
244 innerFrame.size.width += 2; | 175 innerFrame.size.width += 2; |
245 // Fallthrough | 176 // Fallthrough |
246 case kRightButtonType: | 177 case kRightButtonType: |
247 drawFrame.origin.x -= 20; | 178 drawFrame.origin.x -= 20; |
248 innerFrame.origin.x -= 2; | 179 innerFrame.origin.x -= 2; |
249 // Fallthrough | 180 // Fallthrough |
(...skipping 24 matching lines...) Expand all Loading... |
274 // Stroke the borders and appropriate fill gradient. If we're borderless, | 205 // Stroke the borders and appropriate fill gradient. If we're borderless, |
275 // the only time we want to draw the inner gradient is if we're highlighted. | 206 // the only time we want to draw the inner gradient is if we're highlighted. |
276 if (([self isBordered] && ![self showsBorderOnlyWhileMouseInside]) || | 207 if (([self isBordered] && ![self showsBorderOnlyWhileMouseInside]) || |
277 pressed || | 208 pressed || |
278 [self isMouseInside]) { | 209 [self isMouseInside]) { |
279 | 210 |
280 [self drawBorderAndFillForTheme:theme | 211 [self drawBorderAndFillForTheme:theme |
281 controlView:controlView | 212 controlView:controlView |
282 outerPath:outerPath | 213 outerPath:outerPath |
283 innerPath:innerPath | 214 innerPath:innerPath |
| 215 showHighlightGradient:YES |
284 showClickedGradient:pressed | 216 showClickedGradient:pressed |
285 showHighlightGradient:[self isHighlighted] | |
286 hoverAlpha:[self hoverAlpha] | |
287 active:active | 217 active:active |
288 cellFrame:cellFrame]; | 218 cellFrame:cellFrame]; |
289 } | 219 } |
290 | 220 |
291 // If this is the left side of a segmented button, draw a slight shadow. | 221 // If this is the left side of a segmented button, draw a slight shadow. |
292 if (type == kLeftButtonWithShadowType) { | 222 if (type == kLeftButtonWithShadowType) { |
293 NSRect borderRect, contentRect; | 223 NSRect borderRect, contentRect; |
294 NSDivideRect(cellFrame, &borderRect, &contentRect, 1.0, NSMaxXEdge); | 224 NSDivideRect(cellFrame, &borderRect, &contentRect, 1.0, NSMaxXEdge); |
295 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton | 225 NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton |
296 state:active]; | 226 state:active]; |
297 [[stroke colorWithAlphaComponent:0.2] set]; | 227 [[stroke colorWithAlphaComponent:0.2] set]; |
298 NSRectFillUsingOperation(NSInsetRect(borderRect, 0, 2), | 228 NSRectFillUsingOperation(NSInsetRect(borderRect, 0, 2), |
299 NSCompositeSourceOver); | 229 NSCompositeSourceOver); |
300 innerFrame.size.width -= 1.0; | 230 innerFrame.size.width -= 1.0; |
301 } | 231 } |
302 [self drawInteriorWithFrame:innerFrame inView:controlView]; | 232 [self drawInteriorWithFrame:innerFrame inView:controlView]; |
303 } | 233 } |
304 | 234 |
305 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 235 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
306 GTMTheme* theme = [controlView gtm_theme]; | 236 GTMTheme* theme = [controlView gtm_theme]; |
307 | 237 |
308 if (shouldTheme_) { | 238 if (shouldTheme_) { |
309 BOOL isTemplate = [[self image] isTemplate]; | 239 BOOL isTemplate = [[self image] isTemplate]; |
310 | 240 |
311 [NSGraphicsContext saveGraphicsState]; | 241 [NSGraphicsContext saveGraphicsState]; |
312 | 242 |
313 CGContextRef context = | 243 CGContextRef context = |
314 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); | 244 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); |
315 | 245 |
316 NSColor* color = [theme iconColorForStyle:GTMThemeStyleToolBarButton | |
317 state:YES]; | |
318 | |
319 if (isTemplate) { | 246 if (isTemplate) { |
320 scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); | 247 scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); |
321 NSColor *shadowColor = [color gtm_legibleTextColor]; | 248 [shadow setShadowColor:[NSColor whiteColor]]; |
322 shadowColor = [shadowColor colorWithAlphaComponent:0.25]; | |
323 [shadow setShadowColor:shadowColor]; | |
324 [shadow setShadowOffset:NSMakeSize(0, -1.0)]; | 249 [shadow setShadowOffset:NSMakeSize(0, -1.0)]; |
325 [shadow setShadowBlurRadius:1.0]; | 250 [shadow setShadowBlurRadius:1.0]; |
326 [shadow set]; | 251 [shadow set]; |
327 } | 252 } |
328 | 253 |
329 [self drawUnderlayImageWithFrame:cellFrame inView:controlView]; | 254 [self drawUnderlayImageWithFrame:cellFrame inView:controlView]; |
330 | 255 |
331 CGContextBeginTransparencyLayer(context, 0); | 256 CGContextBeginTransparencyLayer(context, 0); |
332 NSRect imageRect = NSZeroRect; | 257 NSRect imageRect = NSZeroRect; |
333 imageRect.size = [[self image] size]; | 258 imageRect.size = [[self image] size]; |
334 NSRect drawRect = [self imageRectForBounds:cellFrame]; | |
335 [[self image] setFlipped:[controlView isFlipped]]; | 259 [[self image] setFlipped:[controlView isFlipped]]; |
336 [[self image] drawInRect:drawRect | 260 [[self image] drawInRect:[self imageRectForBounds:cellFrame] |
337 fromRect:imageRect | 261 fromRect:imageRect |
338 operation:NSCompositeSourceOver | 262 operation:NSCompositeSourceOver |
339 fraction:[self isEnabled] ? 1.0 : 0.5]; | 263 fraction:[self isEnabled] ? 1.0 : 0.5]; |
340 if (isTemplate) { | 264 if (isTemplate) { |
| 265 NSColor* color = [theme iconColorForStyle:GTMThemeStyleToolBarButton |
| 266 state:YES]; |
341 if (color) { | 267 if (color) { |
342 [color set]; | 268 [color set]; |
343 NSRectFillUsingOperation(cellFrame, NSCompositeSourceAtop); | 269 NSRectFillUsingOperation(cellFrame, NSCompositeSourceAtop); |
344 } | 270 } |
345 } | 271 } |
346 | 272 |
347 CGContextEndTransparencyLayer(context); | 273 CGContextEndTransparencyLayer(context); |
348 [NSGraphicsContext restoreGraphicsState]; | 274 [NSGraphicsContext restoreGraphicsState]; |
349 } else { | 275 } else { |
350 [self drawUnderlayImageWithFrame:cellFrame inView:controlView]; | 276 [self drawUnderlayImageWithFrame:cellFrame inView:controlView]; |
(...skipping 12 matching lines...) Expand all Loading... |
363 imageRect.size = [underlayImage_ size]; | 289 imageRect.size = [underlayImage_ size]; |
364 [underlayImage_ setFlipped:[controlView isFlipped]]; | 290 [underlayImage_ setFlipped:[controlView isFlipped]]; |
365 [underlayImage_ drawInRect:[self imageRectForBounds:cellFrame] | 291 [underlayImage_ drawInRect:[self imageRectForBounds:cellFrame] |
366 fromRect:imageRect | 292 fromRect:imageRect |
367 operation:NSCompositeSourceOver | 293 operation:NSCompositeSourceOver |
368 fraction:[self isEnabled] ? 1.0 : 0.5]; | 294 fraction:[self isEnabled] ? 1.0 : 0.5]; |
369 } | 295 } |
370 } | 296 } |
371 | 297 |
372 @end | 298 @end |
OLD | NEW |