Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: chrome/browser/cocoa/download_started_animation_mac.mm

Issue 3014005: [Mac] Display a quick animation when a popup is blocked so the user notices it in the Omnibox. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: CGImage Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698