diff options
Diffstat (limited to 'chrome/browser/cocoa/animatable_view.mm')
-rw-r--r-- | chrome/browser/cocoa/animatable_view.mm | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/animatable_view.mm b/chrome/browser/cocoa/animatable_view.mm new file mode 100644 index 0000000..7488a45 --- /dev/null +++ b/chrome/browser/cocoa/animatable_view.mm @@ -0,0 +1,109 @@ +// 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. + +#import <Cocoa/Cocoa.h> +#import <QuartzCore/QuartzCore.h> + +#import "chrome/browser/cocoa/animatable_view.h" +#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" + +// NSAnimation subclass that animates the height of an AnimatableView. Allows +// the caller to start and cancel the animation as desired. +@interface HeightAnimation : NSAnimation { + @private + AnimatableView* view_; // weak, owns us. + CGFloat startHeight_; + CGFloat endHeight_; +} + +// Initialize a new height animation for the given view. The animation will not +// start until startAnimation: is called. +- (id)initWithView:(AnimatableView*)view + finalHeight:(CGFloat)height + duration:(NSTimeInterval)duration; +@end + +@implementation HeightAnimation +- (id)initWithView:(AnimatableView*)view + finalHeight:(CGFloat)height + duration:(NSTimeInterval)duration { + if ((self = [super gtm_initWithDuration:duration + eventMask:NSLeftMouseUpMask + animationCurve:NSAnimationEaseIn])) { + view_ = view; + startHeight_ = [view_ height]; + endHeight_ = height; + [self setAnimationBlockingMode:NSAnimationNonblocking]; + [self setDelegate:view_]; + } + return self; +} + +// Overridden to call setHeight for each progress tick. +- (void)setCurrentProgress:(NSAnimationProgress)progress { + [super setCurrentProgress:progress]; + [view_ setHeight:((progress * (endHeight_ - startHeight_)) + startHeight_)]; +} +@end + + +@implementation AnimatableView +@synthesize delegate = delegate_; +@synthesize resizeDelegate = resizeDelegate_; + +- (void)dealloc { + // Stop the animation if it is running, since it holds a pointer to this view. + [self stopAnimation]; + [super dealloc]; +} + +- (CGFloat)height { + return [self frame].size.height; +} + +- (void)setHeight:(CGFloat)newHeight { + // Force the height to be an integer because some animations look terrible + // with non-integer intermediate heights. We only ever set integer heights + // for our views, so this shouldn't be a limitation in practice. + int height = floor(newHeight); + [resizeDelegate_ resizeView:self newHeight:height]; +} + +- (void)animateToNewHeight:(CGFloat)newHeight + duration:(NSTimeInterval)duration { + [currentAnimation_ stopAnimation]; + + currentAnimation_.reset([[HeightAnimation alloc] initWithView:self + finalHeight:newHeight + duration:duration]); + if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) + [resizeDelegate_ setAnimationInProgress:YES]; + [currentAnimation_ startAnimation]; +} + +- (void)stopAnimation { + [currentAnimation_ stopAnimation]; +} + +- (NSAnimationProgress)currentAnimationProgress { + return [currentAnimation_ currentProgress]; +} + +- (void)animationDidStop:(NSAnimation*)animation { + if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) + [resizeDelegate_ setAnimationInProgress:NO]; + if ([delegate_ respondsToSelector:@selector(animationDidStop:)]) + [delegate_ animationDidStop:animation]; + currentAnimation_.reset(nil); +} + +- (void)animationDidEnd:(NSAnimation*)animation { + if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) + [resizeDelegate_ setAnimationInProgress:NO]; + if ([delegate_ respondsToSelector:@selector(animationDidEnd:)]) + [delegate_ animationDidEnd:animation]; + currentAnimation_.reset(nil); +} + +@end |