OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_animation
.h" |
| 6 |
| 7 #include "base/file_path.h" |
| 8 #include "base/location.h" |
| 9 #import "base/mac/foundation_util.h" |
| 10 #include "base/native_library.h" |
| 11 #include "ui/base/animation/tween.h" |
| 12 |
| 13 // The window animations in this file use private APIs as described here: |
| 14 // http://goo.gl/CK43l |
| 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 // then the window size. This does not seem to apply to scale transformations. |
| 19 |
| 20 // Length of the animation in seconds. |
| 21 const NSTimeInterval kSAnimationDuraiton = 0.18; |
| 22 |
| 23 // The number of pixels above the final destination to animate from. |
| 24 const CGFloat kShowHideVerticalOffset = 20; |
| 25 |
| 26 // Scale the window by this percent when animating. |
| 27 const CGFloat kShowHideScalePercent = 0.99; |
| 28 |
| 29 // Size of the perspective effect as a percent of the window width. |
| 30 const CGFloat kShowHidePerspectivePercent = 0.04; |
| 31 |
| 32 namespace { |
| 33 |
| 34 typedef int CGSWindow; |
| 35 typedef int CGSConnection; |
| 36 typedef struct { |
| 37 float x; |
| 38 float y; |
| 39 } MeshPoint; |
| 40 typedef struct { |
| 41 MeshPoint local; |
| 42 MeshPoint global; |
| 43 } CGPointWarp; |
| 44 |
| 45 // These are private APIs that we look up at runtime. |
| 46 typedef CGSConnection (*CGSDefaultConnectionFunc)(); |
| 47 typedef CGError (*CGSSetWindowTransformFunc)( |
| 48 const CGSConnection cid, |
| 49 const CGSWindow wid, |
| 50 CGAffineTransform transform); |
| 51 typedef CGError (*CGSSetWindowWarpFunc)( |
| 52 const CGSConnection cid, |
| 53 const CGSWindow wid, |
| 54 int w, |
| 55 int h, |
| 56 CGPointWarp* mesh); |
| 57 typedef CGError (*CGSSetWindowAlphaFunc)( |
| 58 const CGSConnection cid, |
| 59 const CGSWindow wid, |
| 60 float alpha); |
| 61 |
| 62 CGSDefaultConnectionFunc g_CGSDefaultConnection; |
| 63 CGSSetWindowTransformFunc g_CGSSetWindowTransform; |
| 64 CGSSetWindowWarpFunc g_CGSSetWindowWarp; |
| 65 CGSSetWindowAlphaFunc g_CGSSetWindowAlpha; |
| 66 |
| 67 // Look up private Window APIs. Returns true on success. |
| 68 bool InitializeWindowAPIs() { |
| 69 bool should_initialize = true; |
| 70 if (should_initialize) { |
| 71 should_initialize = false; |
| 72 CFBundleRef bundle = |
| 73 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreGraphics")); |
| 74 if (!bundle) |
| 75 return false; |
| 76 |
| 77 NSArray* names = [NSArray arrayWithObjects: |
| 78 @"_CGSDefaultConnection", |
| 79 @"CGSSetWindowTransform", |
| 80 @"CGSSetWindowWarp", |
| 81 @"CGSSetWindowAlpha", |
| 82 nil]; |
| 83 void* functions[[names count]]; |
| 84 CFBundleGetFunctionPointersForNames( |
| 85 bundle, base::mac::NSToCFCast(names), functions); |
| 86 |
| 87 g_CGSDefaultConnection = |
| 88 reinterpret_cast<CGSDefaultConnectionFunc>(functions[0]); |
| 89 g_CGSSetWindowTransform = |
| 90 reinterpret_cast<CGSSetWindowTransformFunc>(functions[1]); |
| 91 g_CGSSetWindowWarp = reinterpret_cast<CGSSetWindowWarpFunc>(functions[2]); |
| 92 g_CGSSetWindowAlpha = reinterpret_cast<CGSSetWindowAlphaFunc>(functions[3]); |
| 93 } |
| 94 return g_CGSDefaultConnection && g_CGSSetWindowTransform && |
| 95 g_CGSSetWindowWarp && g_CGSSetWindowAlpha; |
| 96 } |
| 97 |
| 98 // Get the window location relative to the top left of the main screen. |
| 99 // Most Cocoa APIs use a coordinate system where the screen origin is the |
| 100 // bottom left. The various CGSSetWindow* APIs use a coordinate system where |
| 101 // the screen origin is the top left. |
| 102 NSPoint GetCGSWindowScreenOrigin(NSWindow* window) { |
| 103 NSArray *screens = [NSScreen screens]; |
| 104 if ([screens count] == 0) |
| 105 return NSZeroPoint; |
| 106 NSScreen* main_screen = [screens objectAtIndex:0]; |
| 107 |
| 108 NSRect window_frame = [window frame]; |
| 109 NSRect screen_frame = [main_screen frame]; |
| 110 return NSMakePoint(NSMinX(window_frame), |
| 111 NSHeight(screen_frame) - NSMaxY(window_frame)); |
| 112 } |
| 113 |
| 114 // Set the transparency of the window. |
| 115 void SetWindowAlpha(NSWindow* window, float alpha) { |
| 116 CGSConnection cid = g_CGSDefaultConnection(); |
| 117 g_CGSSetWindowAlpha(cid, [window windowNumber], alpha); |
| 118 } |
| 119 |
| 120 // Scales the window and translates it so that it stays centered relative |
| 121 // to its original position. |
| 122 void SetWindowScale(NSWindow* window, float scale) { |
| 123 CGFloat scale_delta = 1.0 - scale; |
| 124 CGFloat cur_scale = 1.0 + scale_delta; |
| 125 CGAffineTransform transform = |
| 126 CGAffineTransformMakeScale(cur_scale, cur_scale); |
| 127 |
| 128 // Translate the window to keep it centered at the original location. |
| 129 NSSize window_size = [window frame].size; |
| 130 CGFloat scale_offset_x = window_size.width * (1 - cur_scale) / 2.0; |
| 131 CGFloat scale_offset_y = window_size.height * (1 - cur_scale) / 2.0; |
| 132 |
| 133 NSPoint origin = GetCGSWindowScreenOrigin(window); |
| 134 CGFloat new_x = -origin.x + scale_offset_x; |
| 135 CGFloat new_y = -origin.y + scale_offset_y; |
| 136 transform = CGAffineTransformTranslate(transform, new_x, new_y); |
| 137 |
| 138 CGSConnection cid = g_CGSDefaultConnection(); |
| 139 g_CGSSetWindowTransform(cid, [window windowNumber], transform); |
| 140 } |
| 141 |
| 142 // Unsets any window warp that may have been previously applied. |
| 143 // Window warp prevents other effects such as CGSSetWindowTransform from |
| 144 // being applied. |
| 145 void ClearWindowWarp(NSWindow* window) { |
| 146 CGSConnection cid = g_CGSDefaultConnection(); |
| 147 g_CGSSetWindowWarp(cid, [window windowNumber], 0, 0, NULL); |
| 148 } |
| 149 |
| 150 // Applies various transformations using a warp effect. The window is |
| 151 // translated vertically by |y_offset|. The window is scaled |by |scale| and |
| 152 // translated so that the it remains centered relative to its original position. |
| 153 // Finally, perspective is effect is applied by shrinking the top of the window. |
| 154 void SetWindowWarp(NSWindow* window, |
| 155 float y_offset, |
| 156 float scale, |
| 157 float perspective_offset) { |
| 158 NSRect win_rect = [window frame]; |
| 159 win_rect.origin = NSZeroPoint; |
| 160 NSRect screen_rect = win_rect; |
| 161 screen_rect.origin = GetCGSWindowScreenOrigin(window); |
| 162 |
| 163 // Apply a vertical translate. |
| 164 screen_rect.origin.y -= y_offset; |
| 165 |
| 166 // Apply a scale and translate to keep the window centered. |
| 167 screen_rect.origin.x += (NSWidth(win_rect) - NSWidth(screen_rect)) / 2.0; |
| 168 screen_rect.origin.y += (NSHeight(win_rect) - NSHeight(screen_rect)) / 2.0; |
| 169 |
| 170 // A 2 x 2 mesh that maps each corner of the window to a location in screen |
| 171 // coordinates. Note that the origin of the coordinate system is top, left. |
| 172 CGPointWarp mesh[2][2] = { |
| 173 { |
| 174 { // Top left. |
| 175 {NSMinX(win_rect), NSMinY(win_rect)}, |
| 176 {NSMinX(screen_rect) + perspective_offset, NSMinY(screen_rect)}, |
| 177 }, |
| 178 { // Top right. |
| 179 {NSMaxX(win_rect), NSMinY(win_rect)}, |
| 180 {NSMaxX(screen_rect) - perspective_offset, NSMinY(screen_rect)}, |
| 181 } |
| 182 }, |
| 183 { |
| 184 { // Bottom left. |
| 185 {NSMinX(win_rect), NSMaxY(win_rect)}, |
| 186 {NSMinX(screen_rect), NSMaxY(screen_rect)}, |
| 187 }, |
| 188 { // Bottom right. |
| 189 {NSMaxX(win_rect), NSMaxY(win_rect)}, |
| 190 {NSMaxX(screen_rect), NSMaxY(screen_rect)}, |
| 191 } |
| 192 }, |
| 193 }; |
| 194 |
| 195 CGSConnection cid = g_CGSDefaultConnection(); |
| 196 g_CGSSetWindowWarp(cid, [window windowNumber], 2, 2, &(mesh[0][0])); |
| 197 } |
| 198 |
| 199 // Sets the varoius effects that are a part of the Show/Hide animation. |
| 200 // Value is a number between 0 and 1 where 0 means the window is completely |
| 201 // hidden and 1 means the window is fully visible. |
| 202 void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) { |
| 203 CGFloat inverse_value = 1.0 - value; |
| 204 |
| 205 SetWindowAlpha(window, value); |
| 206 CGFloat y_offset = kShowHideVerticalOffset * inverse_value; |
| 207 CGFloat scale = 1.0 - (1.0 - kShowHideScalePercent) * inverse_value; |
| 208 CGFloat perspective_offset = |
| 209 ([window frame].size.width * kShowHidePerspectivePercent) * inverse_value; |
| 210 |
| 211 SetWindowWarp(window, y_offset, scale, perspective_offset); |
| 212 } |
| 213 |
| 214 } |
| 215 |
| 216 @interface ConstrainedWindowAnimationBase () |
| 217 // Subclasses should override these to update the window state for the current |
| 218 // animation value. |
| 219 - (void)setWindowStateForStart; |
| 220 - (void)setWindowStateForValue:(float)value; |
| 221 - (void)setWindowStateForEnd; |
| 222 @end |
| 223 |
| 224 @implementation ConstrainedWindowAnimationBase |
| 225 |
| 226 - (id)initWithWindow:(NSWindow*)window { |
| 227 if (!InitializeWindowAPIs()) { |
| 228 [self release]; |
| 229 return nil; |
| 230 } |
| 231 |
| 232 if ((self = [self initWithDuration:kSAnimationDuraiton |
| 233 animationCurve:NSAnimationEaseInOut])) { |
| 234 window_.reset([window retain]); |
| 235 [self setAnimationBlockingMode:NSAnimationNonblocking]; |
| 236 [self setWindowStateForStart]; |
| 237 } |
| 238 return self; |
| 239 } |
| 240 |
| 241 - (void)stopAnimation { |
| 242 [super stopAnimation]; |
| 243 [self setWindowStateForEnd]; |
| 244 if ([[self delegate] respondsToSelector:@selector(animationDidEnd:)]) |
| 245 [[self delegate] animationDidEnd:self]; |
| 246 } |
| 247 |
| 248 - (void)setCurrentProgress:(NSAnimationProgress)progress { |
| 249 [super setCurrentProgress:progress]; |
| 250 |
| 251 if (progress >= 1.0) { |
| 252 [self setWindowStateForEnd]; |
| 253 return; |
| 254 } |
| 255 [self setWindowStateForValue:[self currentValue]]; |
| 256 } |
| 257 |
| 258 - (void)setWindowStateForStart { |
| 259 // Subclasses can optionally override this method. |
| 260 } |
| 261 |
| 262 - (void)setWindowStateForValue:(float)value { |
| 263 // Subclasses must override this method. |
| 264 NOTREACHED(); |
| 265 } |
| 266 |
| 267 - (void)setWindowStateForEnd { |
| 268 // Subclasses can optionally override this method. |
| 269 } |
| 270 |
| 271 @end |
| 272 |
| 273 |
| 274 @implementation ConstrainedWindowAnimationShow |
| 275 |
| 276 - (void)setWindowStateForStart { |
| 277 SetWindowAlpha(window_, 0.0); |
| 278 } |
| 279 |
| 280 - (void)setWindowStateForValue:(float)value { |
| 281 UpdateWindowShowHideAnimationState(window_, value); |
| 282 } |
| 283 |
| 284 - (void)setWindowStateForEnd { |
| 285 SetWindowAlpha(window_, 1.0); |
| 286 ClearWindowWarp(window_); |
| 287 } |
| 288 |
| 289 @end |
| 290 |
| 291 @implementation ConstrainedWindowAnimationHide |
| 292 |
| 293 - (void)setWindowStateForValue:(float)value { |
| 294 UpdateWindowShowHideAnimationState(window_, 1.0 - value); |
| 295 } |
| 296 |
| 297 - (void)setWindowStateForEnd { |
| 298 SetWindowAlpha(window_, 0.0); |
| 299 ClearWindowWarp(window_); |
| 300 } |
| 301 |
| 302 @end |
| 303 |
| 304 @implementation ConstrainedWindowAnimationPulse |
| 305 |
| 306 // Sets the window scale based on the animation progress. |
| 307 - (void)setWindowStateForValue:(float)value { |
| 308 struct KeyFrame { |
| 309 float value; |
| 310 float scale; |
| 311 }; |
| 312 KeyFrame frames[] = { |
| 313 {0.00, 1.0}, |
| 314 {0.40, 1.02}, |
| 315 {0.60, 1.02}, |
| 316 {1.00, 1.0}, |
| 317 }; |
| 318 |
| 319 CGFloat scale = 1; |
| 320 int count = sizeof(frames) / sizeof(*frames); |
| 321 for (int i = count - 1; i >= 0; --i) { |
| 322 if (value >= frames[i].value) { |
| 323 CGFloat delta = frames[i + 1].value - frames[i].value; |
| 324 CGFloat percent = (value - frames[i].value) / delta; |
| 325 scale = ui::Tween::ValueBetween(percent, |
| 326 frames[i].scale, |
| 327 frames[i + 1].scale); |
| 328 break; |
| 329 } |
| 330 } |
| 331 |
| 332 SetWindowScale(window_, scale); |
| 333 } |
| 334 |
| 335 - (void)setWindowStateForEnd { |
| 336 SetWindowScale(window_, 1.0); |
| 337 } |
| 338 |
| 339 @end |
OLD | NEW |