Chromium Code Reviews| 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 #include "chrome/browser/ui/cocoa/status_bubble_mac.h" | 5 #include "chrome/browser/ui/cocoa/status_bubble_mac.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| 11 #include "base/mac/mac_util.h" | 11 #include "base/mac/mac_util.h" |
| 12 #include "base/mac/scoped_block.h" | |
| 12 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
| 13 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 14 #include "base/strings/sys_string_conversions.h" | 15 #include "base/strings/sys_string_conversions.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 16 #import "chrome/browser/ui/cocoa/bubble_view.h" | 17 #import "chrome/browser/ui/cocoa/bubble_view.h" |
| 17 #include "chrome/browser/ui/elide_url.h" | 18 #include "chrome/browser/ui/elide_url.h" |
| 18 #include "net/base/net_util.h" | 19 #include "net/base/net_util.h" |
| 19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h " | 20 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h " |
| 20 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSBezierPath+RoundRect .h" | 21 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSBezierPath+RoundRect .h" |
| 21 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSColor+Luminance.h" | 22 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSColor+Luminance.h" |
| 22 #include "ui/base/cocoa/window_size_constants.h" | 23 #include "ui/base/cocoa/window_size_constants.h" |
| 23 #include "ui/gfx/font_list.h" | 24 #include "ui/gfx/font_list.h" |
| 24 #include "ui/gfx/point.h" | 25 #include "ui/gfx/point.h" |
| 25 #include "ui/gfx/text_elider.h" | 26 #include "ui/gfx/text_elider.h" |
| 26 #include "ui/gfx/text_utils.h" | 27 #include "ui/gfx/text_utils.h" |
| 27 | 28 |
| 28 namespace { | 29 namespace { |
| 29 | 30 |
| 30 const int kWindowHeight = 18; | 31 const int kWindowHeight = 18; |
| 31 | 32 |
| 32 // The width of the bubble in relation to the width of the parent window. | 33 // The width of the bubble in relation to the width of the parent window. |
| 33 const CGFloat kWindowWidthPercent = 1.0 / 3.0; | 34 const CGFloat kWindowWidthPercent = 1.0 / 3.0; |
| 34 | 35 |
| 35 // How close the mouse can get to the infobubble before it starts sliding | 36 // How close the mouse can get to the infobubble before it starts sliding |
| 36 // off-screen. | 37 // off-screen. |
| 37 const int kMousePadding = 20; | 38 const int kMousePadding = 20; |
| 38 | 39 |
| 39 const int kTextPadding = 3; | 40 const int kTextPadding = 3; |
| 40 | 41 |
| 41 // The animation key used for fade-in and fade-out transitions. | |
| 42 NSString* const kFadeAnimationKey = @"alphaValue"; | |
| 43 | |
| 44 // The status bubble's maximum opacity, when fully faded in. | 42 // The status bubble's maximum opacity, when fully faded in. |
| 45 const CGFloat kBubbleOpacity = 1.0; | 43 const CGFloat kBubbleOpacity = 1.0; |
| 46 | 44 |
| 47 // Delay before showing or hiding the bubble after a SetStatus or SetURL call. | 45 // Delay before showing or hiding the bubble after a SetStatus or SetURL call. |
| 48 const int64 kShowDelayMS = 80; | 46 const int64 kShowDelayMS = 80; |
| 49 const int64 kHideDelayMS = 250; | 47 const int64 kHideDelayMS = 250; |
| 50 | 48 |
| 51 // How long each fade should last. | 49 // How long each fade should last. |
| 52 const NSTimeInterval kShowFadeInDurationSeconds = 0.120; | 50 const NSTimeInterval kShowFadeInDurationSeconds = 0.120; |
| 53 const NSTimeInterval kHideFadeOutDurationSeconds = 0.200; | 51 const NSTimeInterval kHideFadeOutDurationSeconds = 0.200; |
| 54 | 52 |
| 55 // The minimum representable time interval. This can be used as the value | 53 // The minimum representable time interval. This can be used as the value |
| 56 // passed to +[NSAnimationContext setDuration:] to stop an in-progress | 54 // passed to +[NSAnimationContext setDuration:] to stop an in-progress |
| 57 // animation as quickly as possible. | 55 // animation as quickly as possible. |
| 58 const NSTimeInterval kMinimumTimeInterval = | 56 const NSTimeInterval kMinimumTimeInterval = |
| 59 std::numeric_limits<NSTimeInterval>::min(); | 57 std::numeric_limits<NSTimeInterval>::min(); |
| 60 | 58 |
| 61 // How quickly the status bubble should expand. | 59 // How quickly the status bubble should expand. |
| 62 const CGFloat kExpansionDurationSeconds = 0.125; | 60 const CGFloat kExpansionDurationSeconds = 0.125; |
| 63 | 61 |
| 64 } // namespace | 62 } // namespace |
| 65 | 63 |
| 64 #if !defined(MAC_OS_X_VERSION_10_7) || \ | |
| 65 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 | |
| 66 @interface NSAnimationContext (LionSDK) | |
|
Mark Mentovai
2014/02/14 20:34:04
I think we like to unify these in base/mac/sdk_for
Avi (use Gerrit)
2014/02/14 20:44:33
Done.
| |
| 67 + (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes | |
|
Mark Mentovai
2014/02/14 20:34:04
This file, like most Chrome code, prefers “Type* v
Avi (use Gerrit)
2014/02/14 20:44:33
Done.
| |
| 68 completionHandler:(void (^)(void))completionHandler; | |
| 69 @end | |
| 70 #endif // MAC_OS_X_VERSION_10_7 | |
| 71 | |
| 66 @interface StatusBubbleAnimationDelegate : NSObject { | 72 @interface StatusBubbleAnimationDelegate : NSObject { |
| 67 @private | 73 @private |
| 68 StatusBubbleMac* statusBubble_; // weak; owns us indirectly | 74 base::mac::ScopedBlock<void (^)(void)> completionHandler_; |
| 69 } | 75 } |
| 70 | 76 |
| 71 - (id)initWithStatusBubble:(StatusBubbleMac*)statusBubble; | 77 - (id)initWithCompletionHandler:(void (^)(void))completionHandler; |
| 72 | |
| 73 // Invalidates this object so that no further calls will be made to | |
| 74 // statusBubble_. This should be called when statusBubble_ is released, to | |
| 75 // prevent attempts to call into the released object. | |
| 76 - (void)invalidate; | |
| 77 | 78 |
| 78 // CAAnimation delegate method | 79 // CAAnimation delegate method |
| 79 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished; | 80 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished; |
| 80 @end | 81 @end |
| 81 | 82 |
| 82 @implementation StatusBubbleAnimationDelegate | 83 @implementation StatusBubbleAnimationDelegate |
| 83 | 84 |
| 84 - (id)initWithStatusBubble:(StatusBubbleMac*)statusBubble { | 85 - (id)initWithCompletionHandler:(void (^)(void))completionHandler { |
| 85 if ((self = [super init])) { | 86 if ((self = [super init])) { |
| 86 statusBubble_ = statusBubble; | 87 completionHandler_.reset(completionHandler, base::scoped_policy::RETAIN); |
| 87 } | 88 } |
| 88 | 89 |
| 89 return self; | 90 return self; |
| 90 } | 91 } |
| 91 | 92 |
| 92 - (void)invalidate { | |
| 93 statusBubble_ = NULL; | |
| 94 } | |
| 95 | |
| 96 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished { | 93 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished { |
| 97 if (statusBubble_) | 94 completionHandler_.get()(); |
| 98 statusBubble_->AnimationDidStop(animation, finished); | |
| 99 } | 95 } |
| 100 | 96 |
| 101 @end | 97 @end |
| 98 | |
| 99 @interface StatusBubbleWindow : NSWindow { | |
| 100 @private | |
| 101 void (^completionHandler_)(void); | |
| 102 } | |
| 103 | |
| 104 - (id)animationForKey:(NSString *)key; | |
| 105 - (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes | |
| 106 completionHandler:(void (^)(void))completionHandler; | |
| 107 @end | |
| 108 | |
| 109 @implementation StatusBubbleWindow | |
| 110 | |
| 111 - (id)animationForKey:(NSString *)key { | |
| 112 CAAnimation* animation = [super animationForKey:key]; | |
| 113 // If completionHandler_ isn't nil, then this is the first of (potentially) | |
| 114 // multiple animations in a grouping; give it the completion handler. If | |
| 115 // completionHandler_ is nil, then some other animation was tagged with the | |
| 116 // completion handler. | |
| 117 if (completionHandler_) { | |
|
Mark Mentovai
2014/02/14 20:34:04
Can you DCHECK in this block that you’re on 10.6?
Avi (use Gerrit)
2014/02/14 20:44:33
Sure.
(You do mean that NSAnimationContext DOESN'
Mark Mentovai
2014/02/14 20:49:21
Avi wrote:
| |
| 118 StatusBubbleAnimationDelegate* animation_delegate = | |
| 119 [[StatusBubbleAnimationDelegate alloc] | |
| 120 initWithCompletionHandler:completionHandler_]; | |
| 121 [animation setDelegate:animation_delegate]; | |
| 122 completionHandler_ = nil; | |
|
Mark Mentovai
2014/02/14 20:34:04
Crafty.
Avi (use Gerrit)
2014/02/14 20:44:33
Thank you.
| |
| 123 } | |
| 124 return animation; | |
| 125 } | |
| 126 | |
| 127 - (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes | |
| 128 completionHandler:(void (^)(void))completionHandler { | |
| 129 if ([NSAnimationContext respondsToSelector: | |
| 130 @selector(runAnimationGroup:completionHandler:)]) { | |
| 131 [NSAnimationContext runAnimationGroup:changes | |
| 132 completionHandler:completionHandler]; | |
| 133 } else { | |
| 134 // Mac OS 10.6 does not have completion handler callbacks at the Cocoa | |
| 135 // level, only at the CoreAnimation level. So intercept calls made to | |
| 136 // -animationForKey: and tag one of the animations with a delegate that will | |
| 137 // execute the completion handler. | |
| 138 completionHandler_ = completionHandler; | |
| 139 [NSAnimationContext beginGrouping]; | |
| 140 changes([NSAnimationContext currentContext]); | |
| 141 // At this point, -animationForKey should have been called by CoreAnimation | |
| 142 // to set up the animation to run. Verify this. | |
| 143 DCHECK(completionHandler_ == nil); | |
| 144 [NSAnimationContext endGrouping]; | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 @end | |
| 102 | 149 |
| 103 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) | 150 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) |
| 104 : timer_factory_(this), | 151 : timer_factory_(this), |
| 105 expand_timer_factory_(this), | 152 expand_timer_factory_(this), |
| 153 completion_handler_factory_(this), | |
| 106 parent_(parent), | 154 parent_(parent), |
| 107 delegate_(delegate), | 155 delegate_(delegate), |
| 108 window_(nil), | 156 window_(nil), |
| 109 status_text_(nil), | 157 status_text_(nil), |
| 110 url_text_(nil), | 158 url_text_(nil), |
| 111 state_(kBubbleHidden), | 159 state_(kBubbleHidden), |
| 112 immediate_(false), | 160 immediate_(false), |
| 113 is_expanded_(false) { | 161 is_expanded_(false) { |
| 114 Create(); | 162 Create(); |
| 115 Attach(); | 163 Attach(); |
| 116 } | 164 } |
| 117 | 165 |
| 118 StatusBubbleMac::~StatusBubbleMac() { | 166 StatusBubbleMac::~StatusBubbleMac() { |
| 119 DCHECK(window_); | 167 DCHECK(window_); |
| 120 | 168 |
| 121 Hide(); | 169 Hide(); |
| 122 | 170 |
| 123 [[[window_ animationForKey:kFadeAnimationKey] delegate] invalidate]; | 171 completion_handler_factory_.InvalidateWeakPtrs(); |
| 124 Detach(); | 172 Detach(); |
| 125 [window_ release]; | 173 [window_ release]; |
| 126 window_ = nil; | 174 window_ = nil; |
| 127 } | 175 } |
| 128 | 176 |
| 129 void StatusBubbleMac::SetStatus(const base::string16& status) { | 177 void StatusBubbleMac::SetStatus(const base::string16& status) { |
| 130 SetText(status, false); | 178 SetText(status, false); |
| 131 } | 179 } |
| 132 | 180 |
| 133 void StatusBubbleMac::SetURL(const GURL& url, const std::string& languages) { | 181 void StatusBubbleMac::SetURL(const GURL& url, const std::string& languages) { |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 230 is_expanded_ = false; | 278 is_expanded_ = false; |
| 231 | 279 |
| 232 bool fade_out = false; | 280 bool fade_out = false; |
| 233 if (state_ == kBubbleHidingFadeOut || state_ == kBubbleShowingFadeIn) { | 281 if (state_ == kBubbleHidingFadeOut || state_ == kBubbleShowingFadeIn) { |
| 234 SetState(kBubbleHidingFadeOut); | 282 SetState(kBubbleHidingFadeOut); |
| 235 | 283 |
| 236 if (!immediate_) { | 284 if (!immediate_) { |
| 237 // An animation is in progress. Cancel it by starting a new animation. | 285 // An animation is in progress. Cancel it by starting a new animation. |
| 238 // Use kMinimumTimeInterval to set the opacity as rapidly as possible. | 286 // Use kMinimumTimeInterval to set the opacity as rapidly as possible. |
| 239 fade_out = true; | 287 fade_out = true; |
| 240 [NSAnimationContext beginGrouping]; | 288 AnimateWindowAlpha(0.0, kMinimumTimeInterval); |
| 241 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; | |
| 242 [[window_ animator] setAlphaValue:0.0]; | |
| 243 [NSAnimationContext endGrouping]; | |
| 244 } | 289 } |
| 245 } | 290 } |
| 246 | 291 |
| 247 if (!fade_out) { | 292 if (!fade_out) { |
| 248 // No animation is in progress, so the opacity can be set directly. | 293 // No animation is in progress, so the opacity can be set directly. |
| 249 [window_ setAlphaValue:0.0]; | 294 [window_ setAlphaValue:0.0]; |
| 250 SetState(kBubbleHidden); | 295 SetState(kBubbleHidden); |
| 251 } | 296 } |
| 252 | 297 |
| 253 // Stop any width animation and reset the bubble size. | 298 // Stop any width animation and reset the bubble size. |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 357 SetFrameAvoidingMouse([window_ frame], location); | 402 SetFrameAvoidingMouse([window_ frame], location); |
| 358 } | 403 } |
| 359 | 404 |
| 360 void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { | 405 void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { |
| 361 UpdateSizeAndPosition(); | 406 UpdateSizeAndPosition(); |
| 362 } | 407 } |
| 363 | 408 |
| 364 void StatusBubbleMac::Create() { | 409 void StatusBubbleMac::Create() { |
| 365 DCHECK(!window_); | 410 DCHECK(!window_); |
| 366 | 411 |
| 367 window_ = [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater | 412 window_ = [[StatusBubbleWindow alloc] |
| 368 styleMask:NSBorderlessWindowMask | 413 initWithContentRect:ui::kWindowSizeDeterminedLater |
| 369 backing:NSBackingStoreBuffered | 414 styleMask:NSBorderlessWindowMask |
| 370 defer:YES]; | 415 backing:NSBackingStoreBuffered |
| 416 defer:YES]; | |
| 371 [window_ setMovableByWindowBackground:NO]; | 417 [window_ setMovableByWindowBackground:NO]; |
| 372 [window_ setBackgroundColor:[NSColor clearColor]]; | 418 [window_ setBackgroundColor:[NSColor clearColor]]; |
| 373 [window_ setLevel:NSNormalWindowLevel]; | 419 [window_ setLevel:NSNormalWindowLevel]; |
| 374 [window_ setOpaque:NO]; | 420 [window_ setOpaque:NO]; |
| 375 [window_ setHasShadow:NO]; | 421 [window_ setHasShadow:NO]; |
| 376 | 422 |
| 377 // We do not need to worry about the bubble outliving |parent_| because our | 423 // We do not need to worry about the bubble outliving |parent_| because our |
| 378 // teardown sequence in BWC guarantees that |parent_| outlives the status | 424 // teardown sequence in BWC guarantees that |parent_| outlives the status |
| 379 // bubble and that the StatusBubble is torn down completely prior to the | 425 // bubble and that the StatusBubble is torn down completely prior to the |
| 380 // window going away. | 426 // window going away. |
| 381 base::scoped_nsobject<BubbleView> view( | 427 base::scoped_nsobject<BubbleView> view( |
| 382 [[BubbleView alloc] initWithFrame:NSZeroRect themeProvider:parent_]); | 428 [[BubbleView alloc] initWithFrame:NSZeroRect themeProvider:parent_]); |
| 383 [window_ setContentView:view]; | 429 [window_ setContentView:view]; |
| 384 | 430 |
| 385 [window_ setAlphaValue:0.0]; | 431 [window_ setAlphaValue:0.0]; |
| 386 | 432 |
| 387 // TODO(dtseng): Ignore until we provide NSAccessibility support. | 433 // TODO(dtseng): Ignore until we provide NSAccessibility support. |
| 388 [window_ accessibilitySetOverrideValue:NSAccessibilityUnknownRole | 434 [window_ accessibilitySetOverrideValue:NSAccessibilityUnknownRole |
| 389 forAttribute:NSAccessibilityRoleAttribute]; | 435 forAttribute:NSAccessibilityRoleAttribute]; |
| 390 | 436 |
| 391 // Set a delegate for the fade-in and fade-out transitions to be notified | |
| 392 // when fades are complete. The ownership model is for window_ to own | |
| 393 // animation_dictionary, which owns animation, which owns | |
| 394 // animation_delegate. | |
| 395 CAAnimation* animation = [[window_ animationForKey:kFadeAnimationKey] copy]; | |
| 396 [animation autorelease]; | |
| 397 StatusBubbleAnimationDelegate* animation_delegate = | |
| 398 [[StatusBubbleAnimationDelegate alloc] initWithStatusBubble:this]; | |
| 399 [animation_delegate autorelease]; | |
| 400 [animation setDelegate:animation_delegate]; | |
| 401 NSMutableDictionary* animation_dictionary = | |
| 402 [NSMutableDictionary dictionaryWithDictionary:[window_ animations]]; | |
| 403 [animation_dictionary setObject:animation forKey:kFadeAnimationKey]; | |
| 404 [window_ setAnimations:animation_dictionary]; | |
| 405 | |
| 406 [view setCornerFlags:kRoundedTopRightCorner]; | 437 [view setCornerFlags:kRoundedTopRightCorner]; |
| 407 MouseMoved(gfx::Point(), false); | 438 MouseMoved(gfx::Point(), false); |
| 408 } | 439 } |
| 409 | 440 |
| 410 void StatusBubbleMac::Attach() { | 441 void StatusBubbleMac::Attach() { |
| 411 DCHECK(!is_attached()); | 442 DCHECK(!is_attached()); |
| 412 | 443 |
| 413 [window_ orderFront:nil]; | 444 [window_ orderFront:nil]; |
| 414 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; | 445 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; |
| 415 | 446 |
| 416 [[window_ contentView] setThemeProvider:parent_]; | 447 [[window_ contentView] setThemeProvider:parent_]; |
| 417 } | 448 } |
| 418 | 449 |
| 419 void StatusBubbleMac::Detach() { | 450 void StatusBubbleMac::Detach() { |
| 420 DCHECK(is_attached()); | 451 DCHECK(is_attached()); |
| 421 | 452 |
| 422 // Magic setFrame: See http://crbug.com/58506 and http://crrev.com/3564021 . | 453 // Magic setFrame: See http://crbug.com/58506 and http://crrev.com/3564021 . |
| 423 [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:NO]; | 454 [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:NO]; |
| 424 [parent_ removeChildWindow:window_]; // See crbug.com/28107 ... | 455 [parent_ removeChildWindow:window_]; // See crbug.com/28107 ... |
| 425 [window_ orderOut:nil]; // ... and crbug.com/29054. | 456 [window_ orderOut:nil]; // ... and crbug.com/29054. |
| 426 | 457 |
| 427 [[window_ contentView] setThemeProvider:nil]; | 458 [[window_ contentView] setThemeProvider:nil]; |
| 428 } | 459 } |
| 429 | 460 |
| 430 void StatusBubbleMac::AnimationDidStop(CAAnimation* animation, bool finished) { | 461 void StatusBubbleMac::AnimationDidStop() { |
| 431 DCHECK([NSThread isMainThread]); | 462 DCHECK([NSThread isMainThread]); |
| 432 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); | 463 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); |
| 433 DCHECK(is_attached()); | 464 DCHECK(is_attached()); |
| 434 | 465 |
| 435 if (finished) { | 466 if (state_ == kBubbleShowingFadeIn) { |
| 436 // Because of the mechanism used to interrupt animations, this is never | 467 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); |
| 437 // actually called with finished set to false. If animations ever become | 468 SetState(kBubbleShown); |
| 438 // directly interruptible, the check will ensure that state_ remains | 469 } else { |
| 439 // properly synchronized. | 470 DCHECK_EQ([[window_ animator] alphaValue], 0.0); |
| 440 if (state_ == kBubbleShowingFadeIn) { | 471 SetState(kBubbleHidden); |
| 441 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); | |
| 442 SetState(kBubbleShown); | |
| 443 } else { | |
| 444 DCHECK_EQ([[window_ animator] alphaValue], 0.0); | |
| 445 SetState(kBubbleHidden); | |
| 446 } | |
| 447 } | 472 } |
| 448 } | 473 } |
| 449 | 474 |
| 450 void StatusBubbleMac::SetState(StatusBubbleState state) { | 475 void StatusBubbleMac::SetState(StatusBubbleState state) { |
| 451 if (state == state_) | 476 if (state == state_) |
| 452 return; | 477 return; |
| 453 | 478 |
| 454 if (state == kBubbleHidden) { | 479 if (state == kBubbleHidden) { |
| 455 // When hidden (with alpha of 0), make the window have the minimum size, | 480 // When hidden (with alpha of 0), make the window have the minimum size, |
| 456 // while still keeping the same origin. It's important to not set the | 481 // while still keeping the same origin. It's important to not set the |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 499 // If an incomplete transition has left the opacity somewhere between 0 and | 524 // If an incomplete transition has left the opacity somewhere between 0 and |
| 500 // kBubbleOpacity, the fade rate is kept constant by shortening the duration. | 525 // kBubbleOpacity, the fade rate is kept constant by shortening the duration. |
| 501 NSTimeInterval duration = | 526 NSTimeInterval duration = |
| 502 full_duration * | 527 full_duration * |
| 503 fabs(opacity - [[window_ animator] alphaValue]) / kBubbleOpacity; | 528 fabs(opacity - [[window_ animator] alphaValue]) / kBubbleOpacity; |
| 504 | 529 |
| 505 // 0.0 will not cancel an in-progress animation. | 530 // 0.0 will not cancel an in-progress animation. |
| 506 if (duration == 0.0) | 531 if (duration == 0.0) |
| 507 duration = kMinimumTimeInterval; | 532 duration = kMinimumTimeInterval; |
| 508 | 533 |
| 509 // This will cancel an in-progress transition and replace it with this fade. | 534 // Cancel an in-progress transition and replace it with this fade. |
| 510 [NSAnimationContext beginGrouping]; | 535 AnimateWindowAlpha(opacity, duration); |
| 511 // Don't use the GTM additon for the "Steve" slowdown because this can happen | 536 } |
| 512 // async from user actions and the effects could be a surprise. | 537 |
| 513 [[NSAnimationContext currentContext] setDuration:duration]; | 538 void StatusBubbleMac::AnimateWindowAlpha(CGFloat alpha, |
| 514 [[window_ animator] setAlphaValue:opacity]; | 539 NSTimeInterval duration) { |
| 515 [NSAnimationContext endGrouping]; | 540 completion_handler_factory_.InvalidateWeakPtrs(); |
| 541 base::WeakPtr<StatusBubbleMac> weak_ptr( | |
| 542 completion_handler_factory_.GetWeakPtr()); | |
| 543 [window_ | |
| 544 runAnimationGroup:^(NSAnimationContext* context) { | |
| 545 [context setDuration:duration]; | |
| 546 [[window_ animator] setAlphaValue:alpha]; | |
| 547 } | |
| 548 completionHandler:^{ | |
| 549 if (weak_ptr) | |
| 550 weak_ptr->AnimationDidStop(); | |
| 551 }]; | |
| 516 } | 552 } |
| 517 | 553 |
| 518 void StatusBubbleMac::StartTimer(int64 delay_ms) { | 554 void StatusBubbleMac::StartTimer(int64 delay_ms) { |
| 519 DCHECK([NSThread isMainThread]); | 555 DCHECK([NSThread isMainThread]); |
| 520 DCHECK(state_ == kBubbleShowingTimer || state_ == kBubbleHidingTimer); | 556 DCHECK(state_ == kBubbleShowingTimer || state_ == kBubbleHidingTimer); |
| 521 | 557 |
| 522 if (immediate_) { | 558 if (immediate_) { |
| 523 TimerFired(); | 559 TimerFired(); |
| 524 return; | 560 return; |
| 525 } | 561 } |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 744 } | 780 } |
| 745 | 781 |
| 746 // Round the top corners when the bubble is below the parent window. | 782 // Round the top corners when the bubble is below the parent window. |
| 747 if (NSMinY(window_frame) < NSMinY(parent_frame)) { | 783 if (NSMinY(window_frame) < NSMinY(parent_frame)) { |
| 748 corner_flags |= kRoundedTopLeftCorner | kRoundedTopRightCorner; | 784 corner_flags |= kRoundedTopLeftCorner | kRoundedTopRightCorner; |
| 749 } | 785 } |
| 750 } | 786 } |
| 751 | 787 |
| 752 return corner_flags; | 788 return corner_flags; |
| 753 } | 789 } |
| OLD | NEW |