| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 // This file contains the Mac implementation the download animation, displayed | 5 // This file contains the Mac implementation the download animation, displayed |
| 6 // at the start of a download. The animation produces an arrow pointing | 6 // at the start of a download. The animation produces an arrow pointing |
| 7 // downwards and animates towards the bottom of the window where the new | 7 // downwards and animates towards the bottom of the window where the new |
| 8 // download appears in the download shelf. | 8 // download appears in the download shelf. |
| 9 | 9 |
| 10 #include "chrome/browser/download/download_started_animation.h" | 10 #include "chrome/browser/download/download_started_animation.h" |
| 11 | 11 |
| 12 #import <QuartzCore/QuartzCore.h> | 12 #import <QuartzCore/QuartzCore.h> |
| 13 | 13 |
| 14 #include "app/resource_bundle.h" | 14 #include "app/resource_bundle.h" |
| 15 #include "base/scoped_cftyperef.h" | 15 #include "base/scoped_cftyperef.h" |
| 16 #include "chrome/browser/tab_contents/tab_contents.h" | 16 #include "chrome/browser/tab_contents/tab_contents.h" |
| 17 #include "chrome/browser/tab_contents/tab_contents_view_mac.h" | 17 #include "chrome/browser/tab_contents/tab_contents_view_mac.h" |
| 18 #import "chrome/browser/cocoa/animatable_image.h" |
| 18 #include "chrome/common/notification_registrar.h" | 19 #include "chrome/common/notification_registrar.h" |
| 19 #include "chrome/common/notification_service.h" | 20 #include "chrome/common/notification_service.h" |
| 20 #include "grit/theme_resources.h" | 21 #include "grit/theme_resources.h" |
| 21 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | 22 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" |
| 22 #include "third_party/skia/include/utils/mac/SkCGUtils.h" | 23 #include "third_party/skia/include/utils/mac/SkCGUtils.h" |
| 23 | 24 |
| 24 class DownloadAnimationTabObserver; | 25 class DownloadAnimationTabObserver; |
| 25 | 26 |
| 26 // A class for managing the Core Animation download animation. | 27 // A class for managing the Core Animation download animation. |
| 27 // Should be instantiated using +startAnimationWithTabContents:. | 28 // Should be instantiated using +startAnimationWithTabContents:. |
| 28 @interface DownloadStartedAnimationMac : NSWindow { | 29 @interface DownloadStartedAnimationMac : NSObject { |
| 29 @private | 30 @private |
| 30 // The observer for the TabContents we are drawing on. | 31 // The observer for the TabContents we are drawing on. |
| 31 scoped_ptr<DownloadAnimationTabObserver> observer_; | 32 scoped_ptr<DownloadAnimationTabObserver> observer_; |
| 32 CGFloat imageWidth_; | 33 CGFloat imageWidth_; |
| 34 AnimatableImage* animation_; |
| 33 }; | 35 }; |
| 34 | 36 |
| 35 + (void)startAnimationWithTabContents:(TabContents*)tabContents; | 37 + (void)startAnimationWithTabContents:(TabContents*)tabContents; |
| 36 // Called by our DownloadAnimationTabObserver if the tab is hidden or closed. | 38 // Called by our DownloadAnimationTabObserver if the tab is hidden or closed. |
| 37 - (void)animationComplete; | 39 - (void)animationComplete; |
| 38 | 40 |
| 39 @end | 41 @end |
| 40 | 42 |
| 41 // A helper class to monitor tab hidden and closed notifications. If we receive | 43 // A helper class to monitor tab hidden and closed notifications. If we receive |
| 42 // such a notification, we stop the animation. | 44 // such a notification, we stop the animation. |
| 43 class DownloadAnimationTabObserver : public NotificationObserver { | 45 class DownloadAnimationTabObserver : public NotificationObserver { |
| 44 public: | 46 public: |
| 45 DownloadAnimationTabObserver(DownloadStartedAnimationMac* owner, | 47 DownloadAnimationTabObserver(DownloadStartedAnimationMac* owner, |
| 46 TabContents* tab_contents) | 48 TabContents* tab_contents) |
| 47 : owner_(owner), | 49 : owner_(owner), |
| 48 tab_contents_(tab_contents) { | 50 tab_contents_(tab_contents) { |
| 49 registrar_.Add(this, | 51 registrar_.Add(this, |
| 50 NotificationType::TAB_CONTENTS_HIDDEN, | 52 NotificationType::TAB_CONTENTS_HIDDEN, |
| 51 Source<TabContents>(tab_contents_)); | 53 Source<TabContents>(tab_contents_)); |
| 52 registrar_.Add(this, | 54 registrar_.Add(this, |
| 53 NotificationType::TAB_CONTENTS_DESTROYED, | 55 NotificationType::TAB_CONTENTS_DESTROYED, |
| 54 Source<TabContents>(tab_contents_)); | 56 Source<TabContents>(tab_contents_)); |
| 55 } | 57 } |
| 56 | 58 |
| 57 // Runs when a tab is hidden or destroyed. Let our owner know we should end | 59 // Runs when a tab is hidden or destroyed. Let our owner know we should end |
| 58 // the animation. | 60 // the animation. |
| 59 void Observe(NotificationType type, | 61 void Observe(NotificationType type, |
| 60 const NotificationSource& source, | 62 const NotificationSource& source, |
| 61 const NotificationDetails& details) { | 63 const NotificationDetails& details) { |
| 62 registrar_.Remove(this, | 64 // This ends up deleting us. |
| 63 NotificationType::TAB_CONTENTS_HIDDEN, | |
| 64 Source<TabContents>(tab_contents_)); | |
| 65 registrar_.Remove(this, | |
| 66 NotificationType::TAB_CONTENTS_DESTROYED, | |
| 67 Source<TabContents>(tab_contents_)); | |
| 68 [owner_ animationComplete]; | 65 [owner_ animationComplete]; |
| 69 } | 66 } |
| 70 | 67 |
| 71 private: | 68 private: |
| 72 // The object we need to inform when we get a notification. Weak. | 69 // The object we need to inform when we get a notification. Weak. |
| 73 DownloadStartedAnimationMac* owner_; | 70 DownloadStartedAnimationMac* owner_; |
| 74 | 71 |
| 75 // The tab we are observing. Weak. | 72 // The tab we are observing. Weak. |
| 76 TabContents* tab_contents_; | 73 TabContents* tab_contents_; |
| 77 | 74 |
| 78 // Used for registering to receive notifications and automatic clean up. | 75 // Used for registering to receive notifications and automatic clean up. |
| 79 NotificationRegistrar registrar_; | 76 NotificationRegistrar registrar_; |
| 80 | 77 |
| 81 DISALLOW_COPY_AND_ASSIGN(DownloadAnimationTabObserver); | 78 DISALLOW_COPY_AND_ASSIGN(DownloadAnimationTabObserver); |
| 82 }; | 79 }; |
| 83 | 80 |
| 84 @implementation DownloadStartedAnimationMac | 81 @implementation DownloadStartedAnimationMac |
| 85 | 82 |
| 86 - (id)initWithTabContents:(TabContents*)tabContents { | 83 - (id)initWithTabContents:(TabContents*)tabContents { |
| 87 // Load the image of the download arrow. | 84 if ((self = [super init])) { |
| 88 ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); | 85 // Load the image of the download arrow. |
| 89 SkBitmap* imageBitmap = bundle.GetBitmapNamed(IDR_DOWNLOAD_ANIMATION_BEGIN); | 86 ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); |
| 90 scoped_cftyperef<CGImageRef> image(SkCreateCGImageRef(*imageBitmap)); | 87 SkBitmap* imageBitmap = bundle.GetBitmapNamed(IDR_DOWNLOAD_ANIMATION_BEGIN); |
| 88 scoped_cftyperef<CGImageRef> image(SkCreateCGImageRef(*imageBitmap)); |
| 91 | 89 |
| 92 // Figure out the positioning in the current tab. Try to position the layer | 90 // Figure out the positioning in the current tab. Try to position the layer |
| 93 // against the left edge, and three times the download image's height from the | 91 // against the left edge, and three times the download image's height from |
| 94 // bottom of the tab, assuming there is enough room. If there isn't enough, | 92 // the bottom of the tab, assuming there is enough room. If there isn't |
| 95 // don't show the animation and let the shelf speak for itself. | 93 // enough, don't show the animation and let the shelf speak for itself. |
| 96 gfx::Rect bounds; | 94 gfx::Rect bounds; |
| 97 tabContents->GetContainerBounds(&bounds); | 95 tabContents->GetContainerBounds(&bounds); |
| 98 imageWidth_ = CGImageGetWidth(image); | 96 imageWidth_ = CGImageGetWidth(image); |
| 99 CGFloat imageHeight = CGImageGetHeight(image); | 97 CGFloat imageHeight = CGImageGetHeight(image); |
| 100 | 98 |
| 101 // Sanity check the size in case there's no room to display the animation. | 99 // Sanity check the size in case there's no room to display the animation. |
| 102 if (bounds.height() < imageHeight) { | 100 if (bounds.height() < imageHeight) { |
| 103 [self release]; | 101 [self release]; |
| 104 return nil; | 102 return nil; |
| 105 } | 103 } |
| 106 | 104 |
| 107 NSView* tabContentsView = tabContents->GetNativeView(); | 105 NSView* tabContentsView = tabContents->GetNativeView(); |
| 108 NSWindow* parentWindow = [tabContentsView window]; | 106 NSWindow* parentWindow = [tabContentsView window]; |
| 109 if (!parentWindow) { | 107 if (!parentWindow) { |
| 110 // The tab is no longer frontmost. | 108 // The tab is no longer frontmost. |
| 111 [self release]; | 109 [self release]; |
| 112 return nil; | 110 return nil; |
| 113 } | 111 } |
| 114 | 112 |
| 115 NSPoint origin = [tabContentsView frame].origin; | 113 NSPoint origin = [tabContentsView frame].origin; |
| 116 origin = [tabContentsView convertPoint:origin toView:nil]; | 114 origin = [tabContentsView convertPoint:origin toView:nil]; |
| 117 origin = [parentWindow convertBaseToScreen:origin]; | 115 origin = [parentWindow convertBaseToScreen:origin]; |
| 118 | 116 |
| 119 // Create a window to host a layer that animates the sliding and fading. | 117 // Create the animation object to assist in animating and fading. |
| 120 CGFloat animationHeight = MIN(bounds.height(), 4 * imageHeight); | 118 CGFloat animationHeight = MIN(bounds.height(), 4 * imageHeight); |
| 121 NSRect frame = NSMakeRect(origin.x, origin.y, imageWidth_, animationHeight); | 119 NSRect frame = NSMakeRect(origin.x, origin.y, imageWidth_, animationHeight); |
| 122 if ((self = [super initWithContentRect:frame | 120 animation_ = [[AnimatableImage alloc] initWithImage:(id)image.get() |
| 123 styleMask:NSBorderlessWindowMask | 121 animationFrame:frame]; |
| 124 backing:NSBackingStoreBuffered | 122 [parentWindow addChildWindow:animation_ ordered:NSWindowAbove]; |
| 125 defer:NO])) { | |
| 126 [self setOpaque:NO]; | |
| 127 [self setBackgroundColor:[NSColor clearColor]]; | |
| 128 [self setIgnoresMouseEvents:YES]; | |
| 129 | 123 |
| 130 // Must be set or else self will be leaked. | 124 animationHeight = MIN(bounds.height(), 3 * imageHeight); |
| 131 [self setReleasedWhenClosed:YES]; | 125 [animation_ setStartFrame:CGRectMake(0, animationHeight, |
| 126 imageWidth_, imageHeight)]; |
| 127 [animation_ setEndFrame:CGRectMake(0, imageHeight, |
| 128 imageWidth_, imageHeight)]; |
| 129 [animation_ setStartOpacity:1.0]; |
| 130 [animation_ setEndOpacity:0.4]; |
| 131 [animation_ setDuration:0.6]; |
| 132 |
| 133 observer_.reset(new DownloadAnimationTabObserver(self, tabContents)); |
| 132 | 134 |
| 133 // Set up to get notified about resize events on the parent window. | 135 // Set up to get notified about resize events on the parent window. |
| 134 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | 136 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 135 [center addObserver:self | 137 [center addObserver:self |
| 136 selector:@selector(parentWindowChanged:) | 138 selector:@selector(parentWindowChanged:) |
| 137 name:NSWindowDidResizeNotification | 139 name:NSWindowDidResizeNotification |
| 138 object:parentWindow]; | 140 object:parentWindow]; |
| 139 [parentWindow addChildWindow:self ordered:NSWindowAbove]; | 141 // When the animation window closes, it needs to be removed from the |
| 140 | 142 // parent window. |
| 141 // Set up the root layer. By calling -setLayer: followed by -setWantsLayer: | 143 [center addObserver:self |
| 142 // the view becomes a layer hosting view as opposed to a layer backed view. | 144 selector:@selector(windowWillClose:) |
| 143 NSView* view = [self contentView]; | 145 name:NSWindowWillCloseNotification |
| 144 CALayer* rootLayer = [CALayer layer]; | 146 object:animation_]; |
| 145 [view setLayer:rootLayer]; | |
| 146 [view setWantsLayer:YES]; | |
| 147 | |
| 148 // Create the layer that will be animated. | |
| 149 CALayer* layer = [CALayer layer]; | |
| 150 [layer setContents:(id)image.get()]; | |
| 151 [layer setAnchorPoint:CGPointMake(0, 1)]; | |
| 152 [layer setFrame:CGRectMake(0, 0, imageWidth_, imageHeight)]; | |
| 153 [layer setNeedsDisplayOnBoundsChange:YES]; | |
| 154 [rootLayer addSublayer:layer]; | |
| 155 | |
| 156 // Common timing function for all animations. | |
| 157 CAMediaTimingFunction* mediaFunction = | |
| 158 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; | |
| 159 | |
| 160 // Positional animation. | |
| 161 CABasicAnimation* positionAnimation = | |
| 162 [CABasicAnimation animationWithKeyPath:@"position"]; | |
| 163 CGFloat animationHeight = MIN(bounds.height(), 3 * imageHeight); | |
| 164 NSPoint start = NSMakePoint(0, animationHeight); | |
| 165 NSPoint stop = NSMakePoint(0, imageHeight); | |
| 166 [positionAnimation setFromValue:[NSValue valueWithPoint:start]]; | |
| 167 [positionAnimation setToValue:[NSValue valueWithPoint:stop]]; | |
| 168 [positionAnimation gtm_setDuration:0.6 | |
| 169 eventMask:NSLeftMouseUpMask]; | |
| 170 [positionAnimation setTimingFunction:mediaFunction]; | |
| 171 | |
| 172 // Opacity animation. | |
| 173 CABasicAnimation* opacityAnimation = | |
| 174 [CABasicAnimation animationWithKeyPath:@"opacity"]; | |
| 175 [opacityAnimation setFromValue:[NSNumber numberWithFloat:1.0]]; | |
| 176 [opacityAnimation setToValue:[NSNumber numberWithFloat:0.4]]; | |
| 177 [opacityAnimation gtm_setDuration:0.6 | |
| 178 eventMask:NSLeftMouseUpMask]; | |
| 179 [opacityAnimation setTimingFunction:mediaFunction]; | |
| 180 | |
| 181 // Group the animations together. | |
| 182 CAAnimationGroup* animationGroup = [CAAnimationGroup animation]; | |
| 183 NSArray* animations = | |
| 184 [NSArray arrayWithObjects:positionAnimation, opacityAnimation, nil]; | |
| 185 [animationGroup setAnimations:animations]; | |
| 186 | |
| 187 // Set self as delegate so self receives -animationDidStop:finished:; | |
| 188 [animationGroup setDelegate:self]; | |
| 189 [animationGroup setTimingFunction:mediaFunction]; | |
| 190 [animationGroup gtm_setDuration:0.6 | |
| 191 eventMask:NSLeftMouseUpMask]; | |
| 192 [layer addAnimation:animationGroup forKey:@"downloadOpacityAndPosition"]; | |
| 193 | |
| 194 observer_.reset(new DownloadAnimationTabObserver(self, tabContents)); | |
| 195 } | 147 } |
| 196 return self; | 148 return self; |
| 197 } | 149 } |
| 198 | 150 |
| 199 - (void)dealloc { | 151 - (void)dealloc { |
| 200 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 152 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 201 [super dealloc]; | 153 [super dealloc]; |
| 202 } | 154 } |
| 203 | 155 |
| 204 // Called when the parent window is resized. | 156 // Called when the parent window is resized. |
| 205 - (void)parentWindowChanged:(NSNotification*)notification { | 157 - (void)parentWindowChanged:(NSNotification*)notification { |
| 206 NSWindow* parentWindow = [self parentWindow]; | 158 NSWindow* parentWindow = [animation_ parentWindow]; |
| 207 DCHECK([[notification object] isEqual:parentWindow]); | 159 DCHECK([[notification object] isEqual:parentWindow]); |
| 208 NSRect parentFrame = [parentWindow frame]; | 160 NSRect parentFrame = [parentWindow frame]; |
| 209 NSRect frame = parentFrame; | 161 NSRect frame = parentFrame; |
| 210 frame.size.width = MIN(imageWidth_, NSWidth(parentFrame)); | 162 frame.size.width = MIN(imageWidth_, NSWidth(parentFrame)); |
| 211 [self setFrame:frame display:YES]; | 163 [animation_ setFrame:frame display:YES]; |
| 212 } | 164 } |
| 213 | 165 |
| 214 // CAAnimation delegate method called when the animation is complete. | 166 - (void)windowWillClose:(NSNotification*)notification { |
| 215 - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag { | 167 DCHECK([[notification object] isEqual:animation_]); |
| 216 [self animationComplete]; | 168 [self animationComplete]; |
| 217 } | 169 } |
| 218 | 170 |
| 219 // Common clean up code. | |
| 220 - (void)animationComplete { | 171 - (void)animationComplete { |
| 221 [[self parentWindow] removeChildWindow:self]; | 172 [[animation_ parentWindow] removeChildWindow:animation_]; |
| 222 [self close]; | 173 [self release]; |
| 223 } | 174 } |
| 224 | 175 |
| 225 + (void)startAnimationWithTabContents:(TabContents*)contents { | 176 + (void)startAnimationWithTabContents:(TabContents*)contents { |
| 226 // Will be deleted when the animation is complete in -animationComplete. | 177 // Will be deleted when the animation is complete in -animationComplete. |
| 227 [[self alloc] initWithTabContents:contents]; | 178 DownloadStartedAnimationMac* controller = |
| 179 [[self alloc] initWithTabContents:contents]; |
| 180 // The |animation_| releaes itself when done. |
| 181 [controller->animation_ startAnimation]; |
| 228 } | 182 } |
| 229 | 183 |
| 230 @end | 184 @end |
| 231 | 185 |
| 232 void DownloadStartedAnimation::Show(TabContents* tab_contents) { | 186 void DownloadStartedAnimation::Show(TabContents* tab_contents) { |
| 233 DCHECK(tab_contents); | 187 DCHECK(tab_contents); |
| 234 | 188 |
| 235 // Will be deleted when the animation is complete. | 189 // Will be deleted when the animation is complete. |
| 236 [DownloadStartedAnimationMac startAnimationWithTabContents:tab_contents]; | 190 [DownloadStartedAnimationMac startAnimationWithTabContents:tab_contents]; |
| 237 } | 191 } |
| OLD | NEW |