Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm

Issue 848153005: Revert of mac: Implement custom AppKit Enter Fullscreen transition. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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/browser_window_enter_fullscreen_transition.h"
6
7 #include <QuartzCore/QuartzCore.h>
8
9 #include "base/mac/scoped_cftyperef.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/mac/sdk_forward_declarations.h"
12
13 namespace {
14
15 NSString* const kPrimaryWindowAnimationID = @"PrimaryWindowAnimationID";
16 NSString* const kSnapshotWindowAnimationID = @"SnapshotWindowAnimationID";
17 NSString* const kAnimationIDKey = @"AnimationIDKey";
18
19 // This class has two simultaneous animations to resize and reposition layers.
20 // These animations must use the same timing function, otherwise there will be
21 // visual discordance.
22 NSString* const kTransformAnimationTimingFunction =
23 kCAMediaTimingFunctionEaseInEaseOut;
24
25 } // namespace
26
27 @interface BrowserWindowEnterFullscreenTransition () {
28 // The window which is undergoing the fullscreen transition.
29 base::scoped_nsobject<NSWindow> primaryWindow_;
30
31 // A layer that holds a snapshot of the original state of |primaryWindow_|.
32 base::scoped_nsobject<CALayer> snapshotLayer_;
33
34 // A temporary window that holds |snapshotLayer_|.
35 base::scoped_nsobject<NSWindow> snapshotWindow_;
36
37 // The animation applied to |snapshotLayer_|.
38 base::scoped_nsobject<CAAnimationGroup> snapshotAnimation_;
39
40 // The animation applied to the root layer of |primaryWindow_|.
41 base::scoped_nsobject<CAAnimationGroup> primaryWindowAnimation_;
42
43 // The frame of the |primaryWindow_| before the transition began.
44 NSRect primaryWindowInitialFrame_;
45
46 // The background color of |primaryWindow_| before the transition began.
47 base::scoped_nsobject<NSColor> primaryWindowInitialBackgroundColor_;
48
49 // Whether |primaryWindow_| was opaque before the transition began.
50 BOOL primaryWindowInitialOpaque_;
51
52 // Whether the instance is in the process of changing the size of
53 // |primaryWindow_|.
54 BOOL changingPrimaryWindowSize_;
55
56 // The frame that |primaryWindow_| is expected to have after the transition
57 // is finished.
58 NSRect primaryWindowFinalFrame_;
59 }
60
61 // Takes a snapshot of |primaryWindow_| and puts it in |snapshotLayer_|.
62 - (void)takeSnapshot;
63
64 // Creates |snapshotWindow_| and adds |snapshotLayer_| to it.
65 - (void)makeAndPrepareSnapshotWindow;
66
67 // This method has several effects on |primaryWindow_|:
68 // - Saves current state.
69 // - Makes window transparent, with clear background.
70 // - Adds NSFullScreenWindowMask style mask.
71 // - Sets the size to the screen's size.
72 - (void)preparePrimaryWindowForAnimation;
73
74 // Applies the fullscreen animation to |snapshotLayer_|.
75 - (void)animateSnapshotWindowWithDuration:(CGFloat)duration;
76
77 // Applies the fullscreen animation to the root layer of |primaryWindow_|.
78 - (void)animatePrimaryWindowWithDuration:(CGFloat)duration;
79
80 // Override of CAAnimation delegate method.
81 - (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag;
82
83 // Returns the layer of the root view of |window|.
84 - (CALayer*)rootLayerOfWindow:(NSWindow*)window;
85
86 @end
87
88 @implementation BrowserWindowEnterFullscreenTransition
89
90 // -------------------------Public Methods----------------------------
91
92 - (instancetype)initWithWindow:(NSWindow*)window {
93 DCHECK(window);
94 DCHECK([self rootLayerOfWindow:window]);
95 if ((self = [super init])) {
96 primaryWindow_.reset([window retain]);
97 }
98 return self;
99 }
100
101 - (void)dealloc {
102 [snapshotAnimation_ setDelegate:nil];
103 [primaryWindowAnimation_ setDelegate:nil];
104 [super dealloc];
105 }
106
107 - (NSArray*)customWindowsToEnterFullScreen {
108 [self takeSnapshot];
109 [self makeAndPrepareSnapshotWindow];
110 [self preparePrimaryWindowForAnimation];
111 return @[ primaryWindow_.get(), snapshotWindow_.get() ];
112 }
113
114 - (void)startCustomAnimationToEnterFullScreenWithDuration:
115 (NSTimeInterval)duration {
116 [self animateSnapshotWindowWithDuration:duration];
117 [self animatePrimaryWindowWithDuration:duration];
118 }
119
120 - (BOOL)shouldWindowBeUnconstrained {
121 return changingPrimaryWindowSize_;
122 }
123
124 // -------------------------Private Methods----------------------------
125
126 - (void)takeSnapshot {
127 base::ScopedCFTypeRef<CGImageRef> windowSnapshot(CGWindowListCreateImage(
128 CGRectNull, kCGWindowListOptionIncludingWindow,
129 [primaryWindow_ windowNumber], kCGWindowImageBoundsIgnoreFraming));
130 snapshotLayer_.reset([[CALayer alloc] init]);
131 [snapshotLayer_ setFrame:NSRectToCGRect([primaryWindow_ frame])];
132 [snapshotLayer_ setContents:static_cast<id>(windowSnapshot.get())];
133 [snapshotLayer_ setAnchorPoint:CGPointMake(0, 0)];
134 [snapshotLayer_ setBackgroundColor:CGColorCreateGenericRGB(0, 0, 0, 0)];
135 }
136
137 - (void)makeAndPrepareSnapshotWindow {
138 DCHECK(snapshotLayer_);
139
140 snapshotWindow_.reset(
141 [[NSWindow alloc] initWithContentRect:[[primaryWindow_ screen] frame]
142 styleMask:0
143 backing:NSBackingStoreBuffered
144 defer:NO]);
145 [[snapshotWindow_ contentView] setWantsLayer:YES];
146 [snapshotWindow_ setOpaque:NO];
147 [snapshotWindow_ setBackgroundColor:[NSColor clearColor]];
148 [snapshotWindow_ setAnimationBehavior:NSWindowAnimationBehaviorNone];
149
150 [snapshotWindow_ orderFront:nil];
151 [[[snapshotWindow_ contentView] layer] addSublayer:snapshotLayer_];
152
153 // Compute the frame of the snapshot layer such that the snapshot is
154 // positioned exactly on top of the original position of |primaryWindow_|.
155 NSRect snapshotLayerFrame =
156 [snapshotWindow_ convertRectFromScreen:[primaryWindow_ frame]];
157 [snapshotLayer_ setFrame:snapshotLayerFrame];
158 }
159
160 - (void)preparePrimaryWindowForAnimation {
161 // Save initial state of |primaryWindow_|.
162 primaryWindowInitialFrame_ = [primaryWindow_ frame];
163 primaryWindowInitialBackgroundColor_.reset(
164 [[primaryWindow_ backgroundColor] copy]);
165 primaryWindowInitialOpaque_ = [primaryWindow_ isOpaque];
166
167 primaryWindowFinalFrame_ = [[primaryWindow_ screen] frame];
168
169 // Make |primaryWindow_| invisible. This must happen before the window is
170 // resized, since resizing the window will call drawRect: and cause content
171 // to flash over the entire screen.
172 [primaryWindow_ setOpaque:NO];
173 [primaryWindow_ setBackgroundColor:[NSColor clearColor]];
174 CALayer* rootLayer = [self rootLayerOfWindow:primaryWindow_];
175 rootLayer.opacity = 0;
176
177 // As soon as the style mask includes the flag NSFullScreenWindowMask, the
178 // window is expected to receive fullscreen layout. This must be set before
179 // the window is resized, as that causes a relayout.
180 [primaryWindow_
181 setStyleMask:[primaryWindow_ styleMask] | NSFullScreenWindowMask];
182
183 // Resize |primaryWindow_|.
184 changingPrimaryWindowSize_ = YES;
185 [primaryWindow_ setFrame:primaryWindowFinalFrame_ display:YES];
186 changingPrimaryWindowSize_ = NO;
187 }
188
189 - (void)animateSnapshotWindowWithDuration:(CGFloat)duration {
190 // Move the snapshot layer until it's bottom-left corner is at the
191 // bottom-left corner of the screen.
192 CABasicAnimation* positionAnimation =
193 [CABasicAnimation animationWithKeyPath:@"position"];
194 positionAnimation.toValue = [NSValue valueWithPoint:NSZeroPoint];
195 positionAnimation.timingFunction = [CAMediaTimingFunction
196 functionWithName:kTransformAnimationTimingFunction];
197
198 // Expand the bounds until it covers the screen.
199 NSRect finalBounds = NSMakeRect(0, 0, NSWidth(primaryWindowFinalFrame_),
200 NSHeight(primaryWindowFinalFrame_));
201 CABasicAnimation* boundsAnimation =
202 [CABasicAnimation animationWithKeyPath:@"bounds"];
203 boundsAnimation.toValue = [NSValue valueWithRect:finalBounds];
204 boundsAnimation.timingFunction = [CAMediaTimingFunction
205 functionWithName:kTransformAnimationTimingFunction];
206
207 // Fade out the snapshot layer.
208 CABasicAnimation* opacityAnimation =
209 [CABasicAnimation animationWithKeyPath:@"opacity"];
210 opacityAnimation.toValue = @(0.0);
211 opacityAnimation.timingFunction =
212 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
213
214 // Fill forwards, and don't remove the animation. When the animation
215 // completes, the entire window will be removed.
216 CAAnimationGroup* group = [CAAnimationGroup animation];
217 group.removedOnCompletion = NO;
218 group.fillMode = kCAFillModeForwards;
219 group.animations = @[ positionAnimation, boundsAnimation, opacityAnimation ];
220 group.duration = duration;
221 [group setValue:kSnapshotWindowAnimationID forKey:kAnimationIDKey];
222 group.delegate = self;
223
224 snapshotAnimation_.reset([group retain]);
225 [snapshotLayer_ addAnimation:group forKey:nil];
226 }
227
228 - (void)animatePrimaryWindowWithDuration:(CGFloat)duration {
229 // As soon as the window's root layer is scaled down, the opacity should be
230 // set back to 1. There are a couple of ways to do this. The easiest is to
231 // just have a dummy animation as part of the same animation group.
232 CABasicAnimation* opacityAnimation =
233 [CABasicAnimation animationWithKeyPath:@"opacity"];
234 opacityAnimation.fromValue = @(1.0);
235 opacityAnimation.toValue = @(1.0);
236
237 // The root layer's size should start scaled down to the initial size of
238 // |primaryWindow_|. The animation increases the size until the root layer
239 // fills the screen.
240 NSRect initialFrame = primaryWindowInitialFrame_;
241 NSRect endFrame = primaryWindowFinalFrame_;
242 CGFloat xScale = NSWidth(initialFrame) / NSWidth(endFrame);
243 CGFloat yScale = NSHeight(initialFrame) / NSHeight(endFrame);
244 CATransform3D initial = CATransform3DMakeScale(xScale, yScale, 1);
245 CABasicAnimation* transformAnimation =
246 [CABasicAnimation animationWithKeyPath:@"transform"];
247 transformAnimation.fromValue = [NSValue valueWithCATransform3D:initial];
248
249 CALayer* root = [self rootLayerOfWindow:primaryWindow_];
250
251 // Calculate the initial position of the root layer. This calculation is
252 // agnostic of the anchorPoint.
253 CGFloat layerStartPositionDeltaX = NSMidX(initialFrame) - NSMidX(endFrame);
254 CGFloat layerStartPositionDeltaY = NSMidY(initialFrame) - NSMidY(endFrame);
255 NSPoint layerStartPosition =
256 NSMakePoint(root.position.x + layerStartPositionDeltaX,
257 root.position.y + layerStartPositionDeltaY);
258
259 // Animate the primary window from its initial position.
260 CABasicAnimation* positionAnimation =
261 [CABasicAnimation animationWithKeyPath:@"position"];
262 positionAnimation.fromValue = [NSValue valueWithPoint:layerStartPosition];
263
264 CAAnimationGroup* group = [CAAnimationGroup animation];
265 group.removedOnCompletion = NO;
266 group.fillMode = kCAFillModeForwards;
267 group.animations =
268 @[ transformAnimation, opacityAnimation, positionAnimation ];
269 group.timingFunction = [CAMediaTimingFunction
270 functionWithName:kTransformAnimationTimingFunction];
271 group.duration = duration;
272 [group setValue:kPrimaryWindowAnimationID forKey:kAnimationIDKey];
273 group.delegate = self;
274
275 primaryWindowAnimation_.reset([group retain]);
276
277 [root addAnimation:group forKey:kPrimaryWindowAnimationID];
278 }
279
280 - (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag {
281 NSString* animationID = [theAnimation valueForKey:kAnimationIDKey];
282 if ([animationID isEqual:kSnapshotWindowAnimationID]) {
283 [snapshotWindow_ orderOut:nil];
284 snapshotWindow_.reset();
285 snapshotLayer_.reset();
286 return;
287 }
288
289 if ([animationID isEqual:kPrimaryWindowAnimationID]) {
290 [primaryWindow_ setOpaque:YES];
291 [primaryWindow_ setBackgroundColor:primaryWindowInitialBackgroundColor_];
292 CALayer* root = [self rootLayerOfWindow:primaryWindow_];
293 root.opacity = 1;
294 [root removeAnimationForKey:kPrimaryWindowAnimationID];
295 }
296 }
297
298 - (CALayer*)rootLayerOfWindow:(NSWindow*)window {
299 return [[[window contentView] superview] layer];
300 }
301
302 @end
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h ('k') | chrome/chrome_browser_ui.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698