Chromium Code Reviews| 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; |
| } |
| } |