BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / animatable_image.mm
blob4af6be8ef3c92756c854456fd5cb357d73d4bcee
1 // Copyright (c) 2010 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.
5 #import "chrome/browser/ui/cocoa/animatable_image.h"
7 #include "base/logging.h"
8 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
10 @implementation AnimatableImage
12 @synthesize startFrame = startFrame_;
13 @synthesize endFrame = endFrame_;
14 @synthesize startOpacity = startOpacity_;
15 @synthesize endOpacity = endOpacity_;
16 @synthesize duration = duration_;
18 - (id)initWithImage:(NSImage*)image
19      animationFrame:(NSRect)animationFrame {
20   if ((self = [super initWithContentRect:animationFrame
21                                styleMask:NSBorderlessWindowMask
22                                  backing:NSBackingStoreBuffered
23                                    defer:NO])) {
24     DCHECK(image);
25     image_.reset([image retain]);
26     duration_ = 1.0;
27     startOpacity_ = 1.0;
28     endOpacity_ = 1.0;
30     [self setOpaque:NO];
31     [self setBackgroundColor:[NSColor clearColor]];
32     [self setIgnoresMouseEvents:YES];
34     // Must be set or else self will be leaked.
35     [self setReleasedWhenClosed:YES];
36   }
37   return self;
40 - (void)startAnimation {
41   // Set up the root layer. By calling -setLayer: followed by -setWantsLayer:
42   // the view becomes a layer hosting view as opposed to a layer backed view.
43   NSView* view = [self contentView];
44   CALayer* rootLayer = [CALayer layer];
45   [view setLayer:rootLayer];
46   [view setWantsLayer:YES];
48   // Create the layer that will be animated.
49   CALayer* layer = [CALayer layer];
50   [layer setContents:image_.get()];
51   [layer setAnchorPoint:CGPointMake(0, 1)];
52   [layer setFrame:[self startFrame]];
53   [layer setNeedsDisplayOnBoundsChange:YES];
54   [rootLayer addSublayer:layer];
56   // Common timing function for all animations.
57   CAMediaTimingFunction* mediaFunction =
58       [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
60   // Animate the bounds only if the image is resized.
61   CABasicAnimation* boundsAnimation = nil;
62   if (CGRectGetWidth([self startFrame]) != CGRectGetWidth([self endFrame]) ||
63       CGRectGetHeight([self startFrame]) != CGRectGetHeight([self endFrame])) {
64     boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
65     NSRect startRect = NSMakeRect(0, 0,
66                                   CGRectGetWidth([self startFrame]),
67                                   CGRectGetHeight([self startFrame]));
68     [boundsAnimation setFromValue:[NSValue valueWithRect:startRect]];
69     NSRect endRect = NSMakeRect(0, 0,
70                                 CGRectGetWidth([self endFrame]),
71                                 CGRectGetHeight([self endFrame]));
72     [boundsAnimation setToValue:[NSValue valueWithRect:endRect]];
73     [boundsAnimation gtm_setDuration:[self duration]
74                            eventMask:NSLeftMouseUpMask];
75     [boundsAnimation setTimingFunction:mediaFunction];
76   }
78   // Positional animation.
79   CABasicAnimation* positionAnimation =
80       [CABasicAnimation animationWithKeyPath:@"position"];
81   [positionAnimation setFromValue:
82       [NSValue valueWithPoint:NSPointFromCGPoint([self startFrame].origin)]];
83   [positionAnimation setToValue:
84       [NSValue valueWithPoint:NSPointFromCGPoint([self endFrame].origin)]];
85   [positionAnimation gtm_setDuration:[self duration]
86                            eventMask:NSLeftMouseUpMask];
87   [positionAnimation setTimingFunction:mediaFunction];
89   // Opacity animation.
90   CABasicAnimation* opacityAnimation =
91       [CABasicAnimation animationWithKeyPath:@"opacity"];
92   [opacityAnimation setFromValue:
93       [NSNumber numberWithFloat:[self startOpacity]]];
94   [opacityAnimation setToValue:[NSNumber numberWithFloat:[self endOpacity]]];
95   [opacityAnimation gtm_setDuration:[self duration]
96                           eventMask:NSLeftMouseUpMask];
97   [opacityAnimation setTimingFunction:mediaFunction];
98   // Set the delegate just for one of the animations so that this window can
99   // be closed upon completion.
100   [opacityAnimation setDelegate:self];
102   // The CAAnimations only affect the presentational value of a layer, not the
103   // model value. This means that after the animation is done, it can flicker
104   // back to the original values. To avoid this, create an implicit animation of
105   // the values, which are then overridden with the CABasicAnimations.
106   //
107   // Ideally, a call to |-setBounds:| should be here, but, for reasons that
108   // are not understood, doing so causes the animation to break.
109   [layer setPosition:[self endFrame].origin];
110   [layer setOpacity:[self endOpacity]];
112   // Start the animations.
113   [CATransaction begin];
114   [CATransaction setValue:[NSNumber numberWithFloat:[self duration]]
115                    forKey:kCATransactionAnimationDuration];
116   if (boundsAnimation) {
117     [layer addAnimation:boundsAnimation forKey:@"bounds"];
118   }
119   [layer addAnimation:positionAnimation forKey:@"position"];
120   [layer addAnimation:opacityAnimation forKey:@"opacity"];
121   [CATransaction commit];
124 // CAAnimation delegate method called when the animation is complete.
125 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)flag {
126   // Close the window, releasing self.
127   [self close];
130 @end