Home | History | Annotate | Download | only in cocoa
      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/google_toolbox_for_mac/src/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