Index: chrome/browser/ui/cocoa/status_bubble_mac.mm |
diff --git a/chrome/browser/ui/cocoa/status_bubble_mac.mm b/chrome/browser/ui/cocoa/status_bubble_mac.mm |
index c772f4818d909eebbf3d382e9dd1fef493a75c20..8fb8715c7cd6815b95b47f32a484a23985e580bf 100644 |
--- a/chrome/browser/ui/cocoa/status_bubble_mac.mm |
+++ b/chrome/browser/ui/cocoa/status_bubble_mac.mm |
@@ -9,6 +9,7 @@ |
#include "base/bind.h" |
#include "base/compiler_specific.h" |
#include "base/mac/mac_util.h" |
+#include "base/mac/scoped_block.h" |
#include "base/message_loop/message_loop.h" |
#include "base/strings/string_util.h" |
#include "base/strings/sys_string_conversions.h" |
@@ -38,9 +39,6 @@ const int kMousePadding = 20; |
const int kTextPadding = 3; |
-// The animation key used for fade-in and fade-out transitions. |
-NSString* const kFadeAnimationKey = @"alphaValue"; |
- |
// The status bubble's maximum opacity, when fully faded in. |
const CGFloat kBubbleOpacity = 1.0; |
@@ -63,17 +61,20 @@ const CGFloat kExpansionDurationSeconds = 0.125; |
} // namespace |
+#if !defined(MAC_OS_X_VERSION_10_7) || \ |
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 |
+@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.
|
++ (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.
|
+ completionHandler:(void (^)(void))completionHandler; |
+@end |
+#endif // MAC_OS_X_VERSION_10_7 |
+ |
@interface StatusBubbleAnimationDelegate : NSObject { |
@private |
- StatusBubbleMac* statusBubble_; // weak; owns us indirectly |
+ base::mac::ScopedBlock<void (^)(void)> completionHandler_; |
} |
-- (id)initWithStatusBubble:(StatusBubbleMac*)statusBubble; |
- |
-// Invalidates this object so that no further calls will be made to |
-// statusBubble_. This should be called when statusBubble_ is released, to |
-// prevent attempts to call into the released object. |
-- (void)invalidate; |
+- (id)initWithCompletionHandler:(void (^)(void))completionHandler; |
// CAAnimation delegate method |
- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished; |
@@ -81,21 +82,67 @@ const CGFloat kExpansionDurationSeconds = 0.125; |
@implementation StatusBubbleAnimationDelegate |
-- (id)initWithStatusBubble:(StatusBubbleMac*)statusBubble { |
+- (id)initWithCompletionHandler:(void (^)(void))completionHandler { |
if ((self = [super init])) { |
- statusBubble_ = statusBubble; |
+ completionHandler_.reset(completionHandler, base::scoped_policy::RETAIN); |
} |
return self; |
} |
-- (void)invalidate { |
- statusBubble_ = NULL; |
+- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished { |
+ completionHandler_.get()(); |
} |
-- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished { |
- if (statusBubble_) |
- statusBubble_->AnimationDidStop(animation, finished); |
+@end |
+ |
+@interface StatusBubbleWindow : NSWindow { |
+ @private |
+ void (^completionHandler_)(void); |
+} |
+ |
+- (id)animationForKey:(NSString *)key; |
+- (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes |
+ completionHandler:(void (^)(void))completionHandler; |
+@end |
+ |
+@implementation StatusBubbleWindow |
+ |
+- (id)animationForKey:(NSString *)key { |
+ CAAnimation* animation = [super animationForKey:key]; |
+ // If completionHandler_ isn't nil, then this is the first of (potentially) |
+ // multiple animations in a grouping; give it the completion handler. If |
+ // completionHandler_ is nil, then some other animation was tagged with the |
+ // completion handler. |
+ 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:
|
+ StatusBubbleAnimationDelegate* animation_delegate = |
+ [[StatusBubbleAnimationDelegate alloc] |
+ initWithCompletionHandler:completionHandler_]; |
+ [animation setDelegate:animation_delegate]; |
+ completionHandler_ = nil; |
Mark Mentovai
2014/02/14 20:34:04
Crafty.
Avi (use Gerrit)
2014/02/14 20:44:33
Thank you.
|
+ } |
+ return animation; |
+} |
+ |
+- (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes |
+ completionHandler:(void (^)(void))completionHandler { |
+ if ([NSAnimationContext respondsToSelector: |
+ @selector(runAnimationGroup:completionHandler:)]) { |
+ [NSAnimationContext runAnimationGroup:changes |
+ completionHandler:completionHandler]; |
+ } else { |
+ // Mac OS 10.6 does not have completion handler callbacks at the Cocoa |
+ // level, only at the CoreAnimation level. So intercept calls made to |
+ // -animationForKey: and tag one of the animations with a delegate that will |
+ // execute the completion handler. |
+ completionHandler_ = completionHandler; |
+ [NSAnimationContext beginGrouping]; |
+ changes([NSAnimationContext currentContext]); |
+ // At this point, -animationForKey should have been called by CoreAnimation |
+ // to set up the animation to run. Verify this. |
+ DCHECK(completionHandler_ == nil); |
+ [NSAnimationContext endGrouping]; |
+ } |
} |
@end |
@@ -103,6 +150,7 @@ const CGFloat kExpansionDurationSeconds = 0.125; |
StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) |
: timer_factory_(this), |
expand_timer_factory_(this), |
+ completion_handler_factory_(this), |
parent_(parent), |
delegate_(delegate), |
window_(nil), |
@@ -120,7 +168,7 @@ StatusBubbleMac::~StatusBubbleMac() { |
Hide(); |
- [[[window_ animationForKey:kFadeAnimationKey] delegate] invalidate]; |
+ completion_handler_factory_.InvalidateWeakPtrs(); |
Detach(); |
[window_ release]; |
window_ = nil; |
@@ -237,10 +285,7 @@ void StatusBubbleMac::Hide() { |
// An animation is in progress. Cancel it by starting a new animation. |
// Use kMinimumTimeInterval to set the opacity as rapidly as possible. |
fade_out = true; |
- [NSAnimationContext beginGrouping]; |
- [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; |
- [[window_ animator] setAlphaValue:0.0]; |
- [NSAnimationContext endGrouping]; |
+ AnimateWindowAlpha(0.0, kMinimumTimeInterval); |
} |
} |
@@ -364,10 +409,11 @@ void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { |
void StatusBubbleMac::Create() { |
DCHECK(!window_); |
- window_ = [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater |
- styleMask:NSBorderlessWindowMask |
- backing:NSBackingStoreBuffered |
- defer:YES]; |
+ window_ = [[StatusBubbleWindow alloc] |
+ initWithContentRect:ui::kWindowSizeDeterminedLater |
+ styleMask:NSBorderlessWindowMask |
+ backing:NSBackingStoreBuffered |
+ defer:YES]; |
[window_ setMovableByWindowBackground:NO]; |
[window_ setBackgroundColor:[NSColor clearColor]]; |
[window_ setLevel:NSNormalWindowLevel]; |
@@ -388,21 +434,6 @@ void StatusBubbleMac::Create() { |
[window_ accessibilitySetOverrideValue:NSAccessibilityUnknownRole |
forAttribute:NSAccessibilityRoleAttribute]; |
- // Set a delegate for the fade-in and fade-out transitions to be notified |
- // when fades are complete. The ownership model is for window_ to own |
- // animation_dictionary, which owns animation, which owns |
- // animation_delegate. |
- CAAnimation* animation = [[window_ animationForKey:kFadeAnimationKey] copy]; |
- [animation autorelease]; |
- StatusBubbleAnimationDelegate* animation_delegate = |
- [[StatusBubbleAnimationDelegate alloc] initWithStatusBubble:this]; |
- [animation_delegate autorelease]; |
- [animation setDelegate:animation_delegate]; |
- NSMutableDictionary* animation_dictionary = |
- [NSMutableDictionary dictionaryWithDictionary:[window_ animations]]; |
- [animation_dictionary setObject:animation forKey:kFadeAnimationKey]; |
- [window_ setAnimations:animation_dictionary]; |
- |
[view setCornerFlags:kRoundedTopRightCorner]; |
MouseMoved(gfx::Point(), false); |
} |
@@ -427,23 +458,17 @@ void StatusBubbleMac::Detach() { |
[[window_ contentView] setThemeProvider:nil]; |
} |
-void StatusBubbleMac::AnimationDidStop(CAAnimation* animation, bool finished) { |
+void StatusBubbleMac::AnimationDidStop() { |
DCHECK([NSThread isMainThread]); |
DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); |
DCHECK(is_attached()); |
- if (finished) { |
- // Because of the mechanism used to interrupt animations, this is never |
- // actually called with finished set to false. If animations ever become |
- // directly interruptible, the check will ensure that state_ remains |
- // properly synchronized. |
- if (state_ == kBubbleShowingFadeIn) { |
- DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); |
- SetState(kBubbleShown); |
- } else { |
- DCHECK_EQ([[window_ animator] alphaValue], 0.0); |
- SetState(kBubbleHidden); |
- } |
+ if (state_ == kBubbleShowingFadeIn) { |
+ DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); |
+ SetState(kBubbleShown); |
+ } else { |
+ DCHECK_EQ([[window_ animator] alphaValue], 0.0); |
+ SetState(kBubbleHidden); |
} |
} |
@@ -506,13 +531,24 @@ void StatusBubbleMac::Fade(bool show) { |
if (duration == 0.0) |
duration = kMinimumTimeInterval; |
- // This will cancel an in-progress transition and replace it with this fade. |
- [NSAnimationContext beginGrouping]; |
- // Don't use the GTM additon for the "Steve" slowdown because this can happen |
- // async from user actions and the effects could be a surprise. |
- [[NSAnimationContext currentContext] setDuration:duration]; |
- [[window_ animator] setAlphaValue:opacity]; |
- [NSAnimationContext endGrouping]; |
+ // Cancel an in-progress transition and replace it with this fade. |
+ AnimateWindowAlpha(opacity, duration); |
+} |
+ |
+void StatusBubbleMac::AnimateWindowAlpha(CGFloat alpha, |
+ NSTimeInterval duration) { |
+ completion_handler_factory_.InvalidateWeakPtrs(); |
+ base::WeakPtr<StatusBubbleMac> weak_ptr( |
+ completion_handler_factory_.GetWeakPtr()); |
+ [window_ |
+ runAnimationGroup:^(NSAnimationContext* context) { |
+ [context setDuration:duration]; |
+ [[window_ animator] setAlphaValue:alpha]; |
+ } |
+ completionHandler:^{ |
+ if (weak_ptr) |
+ weak_ptr->AnimationDidStop(); |
+ }]; |
} |
void StatusBubbleMac::StartTimer(int64 delay_ms) { |