diff options
author | rohitrao@chromium.org <rohitrao@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-03 20:05:02 +0000 |
---|---|---|
committer | rohitrao@chromium.org <rohitrao@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-03 20:05:02 +0000 |
commit | 06c87330d661e508402b6db25bf93ebb0fc25277 (patch) | |
tree | ea2770fc6794254117ba8bcbb3caaa4dd99f4a66 /chrome/browser/cocoa | |
parent | d338629d11ebad49ce3dc1d4715a1c12ffe5d9f9 (diff) | |
download | chromium_src-06c87330d661e508402b6db25bf93ebb0fc25277.zip chromium_src-06c87330d661e508402b6db25bf93ebb0fc25277.tar.gz chromium_src-06c87330d661e508402b6db25bf93ebb0fc25277.tar.bz2 |
[Mac] Adds a new view that will allow us to animate the height property of views. This class will be the base class for any view that needs to be animated.
BUG=None
TEST=No visible impact. New unittests should pass.
Review URL: http://codereview.chromium.org/350002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30852 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r-- | chrome/browser/cocoa/animatable_view.h | 56 | ||||
-rw-r--r-- | chrome/browser/cocoa/animatable_view.mm | 100 | ||||
-rw-r--r-- | chrome/browser/cocoa/animatable_view_unittest.mm | 48 |
3 files changed, 204 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/animatable_view.h b/chrome/browser/cocoa/animatable_view.h new file mode 100644 index 0000000..cf55781 --- /dev/null +++ b/chrome/browser/cocoa/animatable_view.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef CHROME_BROWSER_COCOA_ANIMATABLE_VIEW_H_ +#define CHROME_BROWSER_COCOA_ANIMATABLE_VIEW_H_ + +#import <Cocoa/Cocoa.h> + +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/view_resizer.h" + +// A view that provides an animatable height property. Provides methods to +// animate to a new height, set a new height immediately, or cancel any running +// animations. +// +// AnimatableView sends an |animationDidEnd:| message to its delegate when the +// animation ends normally and an |animationDidStop:| message when the animation +// was canceled (even when canceled as a result of a new animation starting). + +@interface AnimatableView : NSView { + @protected + IBOutlet id delegate_; // weak, used to send animation ended messages. + + @private + scoped_nsobject<NSAnimation> currentAnimation_; + id<ViewResizer> resizeDelegate_; // weak, usually owns us +} + +// Properties for bindings. +@property(assign) id delegate; + +// Gets the current height of the view. If an animation is currently running, +// this will give the current height at the time of the call, not the target +// height at the end of the animation. +- (CGFloat)height; + +// Sets the height of the view immediately. Cancels any running animations. +- (void)setHeight:(CGFloat)newHeight; + +// Starts a new animation to the given |newHeight| for the given |duration|. +// Cancels any running animations. +- (void)animateToNewHeight:(CGFloat)newHeight + duration:(NSTimeInterval)duration; + +// Cancels any running animations, leaving the view at its current +// (mid-animation) height. +- (void)stopAnimation; + +// Sets the delegate that gets notified when this view needs to chanage its +// height. +- (void)setResizeDelegate:(id<ViewResizer>)resizeDelegate; + +@end + +#endif // CHROME_BROWSER_COCOA_ANIMATABLE_VIEW_H_ diff --git a/chrome/browser/cocoa/animatable_view.mm b/chrome/browser/cocoa/animatable_view.mm new file mode 100644 index 0000000..06d039b --- /dev/null +++ b/chrome/browser/cocoa/animatable_view.mm @@ -0,0 +1,100 @@ +// 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" + +// NSAnimation subclass that animates the height of an AnimatableView. Allows +// the caller to start and cancel the animation as desired. +@interface NSHeightAnimation : 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 NSHeightAnimation +- (id)initWithView:(AnimatableView*)view + finalHeight:(CGFloat)height + duration:(NSTimeInterval)duration { + if ((self = [super initWithDuration:duration + 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_; + +- (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([[NSHeightAnimation alloc] initWithView:self + finalHeight:newHeight + duration:duration]); + [currentAnimation_ startAnimation]; +} + +- (void)stopAnimation { + [currentAnimation_ stopAnimation]; +} + +- (void)setResizeDelegate:(id<ViewResizer>)resizeDelegate { + resizeDelegate_ = resizeDelegate; +} + +- (void)animationDidStop:(NSAnimation*)animation { + if ([delegate_ respondsToSelector:@selector(animationDidStop:)]) + [delegate_ animationDidStop:animation]; + currentAnimation_.reset(nil); +} + +- (void)animationDidEnd:(NSAnimation*)animation { + if ([delegate_ respondsToSelector:@selector(animationDidEnd:)]) + [delegate_ animationDidEnd:animation]; + currentAnimation_.reset(nil); +} + +@end diff --git a/chrome/browser/cocoa/animatable_view_unittest.mm b/chrome/browser/cocoa/animatable_view_unittest.mm new file mode 100644 index 0000000..35a7e8f --- /dev/null +++ b/chrome/browser/cocoa/animatable_view_unittest.mm @@ -0,0 +1,48 @@ +// 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> + +#include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/animatable_view.h" +#import "chrome/browser/cocoa/cocoa_test_helper.h" +#import "chrome/browser/cocoa/view_resizer_pong.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +class AnimatableViewTest : public CocoaTest { + public: + AnimatableViewTest() { + NSRect frame = NSMakeRect(0, 0, 100, 100); + view_.reset([[AnimatableView alloc] initWithFrame:frame]); + [[test_window() contentView] addSubview:view_.get()]; + + resizeDelegate_.reset([[ViewResizerPong alloc] init]); + [view_ setResizeDelegate:resizeDelegate_.get()]; + } + + scoped_nsobject<ViewResizerPong> resizeDelegate_; + scoped_nsobject<AnimatableView> view_; +}; + +// Basic view tests (AddRemove, Display). +TEST_VIEW(AnimatableViewTest, view_); + +TEST_F(AnimatableViewTest, GetAndSetHeight) { + // Make sure the view's height starts out at 100. + NSRect initialFrame = [view_ frame]; + ASSERT_EQ(100, initialFrame.size.height); + EXPECT_EQ(initialFrame.size.height, [view_ height]); + + // Set it directly to 50 and make sure it takes effect. + [resizeDelegate_ setHeight:-1]; + [view_ setHeight:50]; + EXPECT_EQ(50, [resizeDelegate_ height]); +} + +// TODO(rohitrao): Find a way to unittest the animations and delegate messages. + +} // namespace |