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 17 namespace { 18 19 OSStatus 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. 32 const NSTimeInterval kAnimationDuration = 0.35; 33 34 // The height of the tracking area in which mouse entered/exit events will 35 // reveal the tabstrip. 36 const 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. 41 const 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