diff options
author | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-12 18:50:25 +0000 |
---|---|---|
committer | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-12 18:50:25 +0000 |
commit | 8761dfd26bce81b5e438cbee1f020755cabaef7c (patch) | |
tree | 5529a2115cc26318f0491438a3d7cb079bd1af21 /chrome | |
parent | 94905a11e8d08a8902babc5bbd9cec40b2262ac7 (diff) | |
download | chromium_src-8761dfd26bce81b5e438cbee1f020755cabaef7c.zip chromium_src-8761dfd26bce81b5e438cbee1f020755cabaef7c.tar.gz chromium_src-8761dfd26bce81b5e438cbee1f020755cabaef7c.tar.bz2 |
Moves the animation for the download_started into an overlay window to get
rid of jankyness on 10.6. It really shouldn't have worked right on 10.5 either.
The layer needed to be hosted in a view. Views should not overlap.
The only ways to do this correctly are:
a) Turn on setWantsLayer for the browser view and make a subview to host our
animation.
b) An overlay window
Went with 'b', because 'a' may be a big memory hit, and I'm not sure how
webkit works in a layered environment.
BUG=23500
TEST=see bug
Review URL: http://codereview.chromium.org/385053
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31802 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/cocoa/download_started_animation_mac.mm | 234 |
1 files changed, 132 insertions, 102 deletions
diff --git a/chrome/browser/cocoa/download_started_animation_mac.mm b/chrome/browser/cocoa/download_started_animation_mac.mm index e7cf227..37419c8 100644 --- a/chrome/browser/cocoa/download_started_animation_mac.mm +++ b/chrome/browser/cocoa/download_started_animation_mac.mm @@ -13,7 +13,6 @@ #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" @@ -25,32 +24,19 @@ 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_; - +// Should be instantiated using +startAnimationWithTabContents:. +@interface DownloadStartedAnimationMac : NSWindow { + @private // 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_; + CGFloat imageWidth_; }; ++ (void)startAnimationWithTabContents:(TabContents*)tabContents; // Called by our DownloadAnimationTabObserver if the tab is hidden or closed. - (void)animationComplete; -@end // interface DownloadStartedAnimationMac. - +@end // A helper class to monitor tab hidden and closed notifications. If we receive // such a notification, we stop the animation. @@ -95,111 +81,155 @@ private: 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]; -} + // Load the image of the download arrow. + ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); + SkBitmap* imageBitmap = bundle.GetBitmapNamed(IDR_DOWNLOAD_ANIMATION_BEGIN); + scoped_cftyperef<CGImageRef> image(SkCreateCGImageRef(*imageBitmap)); -// 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 + // Figure out the positioning in the current tab. Try to position the layer // 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. + // bottom of the tab, assuming there is enough room. If there isn't enough, + // don'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); + tabContents->GetContainerBounds(&bounds); + imageWidth_ = CGImageGetWidth(image); + CGFloat 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; + return nil; + } + + NSView* tabContentsView = tabContents->GetNativeView(); + NSWindow* parentWindow = [tabContentsView window]; + if (!parentWindow) { + // The tab is no longer frontmost. + [self release]; + return nil; + } + + NSPoint origin = [tabContentsView frame].origin; + origin = [tabContentsView convertPointToBase:origin]; + origin = [parentWindow convertBaseToScreen:origin]; + + // Create a window to host a layer that animates the sliding and fading. + CGFloat animationHeight = MIN(bounds.height(), 4 * imageHeight); + NSRect frame = NSMakeRect(origin.x, origin.y, imageWidth_, animationHeight); + if ((self = [super initWithContentRect:frame + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO])) { + [self setOpaque:NO]; + [self setBackgroundColor:[NSColor clearColor]]; + [self setIgnoresMouseEvents:YES]; + + // Must be set or else self will be leaked. + [self setReleasedWhenClosed:YES]; + + // Set up to get notified about resize events on the parent window. + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(parentWindowChanged:) + name:NSWindowDidResizeNotification + object:parentWindow]; + [parentWindow addChildWindow:self ordered:NSWindowAbove]; + + // Set up the root layer. By calling -setLayer: followed by -setWantsLayer: + // the view becomes a layer hosting view as opposed to a layer backed view. + NSView* view = [self contentView]; + CALayer* rootLayer = [CALayer layer]; + [view setLayer:rootLayer]; + [view setWantsLayer:YES]; + + // Create the layer that will be animated. + CALayer* layer = [CALayer layer]; + [layer setContents:(id)image.get()]; + [layer setAnchorPoint:CGPointMake(0, 1)]; + [layer setFrame:CGRectMake(0, 0, imageWidth_, imageHeight)]; + [layer setNeedsDisplayOnBoundsChange:YES]; + [rootLayer addSublayer:layer]; + + // Common timing function for all animations. + CAMediaTimingFunction* mediaFunction = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + + // Positional animation. + CABasicAnimation* positionAnimation = + [CABasicAnimation animationWithKeyPath:@"position"]; + CGFloat animationHeight = MIN(bounds.height(), 3 * imageHeight); + NSPoint start = NSMakePoint(0, animationHeight); + NSPoint stop = NSMakePoint(0, imageHeight); + [positionAnimation setFromValue:[NSValue valueWithPoint:start]]; + [positionAnimation setToValue:[NSValue valueWithPoint:stop]]; + [positionAnimation gtm_setDuration:0.6]; + [positionAnimation setTimingFunction:mediaFunction]; + + // Opacity animation. + CABasicAnimation* opacityAnimation = + [CABasicAnimation animationWithKeyPath:@"opacity"]; + [opacityAnimation setFromValue:[NSNumber numberWithFloat:1.0]]; + [opacityAnimation setToValue:[NSNumber numberWithFloat:0.4]]; + [opacityAnimation gtm_setDuration:0.6]; + [opacityAnimation setTimingFunction:mediaFunction]; + + // Group the animations together. + CAAnimationGroup* animationGroup = [CAAnimationGroup animation]; + NSArray* animations = + [NSArray arrayWithObjects:positionAnimation, opacityAnimation, nil]; + [animationGroup setAnimations:animations]; + + // Set self as delegate so self receives -animationDidStop:finished:; + [animationGroup setDelegate:self]; + [animationGroup setTimingFunction:mediaFunction]; + [animationGroup gtm_setDuration:0.6]; + [layer addAnimation:animationGroup forKey:@"downloadOpacityAndPosition"]; + + observer_.reset(new DownloadAnimationTabObserver(self, tabContents)); } + return self; +} - 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 gtm_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 gtm_setDuration:1.5]; // Longer, so it doesn't fade too much. - mediaFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; - [animation setTimingFunction:mediaFunction]; - [layer_ addAnimation:animation forKey:@"downloadOpacity"]; +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +// Called when the parent window is resized. +- (void)parentWindowChanged:(NSNotification*)notification { + NSWindow* parentWindow = [self parentWindow]; + DCHECK([[notification object] isEqual:parentWindow]); + NSRect parentFrame = [parentWindow frame]; + NSRect frame = parentFrame; + frame.size.width = MIN(imageWidth_, NSWidth(parentFrame)); + [self setFrame:frame display:YES]; } // CAAnimation delegate method called when the animation is complete. - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag { [self animationComplete]; - [self release]; } -@end // implementation DownloadStartedAnimationMac +// Common clean up code. +- (void)animationComplete { + [[self parentWindow] removeChildWindow:self]; + [self close]; +} + ++ (void)startAnimationWithTabContents:(TabContents*)contents { + // Will be deleted when the animation is complete in -animationComplete. + [[self alloc] initWithTabContents:contents]; +} +@end -// 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]; + [DownloadStartedAnimationMac startAnimationWithTabContents:tab_contents]; } |