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

Unified Diff: chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm

Issue 1276383004: Implemented fullscreen exit animation with AppKit (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Made some changes according to missed comments on Patch Created 5 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
diff --git a/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm b/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
similarity index 61%
rename from chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
rename to chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
index 7eca5ec2087b2c953a1e2a21254a7a57f1700c26..a56b78db6938be04074785b5e1fb5f6b65944af6 100644
--- a/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
+++ b/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
@@ -2,13 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#import "chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h"
+#import "chrome/browser/ui/cocoa/browser_window_fullscreen_transition.h"
#include <QuartzCore/QuartzCore.h>
-#include "base/mac/scoped_cftyperef.h"
-#include "base/mac/scoped_nsobject.h"
-#include "base/mac/sdk_forward_declarations.h"
+#include "base/mac/bind_objc_block.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_util.h"
+#import "base/mac/scoped_nsobject.h"
+#import "base/mac/sdk_forward_declarations.h"
+#include "base/memory/scoped_ptr.h"
+#import "chrome/browser/ui/cocoa/framed_browser_window.h"
+#import "chrome/browser/ui/cocoa/full_size_content_window.h"
namespace {
@@ -23,11 +28,30 @@ NSString* TransformAnimationTimingFunction() {
return kCAMediaTimingFunctionEaseInEaseOut;
}
+// This class locks and unlocks the FrameBrowserWindow. Its destructor
+// ensures that the lock gets released.
+class FrameAndStyleLock {
+ public:
+ FrameAndStyleLock(FramedBrowserWindow* window) {
+ window_.reset([window retain]);
+ }
+
+ void setLock(bool lock) { [window_ setFrameAndStyleMaskLock:lock]; }
+
+ ~FrameAndStyleLock() { setLock(NO); }
+
+ private:
+ base::scoped_nsobject<FramedBrowserWindow> window_;
+};
+
} // namespace
-@interface BrowserWindowEnterFullscreenTransition () {
+@interface BrowserWindowFullscreenTransition () {
+ // Flag to keep track of whether we are entering or exiting full screen
+ BOOL isEnteringFullscreen_;
+
// The window which is undergoing the fullscreen transition.
- base::scoped_nsobject<NSWindow> primaryWindow_;
+ base::scoped_nsobject<FramedBrowserWindow> primaryWindow_;
// A layer that holds a snapshot of the original state of |primaryWindow_|.
base::scoped_nsobject<CALayer> snapshotLayer_;
@@ -35,9 +59,6 @@ NSString* TransformAnimationTimingFunction() {
// A temporary window that holds |snapshotLayer_|.
base::scoped_nsobject<NSWindow> snapshotWindow_;
- // The frame of the |primaryWindow_| before the transition began.
- NSRect primaryWindowInitialFrame_;
-
// The background color of |primaryWindow_| before the transition began.
base::scoped_nsobject<NSColor> primaryWindowInitialBackgroundColor_;
@@ -48,9 +69,19 @@ NSString* TransformAnimationTimingFunction() {
// |primaryWindow_|.
BOOL changingPrimaryWindowSize_;
+ // The frame of the |primaryWindow_| before it starts the transition.
+ NSRect initialFrame_;
+
+ // A frame that represents the value of the |initialFrame_| that's relative
+ // to the current screen coordinates.
erikchen 2015/08/12 22:08:52 It looks like initialFrameRelativeToScreen_ is use
spqchan1 2015/08/13 20:38:48 Done. I modified the name so that it's less confus
+ NSRect initialFrameRelativeToScreen_;
erikchen 2015/08/12 22:08:52 If this ivar is only used for the exit animation,
spqchan1 2015/08/13 20:38:48 Done.
+
// The frame that |primaryWindow_| is expected to have after the transition
// is finished.
- NSRect primaryWindowFinalFrame_;
+ NSRect finalFrame_;
+
+ // Locks and unlocks the FullSizeContentWindow.
+ scoped_ptr<FrameAndStyleLock> lock_;
}
// Takes a snapshot of |primaryWindow_| and puts it in |snapshotLayer_|.
@@ -62,15 +93,16 @@ NSString* TransformAnimationTimingFunction() {
// This method has several effects on |primaryWindow_|:
// - Saves current state.
// - Makes window transparent, with clear background.
-// - Adds NSFullScreenWindowMask style mask.
-// - Sets the size to the screen's size.
+// - If we are entering fullscreen, it will also:
+// - Add NSFullScreenWindowMask style mask.
+// - Set the size to the screen's size.
- (void)preparePrimaryWindowForAnimation;
// Applies the fullscreen animation to |snapshotLayer_|.
- (void)animateSnapshotWindowWithDuration:(CGFloat)duration;
-// Applies the fullscreen animation to the root layer of |primaryWindow_|.
-- (void)animatePrimaryWindowWithDuration:(CGFloat)duration;
+// Sets |primaryWindow_|'s frame to the expected frame.
+- (void)changePrimaryWindowToFinalFrame;
// Override of CAAnimation delegate method.
- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag;
@@ -80,36 +112,71 @@ NSString* TransformAnimationTimingFunction() {
@end
-@implementation BrowserWindowEnterFullscreenTransition
+@implementation BrowserWindowFullscreenTransition
// -------------------------Public Methods----------------------------
-- (instancetype)initWithWindow:(NSWindow*)window {
+- (instancetype)initEnterWithWindow:(FramedBrowserWindow*)window {
DCHECK(window);
DCHECK([self rootLayerOfWindow:window]);
if ((self = [super init])) {
primaryWindow_.reset([window retain]);
+
+ isEnteringFullscreen_ = YES;
+ initialFrame_ = [primaryWindow_ frame];
+ finalFrame_ = [[primaryWindow_ screen] frame];
}
return self;
}
-- (NSArray*)customWindowsToEnterFullScreen {
+- (instancetype)initExitWithWindow:(FramedBrowserWindow*)window
+ frame:(NSRect)frame {
+ DCHECK(window);
+ DCHECK([self rootLayerOfWindow:window]);
+ if ((self = [super init])) {
+ primaryWindow_.reset([window retain]);
+
+ isEnteringFullscreen_ = NO;
+ finalFrame_ = frame;
+ initialFrame_ = [[primaryWindow_ screen] frame];
+
+ // Calculate the inital frame that is relative to coordinates of the
+ // screen which it's on.
+ initialFrameRelativeToScreen_ = finalFrame_;
+ CGFloat screenOriginX = finalFrame_.origin.x - initialFrame_.origin.x;
+ CGFloat screenOriginY = finalFrame_.origin.y - initialFrame_.origin.y;
+ initialFrameRelativeToScreen_.origin =
+ CGPointMake(screenOriginX, screenOriginY);
+
+ lock_.reset(new FrameAndStyleLock(window));
+ }
+ return self;
+}
+
+- (NSArray*)customWindowsForFullScreenTransition {
[self takeSnapshot];
[self makeAndPrepareSnapshotWindow];
- [self preparePrimaryWindowForAnimation];
return @[ primaryWindow_.get(), snapshotWindow_.get() ];
}
-- (void)startCustomAnimationToEnterFullScreenWithDuration:
- (NSTimeInterval)duration {
- [self animateSnapshotWindowWithDuration:duration];
+// Right before the animation begins, change the content view size
+// and lock the primary window so that OSX can't make changes to it
erikchen 2015/08/12 22:08:52 The lock only happens for the exit animation, righ
spqchan1 2015/08/13 20:38:48 Done. Moved this comment to the inside of prepareP
+// before we finish the animation.
+- (void)startCustomFullScreenAnimationWithDuration:(NSTimeInterval)duration {
+ [self preparePrimaryWindowForAnimation];
[self animatePrimaryWindowWithDuration:duration];
+ [self animateSnapshotWindowWithDuration:duration];
}
- (BOOL)shouldWindowBeUnconstrained {
return changingPrimaryWindowSize_;
}
+- (NSSize)getDesiredWindowLayoutSize {
+ return (isEnteringFullscreen_) ? [primaryWindow_ frame].size
+ : [[primaryWindow_ contentView] bounds].size;
+}
+
// -------------------------Private Methods----------------------------
- (void)takeSnapshot {
@@ -128,17 +195,16 @@ NSString* TransformAnimationTimingFunction() {
- (void)makeAndPrepareSnapshotWindow {
DCHECK(snapshotLayer_);
- snapshotWindow_.reset(
- [[NSWindow alloc] initWithContentRect:[[primaryWindow_ screen] frame]
- styleMask:0
- backing:NSBackingStoreBuffered
- defer:NO]);
+ snapshotWindow_.reset([[NSWindow alloc]
+ initWithContentRect:[[primaryWindow_ screen] frame]
+ styleMask:0
+ backing:NSBackingStoreBuffered
+ defer:NO]);
[[snapshotWindow_ contentView] setWantsLayer:YES];
[snapshotWindow_ setOpaque:NO];
[snapshotWindow_ setBackgroundColor:[NSColor clearColor]];
[snapshotWindow_ setAnimationBehavior:NSWindowAnimationBehaviorNone];
- [snapshotWindow_ orderFront:nil];
[[[snapshotWindow_ contentView] layer] addSublayer:snapshotLayer_];
// Compute the frame of the snapshot layer such that the snapshot is
@@ -149,46 +215,47 @@ NSString* TransformAnimationTimingFunction() {
}
- (void)preparePrimaryWindowForAnimation {
- // Save initial state of |primaryWindow_|.
- primaryWindowInitialFrame_ = [primaryWindow_ frame];
+ // Save the initial state of the primary window.
primaryWindowInitialBackgroundColor_.reset(
[[primaryWindow_ backgroundColor] copy]);
primaryWindowInitialOpaque_ = [primaryWindow_ isOpaque];
- primaryWindowFinalFrame_ = [[primaryWindow_ screen] frame];
-
- // Make |primaryWindow_| invisible. This must happen before the window is
erikchen 2015/08/12 22:08:52 Why did you remove these comments?
spqchan1 2015/08/13 20:38:48 Whoops, these were accidentally removed when I was
- // resized, since resizing the window will call drawRect: and cause content
- // to flash over the entire screen.
[primaryWindow_ setOpaque:NO];
- [primaryWindow_ setBackgroundColor:[NSColor clearColor]];
- CALayer* rootLayer = [self rootLayerOfWindow:primaryWindow_];
- rootLayer.opacity = 0;
-
- // As soon as the style mask includes the flag NSFullScreenWindowMask, the
- // window is expected to receive fullscreen layout. This must be set before
- // the window is resized, as that causes a relayout.
- [primaryWindow_
- setStyleMask:[primaryWindow_ styleMask] | NSFullScreenWindowMask];
- // Resize |primaryWindow_|.
- changingPrimaryWindowSize_ = YES;
- [primaryWindow_ setFrame:primaryWindowFinalFrame_ display:YES];
- changingPrimaryWindowSize_ = NO;
+ CALayer* root = [self rootLayerOfWindow:primaryWindow_];
+ root.opacity = 0;
+ root.frame = initialFrameRelativeToScreen_;
erikchen 2015/08/12 22:08:52 The enter animation didn't need to set the root's
spqchan1 2015/08/13 20:38:48 This should only be used for the exit animation. I
+
+ if (isEnteringFullscreen_) {
+ [primaryWindow_
+ setStyleMask:[primaryWindow_ styleMask] | NSFullScreenWindowMask];
+ [self changePrimaryWindowToFinalFrame];
+ } else {
+ FullSizeContentView* content =
+ base::mac::ObjCCast<FullSizeContentView>([primaryWindow_ contentView]);
+ [content forceFrameSize:finalFrame_.size];
+ lock_->setLock(YES);
+ }
}
- (void)animateSnapshotWindowWithDuration:(CGFloat)duration {
- // Move the snapshot layer until it's bottom-left corner is at the
- // bottom-left corner of the screen.
+ [snapshotWindow_ orderFront:nil];
erikchen 2015/08/12 22:08:52 Why did you move this from makeAndPrepareSnapshotW
spqchan1 2015/08/13 20:38:48 I moved this out of makeAndPrepareSnapshotWindow b
+
+ // Calculate the frame so that it's relative to the screen
+ NSRect finalFrameRelativeToScreen =
+ [snapshotWindow_ convertRectFromScreen:finalFrame_];
+
CABasicAnimation* positionAnimation =
[CABasicAnimation animationWithKeyPath:@"position"];
- positionAnimation.toValue = [NSValue valueWithPoint:NSZeroPoint];
+ positionAnimation.toValue =
+ [NSValue valueWithPoint:finalFrameRelativeToScreen.origin];
positionAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:TransformAnimationTimingFunction()];
- // Expand the bounds until it covers the screen.
- NSRect finalBounds = NSMakeRect(0, 0, NSWidth(primaryWindowFinalFrame_),
- NSHeight(primaryWindowFinalFrame_));
+ // Resize the bounds until it reaches the expected size at the end of the
+ // animation.
+ NSRect finalBounds =
+ NSMakeRect(0, 0, NSWidth(finalFrame_), NSHeight(finalFrame_));
CABasicAnimation* boundsAnimation =
[CABasicAnimation animationWithKeyPath:@"bounds"];
boundsAnimation.toValue = [NSValue valueWithRect:finalBounds];
@@ -227,8 +294,8 @@ NSString* TransformAnimationTimingFunction() {
// The root layer's size should start scaled down to the initial size of
// |primaryWindow_|. The animation increases the size until the root layer
// fills the screen.
- NSRect initialFrame = primaryWindowInitialFrame_;
- NSRect endFrame = primaryWindowFinalFrame_;
+ NSRect initialFrame = initialFrame_;
+ NSRect endFrame = finalFrame_;
CGFloat xScale = NSWidth(initialFrame) / NSWidth(endFrame);
CGFloat yScale = NSHeight(initialFrame) / NSHeight(endFrame);
CATransform3D initial = CATransform3DMakeScale(xScale, yScale, 1);
@@ -236,10 +303,9 @@ NSString* TransformAnimationTimingFunction() {
[CABasicAnimation animationWithKeyPath:@"transform"];
transformAnimation.fromValue = [NSValue valueWithCATransform3D:initial];
- CALayer* root = [self rootLayerOfWindow:primaryWindow_];
-
// Calculate the initial position of the root layer. This calculation is
// agnostic of the anchorPoint.
+ CALayer* root = [self rootLayerOfWindow:primaryWindow_];
CGFloat layerStartPositionDeltaX = NSMidX(initialFrame) - NSMidX(endFrame);
CGFloat layerStartPositionDeltaY = NSMidY(initialFrame) - NSMidY(endFrame);
NSPoint layerStartPosition =
@@ -255,7 +321,7 @@ NSString* TransformAnimationTimingFunction() {
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
group.animations =
- @[ transformAnimation, opacityAnimation, positionAnimation ];
+ @[ opacityAnimation, positionAnimation, transformAnimation ];
group.timingFunction = [CAMediaTimingFunction
functionWithName:TransformAnimationTimingFunction()];
group.duration = duration;
@@ -265,8 +331,16 @@ NSString* TransformAnimationTimingFunction() {
[root addAnimation:group forKey:kPrimaryWindowAnimationID];
}
+- (void)changePrimaryWindowToFinalFrame {
+ changingPrimaryWindowSize_ = YES;
+ [primaryWindow_ setFrame:finalFrame_ display:NO];
+ changingPrimaryWindowSize_ = NO;
+}
+
- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag {
NSString* animationID = [theAnimation valueForKey:kAnimationIDKey];
+
+ // Remove the snapshot window.
if ([animationID isEqual:kSnapshotWindowAnimationID]) {
[snapshotWindow_ orderOut:nil];
snapshotWindow_.reset();
@@ -275,11 +349,29 @@ NSString* TransformAnimationTimingFunction() {
}
if ([animationID isEqual:kPrimaryWindowAnimationID]) {
- [primaryWindow_ setOpaque:YES];
+ // If we're exiting full screen, we want to set the primary window
+ // size to the expected frame at the end. The window's frame will also
+ // need to be unlocked.
+ if (!isEnteringFullscreen_) {
+ lock_->setLock(NO);
+ [primaryWindow_
+ setStyleMask:[primaryWindow_ styleMask] & ~NSFullScreenWindowMask];
+ [self changePrimaryWindowToFinalFrame];
+ }
+
+ // Check if the final content view size is correct
+ NSSize expectedSize = finalFrame_.size;
+ NSView* content = [primaryWindow_ contentView];
+ DCHECK_EQ(NSHeight(content.frame), expectedSize.height);
+ DCHECK_EQ(NSWidth(content.frame), expectedSize.width);
+
+ // Restore the state of the primary window and make it visible again.
+ [primaryWindow_ setOpaque:primaryWindowInitialOpaque_];
[primaryWindow_ setBackgroundColor:primaryWindowInitialBackgroundColor_];
+
CALayer* root = [self rootLayerOfWindow:primaryWindow_];
- root.opacity = 1;
[root removeAnimationForKey:kPrimaryWindowAnimationID];
+ root.opacity = 1;
}
}

Powered by Google App Engine
This is Rietveld 408576698