| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // 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/fullscreen_mode_controller.h" |
| |
| #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
| #import "chrome/browser/ui/cocoa/browser_window_controller_private.h" |
| #import "chrome/browser/ui/cocoa/fast_resize_view.h" |
| |
| @interface FullscreenModeController (Private) |
| - (void)startAnimationToState:(FullscreenToolbarState)state; |
| - (void)setMenuBarRevealProgress:(CGFloat)progress; |
| - (void)setRevealAnimationProgress:(NSAnimationProgress)progress; |
| @end |
| |
| namespace { |
| |
| OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| EventRef event, |
| void* context) { |
| FullscreenModeController* self = |
| static_cast<FullscreenModeController*>(context); |
| CGFloat revealFraction = 0; |
| GetEventParameter(event, FOUR_CHAR_CODE('rvlf'), typeCGFloat, NULL, |
| sizeof(CGFloat), NULL, &revealFraction); |
| [self setMenuBarRevealProgress:revealFraction]; |
| return CallNextEventHandler(handler, event); |
| } |
| |
| // The duration of the animation to bring down the tabstrip. |
| const NSTimeInterval kAnimationDuration = 0.35; |
| |
| // The height of the tracking area in which mouse entered/exit events will |
| // reveal the tabstrip. |
| const NSUInteger kTrackingAreaHeight = 100; |
| |
| // There is a tiny gap between the window's max Y and the top of the screen, |
| // which will produce a mouse exit event when the menu bar should be revealed. |
| // This factor is the size of that gap plus padding. |
| const CGFloat kTrackingAreaMaxYEpsilon = 15; |
| |
| } // namespace |
| |
| // Animation /////////////////////////////////////////////////////////////////// |
| |
| @interface FullscreenModeDropDownAnimation : NSAnimation { |
| FullscreenModeController* controller_; |
| } |
| @end |
| |
| @implementation FullscreenModeDropDownAnimation |
| |
| - (id)initWithFullscreenModeController:(FullscreenModeController*)controller { |
| if ((self = [super initWithDuration:kAnimationDuration |
| animationCurve:NSAnimationEaseInOut])) { |
| controller_ = controller; |
| [self setAnimationBlockingMode:NSAnimationNonblocking]; |
| [self setDelegate:controller_]; |
| } |
| return self; |
| } |
| |
| - (void)setCurrentProgress:(NSAnimationProgress)progress { |
| [controller_ setRevealAnimationProgress:progress]; |
| } |
| |
| @end |
| |
| // Implementation ////////////////////////////////////////////////////////////// |
| |
| @implementation FullscreenModeController |
| |
| - (id)initWithBrowserWindowController:(BrowserWindowController*)bwc { |
| if ((self = [super init])) { |
| controller_ = bwc; |
| |
| currentState_ = kFullscreenToolbarOnly; |
| destinationState_ = kFullscreenToolbarOnly; |
| |
| // Create the tracking area at the top of the window. |
| NSRect windowFrame = [[bwc window] frame]; |
| NSRect trackingRect = NSMakeRect( |
| 0, NSHeight(windowFrame) - kTrackingAreaHeight, |
| NSWidth(windowFrame), kTrackingAreaHeight); |
| trackingArea_.reset( |
| [[CrTrackingArea alloc] initWithRect:trackingRect |
| options:NSTrackingMouseEnteredAndExited | |
| NSTrackingActiveAlways |
| owner:self |
| userInfo:nil]); |
| [[[bwc window] contentView] addTrackingArea:trackingArea_.get()]; |
| |
| // Install the Carbon event handler for the undocumented menu bar show/hide |
| // event. |
| EventTypeSpec eventSpec = { kEventClassMenu, 2004 }; |
| InstallApplicationEventHandler(NewEventHandlerUPP(&MenuBarRevealHandler), |
| 1, &eventSpec, |
| self, &menuBarTrackingHandler_); |
| } |
| return self; |
| } |
| |
| - (void)dealloc { |
| RemoveEventHandler(menuBarTrackingHandler_); |
| [[[controller_ window] contentView] removeTrackingArea:trackingArea_.get()]; |
| [super dealloc]; |
| } |
| |
| - (CGFloat)menuBarHeight { |
| // -[NSMenu menuBarHeight] will return 0 when the menu bar is hidden, so |
| // use the status bar API instead, which always returns a constant value. |
| return [[NSStatusBar systemStatusBar] thickness] * menuBarRevealFraction_; |
| } |
| |
| - (void)mouseEntered:(NSEvent*)event { |
| if (animation_ || currentState_ == kFullscreenToolbarAndTabstrip) |
| return; |
| |
| [self startAnimationToState:kFullscreenToolbarAndTabstrip]; |
| } |
| |
| - (void)mouseExited:(NSEvent*)event { |
| if (animation_ || currentState_ == kFullscreenToolbarOnly) |
| return; |
| |
| // Take allowance for the small gap between the window max Y and top edge of |
| // the screen. |
| NSPoint mousePoint = [NSEvent mouseLocation]; |
| NSRect screenRect = [[[controller_ window] screen] frame]; |
| if (mousePoint.y >= NSMaxY(screenRect) - kTrackingAreaMaxYEpsilon) |
| return; |
| |
| [self startAnimationToState:kFullscreenToolbarOnly]; |
| } |
| |
| - (void)startAnimationToState:(FullscreenToolbarState)state { |
| DCHECK_NE(currentState_, state); |
| |
| destinationState_ = state; |
| |
| // Turn on fast resize mode to ensure a smooth web contents animation. |
| // TODO(rsesek): This makes the animation jump at the end. |
| [[controller_ tabContentArea] setFastResizeMode:YES]; |
| |
| animation_.reset([[FullscreenModeDropDownAnimation alloc] |
| initWithFullscreenModeController:self]); |
| [animation_ startAnimation]; |
| } |
| |
| - (void)setMenuBarRevealProgress:(CGFloat)progress { |
| menuBarRevealFraction_ = progress; |
| |
| // If an animation is not running, then -layoutSubviews will not be called |
| // for each tick of the menu bar reveal. Do that manually. |
| // TODO(rsesek): This is kind of hacky and janky. |
| if (!animation_) |
| [controller_ layoutSubviews]; |
| } |
| |
| - (void)setRevealAnimationProgress:(NSAnimationProgress)progress { |
| // When hiding the tabstrip, invert the fraction. |
| if (destinationState_ == kFullscreenToolbarOnly) |
| progress = 1.0 - progress; |
| [controller_ setFloatingBarShownFraction:progress]; |
| } |
| |
| - (void)animationDidEnd:(NSAnimation*)animation { |
| DCHECK_EQ(animation_.get(), animation); |
| |
| currentState_ = destinationState_; |
| |
| [animation_ setDelegate:nil]; |
| animation_.reset(); |
| |
| [[controller_ tabContentArea] setFastResizeMode:NO]; |
| } |
| |
| @end |