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 3// found in the LICENSE file. 4 5#import <Cocoa/Cocoa.h> 6#import <QuartzCore/QuartzCore.h> 7 8#import "chrome/browser/ui/cocoa/animatable_view.h" 9#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" 10 11// NSAnimation subclass that animates the height of an AnimatableView. Allows 12// the caller to start and cancel the animation as desired. 13@interface HeightAnimation : NSAnimation { 14 @private 15 AnimatableView* view_; // weak, owns us. 16 CGFloat startHeight_; 17 CGFloat endHeight_; 18} 19 20// Initialize a new height animation for the given view. The animation will not 21// start until startAnimation: is called. 22- (id)initWithView:(AnimatableView*)view 23 finalHeight:(CGFloat)height 24 duration:(NSTimeInterval)duration; 25@end 26 27@implementation HeightAnimation 28- (id)initWithView:(AnimatableView*)view 29 finalHeight:(CGFloat)height 30 duration:(NSTimeInterval)duration { 31 if ((self = [super gtm_initWithDuration:duration 32 eventMask:NSLeftMouseUpMask 33 animationCurve:NSAnimationEaseIn])) { 34 view_ = view; 35 startHeight_ = [view_ height]; 36 endHeight_ = height; 37 [self setAnimationBlockingMode:NSAnimationNonblocking]; 38 [self setDelegate:view_]; 39 } 40 return self; 41} 42 43// Overridden to call setHeight for each progress tick. 44- (void)setCurrentProgress:(NSAnimationProgress)progress { 45 [super setCurrentProgress:progress]; 46 [view_ setHeight:((progress * (endHeight_ - startHeight_)) + startHeight_)]; 47} 48@end 49 50 51@implementation AnimatableView 52@synthesize delegate = delegate_; 53@synthesize resizeDelegate = resizeDelegate_; 54 55- (void)dealloc { 56 // Stop the animation if it is running, since it holds a pointer to this view. 57 [self stopAnimation]; 58 [super dealloc]; 59} 60 61- (CGFloat)height { 62 return [self frame].size.height; 63} 64 65- (void)setHeight:(CGFloat)newHeight { 66 // Force the height to be an integer because some animations look terrible 67 // with non-integer intermediate heights. We only ever set integer heights 68 // for our views, so this shouldn't be a limitation in practice. 69 int height = floor(newHeight); 70 [resizeDelegate_ resizeView:self newHeight:height]; 71} 72 73- (void)animateToNewHeight:(CGFloat)newHeight 74 duration:(NSTimeInterval)duration { 75 [currentAnimation_ stopAnimation]; 76 77 currentAnimation_.reset([[HeightAnimation alloc] initWithView:self 78 finalHeight:newHeight 79 duration:duration]); 80 if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) 81 [resizeDelegate_ setAnimationInProgress:YES]; 82 [currentAnimation_ startAnimation]; 83} 84 85- (void)stopAnimation { 86 [currentAnimation_ stopAnimation]; 87} 88 89- (NSAnimationProgress)currentAnimationProgress { 90 return [currentAnimation_ currentProgress]; 91} 92 93- (void)animationDidStop:(NSAnimation*)animation { 94 if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) 95 [resizeDelegate_ setAnimationInProgress:NO]; 96 if ([delegate_ respondsToSelector:@selector(animationDidStop:)]) 97 [delegate_ animationDidStop:animation]; 98 currentAnimation_.reset(nil); 99} 100 101- (void)animationDidEnd:(NSAnimation*)animation { 102 if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) 103 [resizeDelegate_ setAnimationInProgress:NO]; 104 if ([delegate_ respondsToSelector:@selector(animationDidEnd:)]) 105 [delegate_ animationDidEnd:animation]; 106 currentAnimation_.reset(nil); 107} 108 109@end 110