| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/browser/ui/cocoa/info_bubble_window.h" | 5 #import "chrome/browser/ui/cocoa/info_bubble_window.h" |
| 6 | 6 |
| 7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/mac/scoped_nsobject.h" | 9 #include "base/mac/scoped_nsobject.h" |
| 10 #include "chrome/browser/chrome_notification_types.h" | 10 #include "chrome/browser/chrome_notification_types.h" |
| 11 #include "content/public/browser/notification_observer.h" | 11 #include "content/public/browser/notification_observer.h" |
| 12 #include "content/public/browser/notification_registrar.h" | 12 #include "content/public/browser/notification_registrar.h" |
| 13 #include "content/public/browser/notification_service.h" | 13 #include "content/public/browser/notification_service.h" |
| 14 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h
" | 14 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h
" |
| 15 #import "ui/base/cocoa/window_animator.h" |
| 15 | 16 |
| 16 namespace { | 17 namespace { |
| 17 const CGFloat kOrderInSlideOffset = 10; | 18 const CGFloat kOrderInSlideOffset = 10; |
| 18 const NSTimeInterval kOrderInAnimationDuration = 0.075; | 19 const NSTimeInterval kOrderInAnimationDuration = 0.075; |
| 19 const NSTimeInterval kOrderOutAnimationDuration = 0.15; | 20 const NSTimeInterval kOrderOutAnimationDuration = 0.15; |
| 20 // The minimum representable time interval. This can be used as the value | |
| 21 // passed to +[NSAnimationContext setDuration:] to stop an in-progress | |
| 22 // animation as quickly as possible. | |
| 23 const NSTimeInterval kMinimumTimeInterval = | |
| 24 std::numeric_limits<NSTimeInterval>::min(); | |
| 25 } // namespace | 21 } // namespace |
| 26 | 22 |
| 27 @interface InfoBubbleWindow (Private) | 23 @interface InfoBubbleWindow (Private) |
| 28 - (void)appIsTerminating; | 24 - (void)appIsTerminating; |
| 29 - (void)finishCloseAfterAnimation; | |
| 30 @end | 25 @end |
| 31 | 26 |
| 32 // A helper class to proxy app notifications to the window. | 27 // A helper class to proxy app notifications to the window. |
| 33 class AppNotificationBridge : public content::NotificationObserver { | 28 class AppNotificationBridge : public content::NotificationObserver { |
| 34 public: | 29 public: |
| 35 explicit AppNotificationBridge(InfoBubbleWindow* owner) : owner_(owner) { | 30 explicit AppNotificationBridge(InfoBubbleWindow* owner) : owner_(owner) { |
| 36 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, | 31 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, |
| 37 content::NotificationService::AllSources()); | 32 content::NotificationService::AllSources()); |
| 38 } | 33 } |
| 39 | 34 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 53 private: | 48 private: |
| 54 // The object we need to inform when we get a notification. Weak. Owns us. | 49 // The object we need to inform when we get a notification. Weak. Owns us. |
| 55 InfoBubbleWindow* owner_; | 50 InfoBubbleWindow* owner_; |
| 56 | 51 |
| 57 // Used for registering to receive notifications and automatic clean up. | 52 // Used for registering to receive notifications and automatic clean up. |
| 58 content::NotificationRegistrar registrar_; | 53 content::NotificationRegistrar registrar_; |
| 59 | 54 |
| 60 DISALLOW_COPY_AND_ASSIGN(AppNotificationBridge); | 55 DISALLOW_COPY_AND_ASSIGN(AppNotificationBridge); |
| 61 }; | 56 }; |
| 62 | 57 |
| 63 // A delegate object for watching the alphaValue animation on InfoBubbleWindows. | |
| 64 // An InfoBubbleWindow instance cannot be the delegate for its own animation | |
| 65 // because CAAnimations retain their delegates, and since the InfoBubbleWindow | |
| 66 // retains its animations a retain loop would be formed. | |
| 67 @interface InfoBubbleWindowCloser : NSObject { | |
| 68 @private | |
| 69 InfoBubbleWindow* window_; // Weak. Window to close. | |
| 70 } | |
| 71 - (id)initWithWindow:(InfoBubbleWindow*)window; | |
| 72 @end | |
| 73 | |
| 74 @implementation InfoBubbleWindowCloser | |
| 75 | |
| 76 - (id)initWithWindow:(InfoBubbleWindow*)window { | |
| 77 if ((self = [super init])) { | |
| 78 window_ = window; | |
| 79 } | |
| 80 return self; | |
| 81 } | |
| 82 | |
| 83 // Callback for the alpha animation. Closes window_ if appropriate. | |
| 84 - (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag { | |
| 85 // When alpha reaches zero, close window_. | |
| 86 if ([window_ alphaValue] == 0.0) { | |
| 87 [window_ finishCloseAfterAnimation]; | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 @end | |
| 92 | |
| 93 | |
| 94 @implementation InfoBubbleWindow | 58 @implementation InfoBubbleWindow |
| 95 | 59 |
| 96 @synthesize allowedAnimations = allowedAnimations_; | 60 @synthesize allowedAnimations = allowedAnimations_; |
| 97 @synthesize canBecomeKeyWindow = canBecomeKeyWindow_; | 61 @synthesize canBecomeKeyWindow = canBecomeKeyWindow_; |
| 98 @synthesize allowShareParentKeyState = allowShareParentKeyState_; | 62 @synthesize allowShareParentKeyState = allowShareParentKeyState_; |
| 99 | 63 |
| 100 - (id)initWithContentRect:(NSRect)contentRect | 64 - (id)initWithContentRect:(NSRect)contentRect |
| 101 styleMask:(NSUInteger)aStyle | 65 styleMask:(NSUInteger)aStyle |
| 102 backing:(NSBackingStoreType)bufferingType | 66 backing:(NSBackingStoreType)bufferingType |
| 103 defer:(BOOL)flag { | 67 defer:(BOOL)flag { |
| 104 if ((self = [super initWithContentRect:contentRect | 68 if ((self = [super initWithContentRect:contentRect |
| 105 styleMask:NSBorderlessWindowMask | 69 styleMask:NSBorderlessWindowMask |
| 106 backing:bufferingType | 70 backing:bufferingType |
| 107 defer:flag])) { | 71 defer:flag])) { |
| 108 [self setBackgroundColor:[NSColor clearColor]]; | 72 [self setBackgroundColor:[NSColor clearColor]]; |
| 109 [self setExcludedFromWindowsMenu:YES]; | 73 [self setExcludedFromWindowsMenu:YES]; |
| 110 [self setAllowShareParentKeyState:YES]; | 74 [self setAllowShareParentKeyState:YES]; |
| 111 [self setOpaque:NO]; | 75 [self setOpaque:NO]; |
| 112 [self setHasShadow:YES]; | 76 [self setHasShadow:YES]; |
| 113 canBecomeKeyWindow_ = YES; | 77 canBecomeKeyWindow_ = YES; |
| 114 allowedAnimations_ = info_bubble::kAnimateOrderIn | | 78 allowedAnimations_ = info_bubble::kAnimateOrderIn | |
| 115 info_bubble::kAnimateOrderOut; | 79 info_bubble::kAnimateOrderOut; |
| 116 notificationBridge_.reset(new AppNotificationBridge(self)); | 80 notificationBridge_.reset(new AppNotificationBridge(self)); |
| 117 | 81 |
| 118 // Start invisible. Will be made visible when ordered front. | 82 // Start invisible. Will be made visible when ordered front. |
| 119 [self setAlphaValue:0.0]; | 83 [self setAlphaValue:0.0]; |
| 120 | 84 |
| 121 // Set up alphaValue animation so that self is delegate for the animation. | 85 animation_controller_.reset([[WindowAnimationController alloc] init]); |
| 122 // Setting up the delegate is required so that the | 86 [animation_controller_ setOrderInDuration:kOrderInAnimationDuration]; |
| 123 // animationDidStop:finished: callback can be handled. | 87 [animation_controller_ setOrderOutDuration:kOrderOutAnimationDuration]; |
| 124 // Notice that only the alphaValue Animation is replaced in case | |
| 125 // superclasses set up animations. | |
| 126 CAAnimation* alphaAnimation = [CABasicAnimation animation]; | |
| 127 base::scoped_nsobject<InfoBubbleWindowCloser> delegate( | |
| 128 [[InfoBubbleWindowCloser alloc] initWithWindow:self]); | |
| 129 [alphaAnimation setDelegate:delegate]; | |
| 130 NSMutableDictionary* animations = | |
| 131 [NSMutableDictionary dictionaryWithDictionary:[self animations]]; | |
| 132 [animations setObject:alphaAnimation forKey:@"alphaValue"]; | |
| 133 [self setAnimations:animations]; | |
| 134 } | 88 } |
| 135 return self; | 89 return self; |
| 136 } | 90 } |
| 137 | 91 |
| 138 // According to | 92 // According to |
| 139 // http://www.cocoabuilder.com/archive/message/cocoa/2006/6/19/165953, | 93 // http://www.cocoabuilder.com/archive/message/cocoa/2006/6/19/165953, |
| 140 // NSBorderlessWindowMask windows cannot become key or main. In this | 94 // NSBorderlessWindowMask windows cannot become key or main. In this |
| 141 // case, this is not necessarily a desired behavior. As an example, the | 95 // case, this is not necessarily a desired behavior. As an example, the |
| 142 // bubble could have buttons. | 96 // bubble could have buttons. |
| 143 - (BOOL)canBecomeKeyWindow { | 97 - (BOOL)canBecomeKeyWindow { |
| 144 return canBecomeKeyWindow_; | 98 return canBecomeKeyWindow_; |
| 145 } | 99 } |
| 146 | 100 |
| 147 // Lets the traffic light buttons on the browser window keep their "active" | 101 // Lets the traffic light buttons on the browser window keep their "active" |
| 148 // state while an info bubble is open. Only has an effect on 10.7. | 102 // state while an info bubble is open. Only has an effect on 10.7. |
| 149 - (BOOL)_sharesParentKeyState { | 103 - (BOOL)_sharesParentKeyState { |
| 150 return allowShareParentKeyState_; | 104 return allowShareParentKeyState_; |
| 151 } | 105 } |
| 152 | 106 |
| 153 - (void)close { | 107 - (void)animateClose { |
| 154 // Block the window from receiving events while it fades out. | 108 if ((allowedAnimations_ & info_bubble::kAnimateOrderOut) == 0) { |
| 155 closing_ = YES; | 109 [self close]; |
| 110 return; |
| 111 } |
| 156 | 112 |
| 157 if ((allowedAnimations_ & info_bubble::kAnimateOrderOut) == 0) { | 113 // Fade out in place and close. |
| 158 [self finishCloseAfterAnimation]; | 114 [animation_controller_ animateWindow:self |
| 159 } else { | 115 targetOrigin:[self frame].origin |
| 160 // Apply animations to hide self. | 116 closing:YES |
| 161 [NSAnimationContext beginGrouping]; | 117 callback:base::Closure()]; |
| 162 [[NSAnimationContext currentContext] | |
| 163 gtm_setDuration:kOrderOutAnimationDuration | |
| 164 eventMask:NSLeftMouseUpMask]; | |
| 165 [[self animator] setAlphaValue:0.0]; | |
| 166 [NSAnimationContext endGrouping]; | |
| 167 } | |
| 168 } | 118 } |
| 169 | 119 |
| 170 // If the app is terminating but the window is still fading out, cancel the | 120 // If the app is terminating but the window is still fading out, cancel the |
| 171 // animation and close the window to prevent it from leaking. | 121 // animation and close the window to prevent it from leaking. |
| 172 // See http://crbug.com/37717 | 122 // See http://crbug.com/37717 |
| 173 - (void)appIsTerminating { | 123 - (void)appIsTerminating { |
| 174 if ((allowedAnimations_ & info_bubble::kAnimateOrderOut) == 0) | 124 [animation_controller_ cancelAnimationWithCallback]; |
| 175 return; // The close has already happened with no Core Animation. | |
| 176 | |
| 177 // Cancel the current animation so that it closes immediately, triggering | |
| 178 // |finishCloseAfterAnimation|. | |
| 179 [NSAnimationContext beginGrouping]; | |
| 180 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; | |
| 181 [[self animator] setAlphaValue:0.0]; | |
| 182 [NSAnimationContext endGrouping]; | |
| 183 } | |
| 184 | |
| 185 // Called by InfoBubbleWindowCloser when the window is to be really closed | |
| 186 // after the fading animation is complete. | |
| 187 - (void)finishCloseAfterAnimation { | |
| 188 if (closing_) | |
| 189 [super close]; | |
| 190 } | 125 } |
| 191 | 126 |
| 192 // Adds animation for info bubbles being ordered to the front. | 127 // Adds animation for info bubbles being ordered to the front. |
| 193 - (void)orderWindow:(NSWindowOrderingMode)orderingMode | 128 - (void)orderWindow:(NSWindowOrderingMode)orderingMode |
| 194 relativeTo:(NSInteger)otherWindowNumber { | 129 relativeTo:(NSInteger)otherWindowNumber { |
| 195 // According to the documentation '0' is the otherWindowNumber when the window | 130 // According to the documentation '0' is the otherWindowNumber when the window |
| 196 // is ordered front. | 131 // is ordered front. |
| 197 if (orderingMode == NSWindowAbove && otherWindowNumber == 0) { | 132 if (orderingMode == NSWindowAbove && otherWindowNumber == 0) { |
| 198 // Order self appropriately assuming that its alpha is zero as set up | 133 // Order self appropriately assuming that its alpha is zero as set up |
| 199 // in the designated initializer. | 134 // in the designated initializer. |
| 200 [super orderWindow:orderingMode relativeTo:otherWindowNumber]; | 135 [super orderWindow:orderingMode relativeTo:otherWindowNumber]; |
| 201 | 136 |
| 202 // Set up frame so it can be adjust down by a few pixels. | 137 // Set up frame so it can be adjust down by a few pixels. |
| 203 NSRect frame = [self frame]; | 138 NSRect frame = [self frame]; |
| 204 NSPoint newOrigin = frame.origin; | 139 NSPoint newOrigin = frame.origin; |
| 205 newOrigin.y += kOrderInSlideOffset; | 140 newOrigin.y += kOrderInSlideOffset; |
| 206 [self setFrameOrigin:newOrigin]; | 141 [self setFrameOrigin:newOrigin]; |
| 207 | 142 |
| 208 // Apply animations to show and move self. | 143 [animation_controller_ animateWindow:self |
| 209 [NSAnimationContext beginGrouping]; | 144 targetOrigin:frame.origin |
| 210 // The star currently triggers on mouse down, not mouse up. | 145 closing:NO |
| 211 NSTimeInterval duration = | 146 callback:base::Closure()]; |
| 212 (allowedAnimations_ & info_bubble::kAnimateOrderIn) | |
| 213 ? kOrderInAnimationDuration : kMinimumTimeInterval; | |
| 214 [[NSAnimationContext currentContext] | |
| 215 gtm_setDuration:duration | |
| 216 eventMask:NSLeftMouseUpMask | NSLeftMouseDownMask]; | |
| 217 [[self animator] setAlphaValue:1.0]; | |
| 218 [[self animator] setFrame:frame display:YES]; | |
| 219 [NSAnimationContext endGrouping]; | |
| 220 } else { | 147 } else { |
| 221 [super orderWindow:orderingMode relativeTo:otherWindowNumber]; | 148 [super orderWindow:orderingMode relativeTo:otherWindowNumber]; |
| 222 } | 149 } |
| 223 } | 150 } |
| 224 | 151 |
| 225 // If the window is currently animating a close, block all UI events to the | 152 // If the window is currently animating a close, block all UI events to the |
| 226 // window. | 153 // window. |
| 227 - (void)sendEvent:(NSEvent*)theEvent { | 154 - (void)sendEvent:(NSEvent*)theEvent { |
| 228 if (!closing_) | 155 if (![self isClosing]) |
| 229 [super sendEvent:theEvent]; | 156 [super sendEvent:theEvent]; |
| 230 } | 157 } |
| 231 | 158 |
| 232 - (BOOL)isClosing { | 159 - (BOOL)isClosing { |
| 233 return closing_; | 160 return [animation_controller_ isClosing]; |
| 234 } | 161 } |
| 235 | 162 |
| 236 @end | 163 @end |
| OLD | NEW |