• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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