1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file contains the Mac implementation the download animation, displayed
// at the start of a download. The animation produces an arrow pointing
// downwards and animates towards the bottom of the window where the new
// download appears in the download shelf.
#include "chrome/browser/download/download_started_animation.h"
#import <QuartzCore/QuartzCore.h>
#include "app/resource_bundle.h"
#include "base/scoped_cftyperef.h"
#include "base/scoped_nsobject.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view_mac.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "grit/theme_resources.h"
#include "third_party/skia/include/utils/mac/SkCGUtils.h"
class DownloadAnimationTabObserver;
// A class for managing the Core Animation download animation.
@interface DownloadStartedAnimationMac : NSObject {
// The download arrow image which we are responsible for freeing.
scoped_cftyperef<CGImageRef> image_;
// The TabContents we will animate in (weak).
TabContents* tabContents_;
// The cocoa view object for our TabContents (weak).
NSView* view_;
// The observer for the TabContents we are drawing on.
scoped_ptr<DownloadAnimationTabObserver> observer_;
// Our animation layer.
scoped_nsobject<CALayer> layer_;
// Set once the animation is complete, either by interrupting (via window
// close) or through normal a end of the animation.
BOOL isComplete_;
};
// Called by our DownloadAnimationTabObserver if the tab is hidden or closed.
- (void)animationComplete;
@end // interface DownloadStartedAnimationMac.
// A helper class to monitor tab hidden and closed notifications. If we receive
// such a notification, we stop the animation.
class DownloadAnimationTabObserver : public NotificationObserver {
public:
DownloadAnimationTabObserver(DownloadStartedAnimationMac* owner,
TabContents* tab_contents)
: owner_(owner),
tab_contents_(tab_contents) {
registrar_.Add(this,
NotificationType::TAB_CONTENTS_HIDDEN,
Source<TabContents>(tab_contents_));
registrar_.Add(this,
NotificationType::TAB_CONTENTS_DESTROYED,
Source<TabContents>(tab_contents_));
}
// Runs when a tab is hidden or destroyed. Let our owner know we should end
// the animation.
void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
registrar_.Remove(this,
NotificationType::TAB_CONTENTS_HIDDEN,
Source<TabContents>(tab_contents_));
registrar_.Remove(this,
NotificationType::TAB_CONTENTS_DESTROYED,
Source<TabContents>(tab_contents_));
[owner_ animationComplete];
}
private:
// The object we need to inform when we get a notification. Weak.
DownloadStartedAnimationMac* owner_;
// The tab we are observing. Weak.
TabContents* tab_contents_;
// Used for registering to receive notifications and automatic clean up.
NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(DownloadAnimationTabObserver);
};
@implementation DownloadStartedAnimationMac
// Load the image of the download arrow.
- (id)initWithTabContents:(TabContents*)tabContents {
if ((self = [super init])) {
SkBitmap* image_bitmap =
ResourceBundle::GetSharedInstance().GetBitmapNamed(
IDR_DOWNLOAD_ANIMATION_BEGIN);
image_.reset(SkCreateCGImageRef(*image_bitmap));
tabContents_ = tabContents;
view_ = tabContents_->GetContentNativeView();
observer_.reset(new DownloadAnimationTabObserver(self, tabContents));
isComplete_ = NO;
}
return self;
}
// Common clean up code.
- (void)animationComplete {
if (isComplete_)
return;
isComplete_ = YES;
[view_ setWantsLayer:NO];
[layer_ removeAllAnimations];
[layer_ removeFromSuperlayer];
}
// Set up the animation and let Core Animation do all the hard work.
- (void)animate {
// Figure out the positioning in the current tab. We try to position ourselves
// against the left edge, and three times the download image's height from the
// bottom of the tab, assuming there is enough room. If there isn't enough, we
// won't show the animation and let the shelf speak for itself.
gfx::Rect bounds;
tabContents_->GetContainerBounds(&bounds);
int imageWidth = CGImageGetWidth(image_);
int imageHeight = CGImageGetHeight(image_);
CGRect imageBounds = CGRectMake(0, 0, imageWidth, imageHeight);
// Sanity check the size in case there's no room to display the animation.
if (bounds.height() < imageHeight) {
[self release];
return;
}
int animationHeight = std::min(bounds.height(), 3 * imageHeight);
NSPoint start = NSMakePoint(0, animationHeight);
NSPoint stop = NSMakePoint(0, 0); // Bottom of the tab.
// Set for the duration of the animation, or we won't see our layer. We reset
// this in the completion callback. Layers by default scale their content to
// fit, but we need them to clip their content instead, or else renderer
// resizes will look janky.
[view_ setWantsLayer:YES];
[[view_ layer] setContentsGravity:kCAGravityTopLeft];
// CALayer initalization.
layer_.reset([[CALayer layer] retain]);
[layer_ setNeedsDisplay];
[layer_ setContents:(id)image_.get()];
[layer_ setAnchorPoint:CGPointMake(0, 0)];
[layer_ setFrame:imageBounds];
[[view_ layer] addSublayer:layer_];
// Positional animation.
CABasicAnimation *animation =
[CABasicAnimation animationWithKeyPath:@"position"];
[animation setFromValue:[NSValue valueWithPoint:start]];
[animation setToValue:[NSValue valueWithPoint:stop]];
[animation setDuration:0.6];
CAMediaTimingFunction* mediaFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[animation setTimingFunction:mediaFunction];
[animation setDelegate:self];
[layer_ addAnimation:animation forKey:@"downloadPosition"];
// Opacity animation.
animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[animation setFromValue:[NSNumber numberWithFloat:1.0]];
[animation setToValue:[NSNumber numberWithFloat:0.0]];
[animation setDuration:1.5]; // Slightly longer, so it doesn't fade too much.
mediaFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[animation setTimingFunction:mediaFunction];
[layer_ addAnimation:animation forKey:@"downloadOpacity"];
}
// CAAnimation delegate method called when the animation is complete.
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag {
[self animationComplete];
[self release];
}
@end // implementation DownloadStartedAnimationMac
// static
void DownloadStartedAnimation::Show(TabContents* tab_contents) {
DCHECK(tab_contents);
// Will be deleted when the animation is complete.
DownloadStartedAnimationMac* downloadArrow =
[[DownloadStartedAnimationMac alloc] initWithTabContents:tab_contents];
// Go!
[downloadArrow animate];
}
|