Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / hover_close_button.mm
blobfa996763bd0bb9c736a1b03b0803f0ee83a55652
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/hover_close_button.h"
7 #include "grit/generated_resources.h"
8 #include "grit/theme_resources.h"
9 #include "grit/ui_resources.h"
10 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMKeyValueAnimation.h"
11 #include "ui/base/cocoa/animation_utils.h"
12 #include "ui/base/l10n/l10n_util.h"
13 #include "ui/base/resource/resource_bundle.h"
15 namespace  {
16 const CGFloat kFramesPerSecond = 16; // Determined experimentally to look good.
17 const CGFloat kCloseAnimationDuration = 0.1;
19 // Strings that are used for all close buttons. Set up in +initialize.
20 NSString* gTooltip = nil;
21 NSString* gDescription = nil;
23 // If this string is changed, the setter (currently setFadeOutValue:) must
24 // be changed as well to match.
25 NSString* const kFadeOutValueKeyPath = @"fadeOutValue";
26 }  // namespace
28 @interface HoverCloseButton ()
30 // Common initialization routine called from initWithFrame and awakeFromNib.
31 - (void)commonInit;
33 // Called by |fadeOutAnimation_| when animated value changes.
34 - (void)setFadeOutValue:(CGFloat)value;
36 // Gets the image for the given hover state.
37 - (NSImage*)imageForHoverState:(HoverState)hoverState;
39 @end
41 @implementation HoverCloseButton
43 + (void)initialize {
44   // Grab some strings that are used by all close buttons.
45   if (!gDescription)
46     gDescription = [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE) copy];
47   if (!gTooltip)
48     gTooltip = [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB) copy];
51 - (id)initWithFrame:(NSRect)frameRect {
52   if ((self = [super initWithFrame:frameRect])) {
53     [self commonInit];
54   }
55   return self;
58 - (void)awakeFromNib {
59   [super awakeFromNib];
60   [self commonInit];
63 - (void)removeFromSuperview {
64   // -stopAnimation will call the animationDidStop: delegate method
65   // which will release our animation.
66   [fadeOutAnimation_ stopAnimation];
67   [super removeFromSuperview];
70 - (void)animationDidStop:(NSAnimation*)animation {
71   DCHECK(animation == fadeOutAnimation_);
72   [fadeOutAnimation_ setDelegate:nil];
73   [fadeOutAnimation_ release];
74   fadeOutAnimation_ = nil;
77 - (void)animationDidEnd:(NSAnimation*)animation {
78   [self animationDidStop:animation];
81 - (void)drawRect:(NSRect)dirtyRect {
82   NSImage* image = [self imageForHoverState:[self hoverState]];
84   // Close boxes align left horizontally, and align center vertically.
85   // http:crbug.com/14739 requires this.
86   NSRect imageRect = NSZeroRect;
87   imageRect.size = [image size];
89   NSRect destRect = [self bounds];
90   destRect.origin.y = floor((NSHeight(destRect) / 2)
91                             - (NSHeight(imageRect) / 2));
92   destRect.size = imageRect.size;
94   switch(self.hoverState) {
95     case kHoverStateMouseOver:
96       [image drawInRect:destRect
97                fromRect:imageRect
98               operation:NSCompositeSourceOver
99                fraction:1.0
100          respectFlipped:YES
101                   hints:nil];
102       break;
104     case kHoverStateMouseDown:
105       [image drawInRect:destRect
106                fromRect:imageRect
107               operation:NSCompositeSourceOver
108                fraction:1.0
109          respectFlipped:YES
110                   hints:nil];
111       break;
113     case kHoverStateNone: {
114       CGFloat value = 1.0;
115       if (fadeOutAnimation_) {
116         value = [fadeOutAnimation_ currentValue];
117         NSImage* previousImage = nil;
118         if (previousState_ == kHoverStateMouseOver)
119           previousImage = [self imageForHoverState:kHoverStateMouseOver];
120         else
121           previousImage = [self imageForHoverState:kHoverStateMouseDown];
122         [previousImage drawInRect:destRect
123                          fromRect:imageRect
124                         operation:NSCompositeSourceOver
125                          fraction:1.0 - value
126                    respectFlipped:YES
127                             hints:nil];
128       }
129       [image drawInRect:destRect
130                fromRect:imageRect
131               operation:NSCompositeSourceOver
132                fraction:value
133          respectFlipped:YES
134                   hints:nil];
135       break;
136     }
137   }
140 - (void)setFadeOutValue:(CGFloat)value {
141   [self setNeedsDisplay];
144 - (NSImage*)imageForHoverState:(HoverState)hoverState {
145   int imageID = IDR_CLOSE_1;
146   switch (hoverState) {
147     case kHoverStateNone:
148       imageID = IDR_CLOSE_1;
149       break;
150     case kHoverStateMouseOver:
151       imageID = IDR_CLOSE_1_H;
152       break;
153     case kHoverStateMouseDown:
154       imageID = IDR_CLOSE_1_P;
155       break;
156   }
157   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
158   return bundle.GetNativeImageNamed(imageID).ToNSImage();
161 - (void)setHoverState:(HoverState)state {
162   if (state != self.hoverState) {
163     previousState_ = self.hoverState;
164     [super setHoverState:state];
165     // Only animate the HoverStateNone case.
166     if (state == kHoverStateNone) {
167       DCHECK(fadeOutAnimation_ == nil);
168       fadeOutAnimation_ =
169           [[GTMKeyValueAnimation alloc] initWithTarget:self
170                                                keyPath:kFadeOutValueKeyPath];
171       [fadeOutAnimation_ setDuration:kCloseAnimationDuration];
172       [fadeOutAnimation_ setFrameRate:kFramesPerSecond];
173       [fadeOutAnimation_ setDelegate:self];
174       [fadeOutAnimation_ startAnimation];
175     } else {
176       // -stopAnimation will call the animationDidStop: delegate method
177       // which will clean up the animation.
178       [fadeOutAnimation_ stopAnimation];
179     }
180   }
183 - (void)commonInit {
184   // Set accessibility description.
185   NSCell* cell = [self cell];
186   [cell accessibilitySetOverrideValue:gDescription
187                          forAttribute:NSAccessibilityDescriptionAttribute];
189   // Add a tooltip. Using 'owner:self' means that
190   // -view:stringForToolTip:point:userData: will be called to provide the
191   // tooltip contents immediately before showing it.
192   [self addToolTipRect:[self bounds] owner:self userData:NULL];
194   // Initialize previousState.
195   previousState_ = kHoverStateNone;
198 // Called each time a tooltip is about to be shown.
199 - (NSString*)view:(NSView*)view
200  stringForToolTip:(NSToolTipTag)tag
201             point:(NSPoint)point
202          userData:(void*)userData {
203   if (self.hoverState == kHoverStateMouseOver) {
204     // In some cases (e.g. the download tray), the button is still in the
205     // hover state, but is outside the bounds of its parent and not visible.
206     // Don't show the tooltip in that case.
207     NSRect buttonRect = [self frame];
208     NSRect parentRect = [[self superview] bounds];
209     if (NSIntersectsRect(buttonRect, parentRect))
210       return gTooltip;
211   }
213   return nil;  // Do not show the tooltip.
216 @end
218 @implementation WebUIHoverCloseButton
220 - (NSImage*)imageForHoverState:(HoverState)hoverState {
221   int imageID = IDR_CLOSE_DIALOG;
222   switch (hoverState) {
223     case kHoverStateNone:
224       imageID = IDR_CLOSE_DIALOG;
225       break;
226     case kHoverStateMouseOver:
227       imageID = IDR_CLOSE_DIALOG_H;
228       break;
229     case kHoverStateMouseDown:
230       imageID = IDR_CLOSE_DIALOG_P;
231       break;
232   }
233   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
234   return bundle.GetNativeImageNamed(imageID).ToNSImage();
237 @end