Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / constrained_window / constrained_window_animation.mm
blob7277d14ab90e11a33d77c62d4fd8e1d7de2cab30
1 // Copyright (c) 2012 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/constrained_window/constrained_window_animation.h"
7 #include "base/files/file_path.h"
8 #include "base/location.h"
9 #import "base/mac/foundation_util.h"
10 #include "base/native_library.h"
11 #include "ui/gfx/animation/tween.h"
13 // The window animations in this file use private APIs as described here:
14 // http://code.google.com/p/undocumented-goodness/source/browse/trunk/CoreGraphics/CGSPrivate.h
15 // There are two important things to keep in mind when modifying this file:
16 // - For most operations the origin of the coordinate system is top left.
17 // - Perspective and shear transformations get clipped if they are bigger
18 //   than the window size. This does not seem to apply to scale transformations.
20 // Length of the animation in seconds.
21 const NSTimeInterval kAnimationDuration = 0.18;
23 // The number of pixels above the final destination to animate from.
24 const CGFloat kShowHideVerticalOffset = 20;
26 // Scale the window by this factor when animating.
27 const CGFloat kShowHideScaleFactor = 0.99;
29 // Size of the perspective effect as a factor of the window width.
30 const CGFloat kShowHidePerspectiveFactor = 0.04;
32 // Forward declare private CoreGraphics APIs used to transform windows.
33 extern "C" {
35 typedef float float32;
37 typedef int32 CGSWindow;
38 typedef int32 CGSConnection;
40 typedef struct {
41   float32 x;
42   float32 y;
43 } MeshPoint;
45 typedef struct {
46   MeshPoint local;
47   MeshPoint global;
48 } CGPointWarp;
50 CGSConnection _CGSDefaultConnection();
51 CGError CGSSetWindowTransform(const CGSConnection cid,
52                               const CGSWindow wid,
53                               CGAffineTransform transform);
54 CGError CGSSetWindowWarp(const CGSConnection cid,
55                          const CGSWindow wid,
56                          int32 w,
57                          int32 h,
58                          CGPointWarp* mesh);
59 CGError CGSSetWindowAlpha(const CGSConnection cid,
60                           const CGSWindow wid,
61                           float32 alpha);
63 }  // extern "C"
65 namespace {
67 struct KeyFrame {
68   float value;
69   float scale;
72 // Get the window location relative to the top left of the main screen.
73 // Most Cocoa APIs use a coordinate system where the screen origin is the
74 // bottom left. The various CGSSetWindow* APIs use a coordinate system where
75 // the screen origin is the top left.
76 NSPoint GetCGSWindowScreenOrigin(NSWindow* window) {
77   NSArray *screens = [NSScreen screens];
78   if ([screens count] == 0)
79     return NSZeroPoint;
80   // Origin is relative to the screen with the menu bar (the screen at index 0).
81   // Note, this is not the same as mainScreen which is the screen with the key
82   // window.
83   NSScreen* main_screen = [screens objectAtIndex:0];
85   NSRect window_frame = [window frame];
86   NSRect screen_frame = [main_screen frame];
87   return NSMakePoint(NSMinX(window_frame),
88                      NSHeight(screen_frame) - NSMaxY(window_frame));
91 // Set the transparency of the window.
92 void SetWindowAlpha(NSWindow* window, float alpha) {
93   CGSConnection cid = _CGSDefaultConnection();
94   CGSSetWindowAlpha(cid, [window windowNumber], alpha);
97 // Scales the window and translates it so that it stays centered relative
98 // to its original position.
99 void SetWindowScale(NSWindow* window, float scale) {
100   CGFloat scale_delta = 1.0 - scale;
101   CGFloat cur_scale = 1.0 + scale_delta;
102   CGAffineTransform transform =
103       CGAffineTransformMakeScale(cur_scale, cur_scale);
105   // Translate the window to keep it centered at the original location.
106   NSSize window_size = [window frame].size;
107   CGFloat scale_offset_x = window_size.width * (1 - cur_scale) / 2.0;
108   CGFloat scale_offset_y = window_size.height * (1 - cur_scale) / 2.0;
110   NSPoint origin = GetCGSWindowScreenOrigin(window);
111   CGFloat new_x = -origin.x + scale_offset_x;
112   CGFloat new_y = -origin.y + scale_offset_y;
113   transform = CGAffineTransformTranslate(transform, new_x, new_y);
115   CGSConnection cid = _CGSDefaultConnection();
116   CGSSetWindowTransform(cid, [window windowNumber], transform);
119 // Unsets any window warp that may have been previously applied.
120 // Window warp prevents other effects such as CGSSetWindowTransform from
121 // being applied.
122 void ClearWindowWarp(NSWindow* window) {
123   CGSConnection cid = _CGSDefaultConnection();
124   CGSSetWindowWarp(cid, [window windowNumber], 0, 0, NULL);
127 // Applies various transformations using a warp effect. The window is
128 // translated vertically by |y_offset|. The window is scaled by |scale| and
129 // translated so that the it remains centered relative to its original position.
130 // Finally, perspective is effect is applied by shrinking the top of the window.
131 void SetWindowWarp(NSWindow* window,
132                    float y_offset,
133                    float scale,
134                    float perspective_offset) {
135   NSRect win_rect = [window frame];
136   win_rect.origin = NSZeroPoint;
137   NSRect screen_rect = win_rect;
138   screen_rect.origin = GetCGSWindowScreenOrigin(window);
140   // Apply a vertical translate.
141   screen_rect.origin.y -= y_offset;
143   // Apply a scale and translate to keep the window centered.
144   screen_rect.origin.x += (NSWidth(win_rect) - NSWidth(screen_rect)) / 2.0;
145   screen_rect.origin.y += (NSHeight(win_rect) - NSHeight(screen_rect)) / 2.0;
147   // A 2 x 2 mesh that maps each corner of the window to a location in screen
148   // coordinates. Note that the origin of the coordinate system is top, left.
149   CGPointWarp mesh[2][2] = {
150     {
151       {  // Top left.
152         {NSMinX(win_rect), NSMinY(win_rect)},
153         {NSMinX(screen_rect) + perspective_offset, NSMinY(screen_rect)},
154       },
155       {  // Top right.
156         {NSMaxX(win_rect), NSMinY(win_rect)},
157         {NSMaxX(screen_rect) - perspective_offset, NSMinY(screen_rect)},
158       }
159     },
160     {
161       {  // Bottom left.
162         {NSMinX(win_rect), NSMaxY(win_rect)},
163         {NSMinX(screen_rect), NSMaxY(screen_rect)},
164       },
165       {  // Bottom right.
166         {NSMaxX(win_rect), NSMaxY(win_rect)},
167         {NSMaxX(screen_rect), NSMaxY(screen_rect)},
168       }
169     },
170   };
172   CGSConnection cid = _CGSDefaultConnection();
173   CGSSetWindowWarp(cid, [window windowNumber], 2, 2, &(mesh[0][0]));
176 // Sets the various effects that are a part of the Show/Hide animation.
177 // Value is a number between 0 and 1 where 0 means the window is completely
178 // hidden and 1 means the window is fully visible.
179 void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) {
180   CGFloat inverse_value = 1.0 - value;
182   SetWindowAlpha(window, value);
183   CGFloat y_offset = kShowHideVerticalOffset * inverse_value;
184   CGFloat scale = 1.0 - (1.0 - kShowHideScaleFactor) * inverse_value;
185   CGFloat perspective_offset =
186       ([window frame].size.width * kShowHidePerspectiveFactor) * inverse_value;
188   SetWindowWarp(window, y_offset, scale, perspective_offset);
191 }  // namespace
193 @interface ConstrainedWindowAnimationBase ()
194 // Subclasses should override these to update the window state for the current
195 // animation value.
196 - (void)setWindowStateForStart;
197 - (void)setWindowStateForValue:(float)value;
198 - (void)setWindowStateForEnd;
199 @end
201 @implementation ConstrainedWindowAnimationBase
203 - (id)initWithWindow:(NSWindow*)window {
204   if ((self = [self initWithDuration:kAnimationDuration
205                       animationCurve:NSAnimationEaseInOut])) {
206     window_.reset([window retain]);
207     [self setAnimationBlockingMode:NSAnimationBlocking];
208     [self setWindowStateForStart];
209   }
210   return self;
213 - (void)stopAnimation {
214   [super stopAnimation];
215   [self setWindowStateForEnd];
216   if ([[self delegate] respondsToSelector:@selector(animationDidEnd:)])
217     [[self delegate] animationDidEnd:self];
220 - (void)setCurrentProgress:(NSAnimationProgress)progress {
221   [super setCurrentProgress:progress];
223   if (progress >= 1.0) {
224     [self setWindowStateForEnd];
225     return;
226   }
227   [self setWindowStateForValue:[self currentValue]];
230 - (void)setWindowStateForStart {
231   // Subclasses can optionally override this method.
234 - (void)setWindowStateForValue:(float)value {
235   // Subclasses must override this method.
236   NOTREACHED();
239 - (void)setWindowStateForEnd {
240   // Subclasses can optionally override this method.
243 @end
245 @implementation ConstrainedWindowAnimationShow
247 - (void)setWindowStateForStart {
248   SetWindowAlpha(window_, 0.0);
251 - (void)setWindowStateForValue:(float)value {
252   UpdateWindowShowHideAnimationState(window_, value);
255 - (void)setWindowStateForEnd {
256   SetWindowAlpha(window_, 1.0);
257   ClearWindowWarp(window_);
260 @end
262 @implementation ConstrainedWindowAnimationHide
264 - (void)setWindowStateForValue:(float)value {
265   UpdateWindowShowHideAnimationState(window_, 1.0 - value);
268 - (void)setWindowStateForEnd {
269   SetWindowAlpha(window_, 0.0);
270   ClearWindowWarp(window_);
273 @end
275 @implementation ConstrainedWindowAnimationPulse
277 // Sets the window scale based on the animation progress.
278 - (void)setWindowStateForValue:(float)value {
279   KeyFrame frames[] = {
280     {0.00, 1.0},
281     {0.40, 1.02},
282     {0.60, 1.02},
283     {1.00, 1.0},
284   };
286   CGFloat scale = 1;
287   for (int i = arraysize(frames) - 1; i >= 0; --i) {
288     if (value >= frames[i].value) {
289       CGFloat delta = frames[i + 1].value - frames[i].value;
290       CGFloat frame_progress = (value - frames[i].value) / delta;
291       scale = gfx::Tween::FloatValueBetween(frame_progress,
292                                             frames[i].scale,
293                                             frames[i + 1].scale);
294       break;
295     }
296   }
298   SetWindowScale(window_, scale);
301 - (void)setWindowStateForEnd {
302   SetWindowScale(window_, 1.0);
305 @end