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 |