1// Copyright 2013 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/fullscreen_mode_controller.h" 6 7#import "chrome/browser/ui/cocoa/browser_window_controller.h" 8#import "chrome/browser/ui/cocoa/browser_window_controller_private.h" 9#import "chrome/browser/ui/cocoa/fast_resize_view.h" 10 11@interface FullscreenModeController (Private) 12- (void)startAnimationToState:(FullscreenToolbarState)state; 13- (void)setMenuBarRevealProgress:(CGFloat)progress; 14- (void)setRevealAnimationProgress:(NSAnimationProgress)progress; 15@end 16 17namespace { 18 19OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, 20 EventRef event, 21 void* context) { 22 FullscreenModeController* self = 23 static_cast<FullscreenModeController*>(context); 24 CGFloat revealFraction = 0; 25 GetEventParameter(event, FOUR_CHAR_CODE('rvlf'), typeCGFloat, NULL, 26 sizeof(CGFloat), NULL, &revealFraction); 27 [self setMenuBarRevealProgress:revealFraction]; 28 return CallNextEventHandler(handler, event); 29} 30 31// The duration of the animation to bring down the tabstrip. 32const NSTimeInterval kAnimationDuration = 0.35; 33 34// The height of the tracking area in which mouse entered/exit events will 35// reveal the tabstrip. 36const NSUInteger kTrackingAreaHeight = 100; 37 38// There is a tiny gap between the window's max Y and the top of the screen, 39// which will produce a mouse exit event when the menu bar should be revealed. 40// This factor is the size of that gap plus padding. 41const CGFloat kTrackingAreaMaxYEpsilon = 15; 42 43} // namespace 44 45// Animation /////////////////////////////////////////////////////////////////// 46 47@interface FullscreenModeDropDownAnimation : NSAnimation { 48 FullscreenModeController* controller_; 49} 50@end 51 52@implementation FullscreenModeDropDownAnimation 53 54- (id)initWithFullscreenModeController:(FullscreenModeController*)controller { 55 if ((self = [super initWithDuration:kAnimationDuration 56 animationCurve:NSAnimationEaseInOut])) { 57 controller_ = controller; 58 [self setAnimationBlockingMode:NSAnimationNonblocking]; 59 [self setDelegate:controller_]; 60 } 61 return self; 62} 63 64- (void)setCurrentProgress:(NSAnimationProgress)progress { 65 [controller_ setRevealAnimationProgress:progress]; 66} 67 68@end 69 70// Implementation ////////////////////////////////////////////////////////////// 71 72@implementation FullscreenModeController 73 74- (id)initWithBrowserWindowController:(BrowserWindowController*)bwc { 75 if ((self = [super init])) { 76 controller_ = bwc; 77 78 currentState_ = kFullscreenToolbarOnly; 79 destinationState_ = kFullscreenToolbarOnly; 80 81 // Create the tracking area at the top of the window. 82 NSRect windowFrame = [[bwc window] frame]; 83 NSRect trackingRect = NSMakeRect( 84 0, NSHeight(windowFrame) - kTrackingAreaHeight, 85 NSWidth(windowFrame), kTrackingAreaHeight); 86 trackingArea_.reset( 87 [[CrTrackingArea alloc] initWithRect:trackingRect 88 options:NSTrackingMouseEnteredAndExited | 89 NSTrackingActiveAlways 90 owner:self 91 userInfo:nil]); 92 [[[bwc window] contentView] addTrackingArea:trackingArea_.get()]; 93 94 // Install the Carbon event handler for the undocumented menu bar show/hide 95 // event. 96 EventTypeSpec eventSpec = { kEventClassMenu, 2004 }; 97 InstallApplicationEventHandler(NewEventHandlerUPP(&MenuBarRevealHandler), 98 1, &eventSpec, 99 self, &menuBarTrackingHandler_); 100 } 101 return self; 102} 103 104- (void)dealloc { 105 RemoveEventHandler(menuBarTrackingHandler_); 106 [[[controller_ window] contentView] removeTrackingArea:trackingArea_.get()]; 107 [super dealloc]; 108} 109 110- (CGFloat)menuBarHeight { 111 // -[NSMenu menuBarHeight] will return 0 when the menu bar is hidden, so 112 // use the status bar API instead, which always returns a constant value. 113 return [[NSStatusBar systemStatusBar] thickness] * menuBarRevealFraction_; 114} 115 116- (void)mouseEntered:(NSEvent*)event { 117 if (animation_ || currentState_ == kFullscreenToolbarAndTabstrip) 118 return; 119 120 [self startAnimationToState:kFullscreenToolbarAndTabstrip]; 121} 122 123- (void)mouseExited:(NSEvent*)event { 124 if (animation_ || currentState_ == kFullscreenToolbarOnly) 125 return; 126 127 // Take allowance for the small gap between the window max Y and top edge of 128 // the screen. 129 NSPoint mousePoint = [NSEvent mouseLocation]; 130 NSRect screenRect = [[[controller_ window] screen] frame]; 131 if (mousePoint.y >= NSMaxY(screenRect) - kTrackingAreaMaxYEpsilon) 132 return; 133 134 [self startAnimationToState:kFullscreenToolbarOnly]; 135} 136 137- (void)startAnimationToState:(FullscreenToolbarState)state { 138 DCHECK_NE(currentState_, state); 139 140 destinationState_ = state; 141 142 // Turn on fast resize mode to ensure a smooth web contents animation. 143 // TODO(rsesek): This makes the animation jump at the end. 144 [[controller_ tabContentArea] setFastResizeMode:YES]; 145 146 animation_.reset([[FullscreenModeDropDownAnimation alloc] 147 initWithFullscreenModeController:self]); 148 [animation_ startAnimation]; 149} 150 151- (void)setMenuBarRevealProgress:(CGFloat)progress { 152 menuBarRevealFraction_ = progress; 153 154 // If an animation is not running, then -layoutSubviews will not be called 155 // for each tick of the menu bar reveal. Do that manually. 156 // TODO(rsesek): This is kind of hacky and janky. 157 if (!animation_) 158 [controller_ layoutSubviews]; 159} 160 161- (void)setRevealAnimationProgress:(NSAnimationProgress)progress { 162 // When hiding the tabstrip, invert the fraction. 163 if (destinationState_ == kFullscreenToolbarOnly) 164 progress = 1.0 - progress; 165 [controller_ setFloatingBarShownFraction:progress]; 166} 167 168- (void)animationDidEnd:(NSAnimation*)animation { 169 DCHECK_EQ(animation_.get(), animation); 170 171 currentState_ = destinationState_; 172 173 [animation_ setDelegate:nil]; 174 animation_.reset(); 175 176 [[controller_ tabContentArea] setFastResizeMode:NO]; 177} 178 179@end 180