#include "base/command_line.h"
#import "base/mac/mac_util.h"
+#include "base/mac/sdk_forward_declarations.h"
#include "chrome/browser/fullscreen.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
-#import "chrome/browser/ui/cocoa/nsview_additions.h"
#include "chrome/common/chrome_switches.h"
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
+#import "ui/base/cocoa/nsview_additions.h"
NSString* const kWillEnterFullscreenNotification =
@"WillEnterFullscreenNotification";
@"WillLeaveFullscreenNotification";
namespace {
+
// The activation zone for the main menu is 4 pixels high; if we make it any
// smaller, then the menu can be made to appear without the bar sliding down.
const CGFloat kDropdownActivationZoneHeight = 4;
// returns 0 when the menu bar is hidden.)
const CGFloat kFloatingBarVerticalOffset = 22;
-} // end namespace
+OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
+ EventRef event,
+ void* context) {
+ PresentationModeController* self =
+ static_cast<PresentationModeController*>(context);
+ CGFloat revealFraction = 0;
+ GetEventParameter(event,
+ FOUR_CHAR_CODE('rvlf'),
+ typeCGFloat,
+ NULL,
+ sizeof(CGFloat),
+ NULL,
+ &revealFraction);
+ [self setMenuBarRevealProgress:revealFraction];
+ return CallNextEventHandler(handler, event);
+}
+} // end namespace
// Helper class to manage animations for the dropdown bar. Calls
-// [PresentationModeController changeFloatingBarShownFraction] once per
+// [PresentationModeController changeToolbarFraction] once per
// animation step.
@interface DropdownAnimation : NSAnimation {
@private
controller:(PresentationModeController*)controller {
// Calculate the effective duration, based on the current shown fraction.
DCHECK(controller);
- CGFloat fromFraction = [controller floatingBarShownFraction];
+ CGFloat fromFraction = controller.toolbarFraction;
CGFloat effectiveDuration = fabs(fullDuration * (fromFraction - toFraction));
if ((self = [super gtm_initWithDuration:effectiveDuration
- (void)setCurrentProgress:(NSAnimationProgress)progress {
CGFloat fraction =
startFraction_ + (progress * (endFraction_ - startFraction_));
- [controller_ changeFloatingBarShownFraction:fraction];
+ [controller_ changeToolbarFraction:fraction];
}
@end
@interface PresentationModeController (PrivateMethods)
+// Updates the visibility of the menu bar and the dock.
+- (void)updateMenuBarAndDockVisibility;
+
+// Whether the current screen is expected to have a menu bar, regardless of
+// current visibility of the menu bar.
+- (BOOL)doesScreenHaveMenuBar;
+
// Returns YES if the window is on the primary screen.
- (BOOL)isWindowOnPrimaryScreen;
-// Returns YES if it is ok to show and hide the menu bar in response to the
-// overlay opening and closing. Will return NO if the window is not main or not
-// on the primary monitor.
-- (BOOL)shouldToggleMenuBar;
-
// Returns |kFullScreenModeHideAll| when the overlay is hidden and
// |kFullScreenModeHideDock| when the overlay is shown.
- (base::mac::FullScreenMode)desiredSystemFullscreenMode;
- (void)showActiveWindowUI;
- (void)hideActiveWindowUI;
+// In Immersive Fullscreen, the menubar is visible iff. toolbarFraction_ >=
+// 1.0.
+- (BOOL)shouldShowMenubarInImmersiveFullscreen;
+
@end
@implementation PresentationModeController
@synthesize inPresentationMode = inPresentationMode_;
+@synthesize slidingStyle = slidingStyle_;
+@synthesize toolbarFraction = toolbarFraction_;
-- (id)initWithBrowserController:(BrowserWindowController*)controller {
+- (id)initWithBrowserController:(BrowserWindowController*)controller
+ style:(fullscreen_mac::SlidingStyle)style {
if ((self = [super init])) {
browserController_ = controller;
systemFullscreenMode_ = base::mac::kFullScreenModeNormal;
+ slidingStyle_ = style;
}
// Let the world know what we're up to.
postNotificationName:kWillEnterFullscreenNotification
object:nil];
+ // 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_);
DCHECK(!inPresentationMode_);
DCHECK(!trackingArea_);
[super dealloc];
enteringPresentationMode_ = YES;
inPresentationMode_ = YES;
contentView_ = contentView;
- [self changeFloatingBarShownFraction:(showDropdown ? 1 : 0)];
+ [self changeToolbarFraction:(showDropdown ? 1 : 0)];
+ [self updateMenuBarAndDockVisibility];
// Register for notifications. Self is removed as an observer in |-cleanup|.
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
return;
+ if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
+ return;
+
if (animate) {
if (delay) {
[self startShowTimer];
if (!inPresentationMode_)
return;
+ if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
+ return;
+
if (animate) {
if (delay) {
[self startHideTimer];
currentAnimation_.reset();
}
-- (CGFloat)floatingBarShownFraction {
- return [browserController_ floatingBarShownFraction];
-}
-
- (void)setSystemFullscreenModeTo:(base::mac::FullScreenMode)mode {
if (mode == systemFullscreenMode_)
return;
systemFullscreenMode_ = mode;
}
-- (void)changeFloatingBarShownFraction:(CGFloat)fraction {
- [browserController_ setFloatingBarShownFraction:fraction];
+- (void)changeToolbarFraction:(CGFloat)fraction {
+ toolbarFraction_ = fraction;
+ [browserController_ layoutSubviews];
+
+ // In AppKit fullscreen, moving the mouse to the top of the screen toggles
+ // menu visibility. Replicate the same effect for immersive fullscreen.
+ if ([browserController_ isInImmersiveFullscreen])
+ [self updateMenuBarAndDockVisibility];
+}
+
+// This method works, but is fragile.
+//
+// It gets used during view layout, which sometimes needs to be done at the
+// beginning of an animation. As such, this method needs to reflect the
+// menubarOffset expected at the end of the animation. This information is not
+// readily available. (The layout logic needs a refactor).
+//
+// For AppKit Fullscreen, the menubar always starts hidden, and
+// menubarFraction_ always starts at 0, so the logic happens to work. For
+// Immersive Fullscreen, this class controls the visibility of the menu bar, so
+// the logic is correct and not fragile.
+- (CGFloat)menubarOffset {
+ if ([browserController_ isInAppKitFullscreen])
+ return -std::floor(menubarFraction_ * [self floatingBarVerticalOffset]);
- if ([self shouldToggleMenuBar])
- [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
+ return [self shouldShowMenubarInImmersiveFullscreen]
+ ? -[self floatingBarVerticalOffset]
+ : 0;
}
// Used to activate the floating bar in presentation mode.
[self scheduleHideForMouse];
}
+- (void)setMenuBarRevealProgress:(CGFloat)progress {
+ menubarFraction_ = 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(erikchen): The animation is janky. layoutSubviews need a refactor so
+ // that it calls setFrameOffset: instead of setFrame: if the frame's size has
+ // not changed.
+ if (!currentAnimation_.get())
+ [browserController_ layoutSubviews];
+}
+
@end
@implementation PresentationModeController (PrivateMethods)
+- (void)updateMenuBarAndDockVisibility {
+ if (![[browserController_ window] isMainWindow] ||
+ ![browserController_ isInImmersiveFullscreen]) {
+ [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
+ return;
+ }
+
+ // The screen does not have a menu bar, so there's no need to hide it.
+ if (![self doesScreenHaveMenuBar]) {
+ [self setSystemFullscreenModeTo:base::mac::kFullScreenModeHideDock];
+ return;
+ }
+
+ [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
+}
+
+- (BOOL)doesScreenHaveMenuBar {
+ if (![[NSScreen class]
+ respondsToSelector:@selector(screensHaveSeparateSpaces)])
+ return [self isWindowOnPrimaryScreen];
+
+ BOOL eachScreenShouldHaveMenuBar = [NSScreen screensHaveSeparateSpaces];
+ return eachScreenShouldHaveMenuBar ?: [self isWindowOnPrimaryScreen];
+}
+
- (BOOL)isWindowOnPrimaryScreen {
NSScreen* screen = [[browserController_ window] screen];
NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0];
return (screen == primaryScreen);
}
-- (BOOL)shouldToggleMenuBar {
- return [browserController_ isInImmersiveFullscreen] &&
- [self isWindowOnPrimaryScreen] &&
- [[browserController_ window] isMainWindow];
-}
-
- (base::mac::FullScreenMode)desiredSystemFullscreenMode {
- if ([browserController_ floatingBarShownFraction] >= 1.0)
+ if ([self shouldShowMenubarInImmersiveFullscreen])
return base::mac::kFullScreenModeHideDock;
return base::mac::kFullScreenModeHideAll;
}
// The non-animated case is really simple, so do it and return.
if (!animate) {
[currentAnimation_ stopAnimation];
- [self changeFloatingBarShownFraction:fraction];
+ [self changeToolbarFraction:fraction];
return;
}
// to show or to hide).
[currentAnimation_ stopAnimation];
- // Now, if it happens to already be in the right state, there's nothing more
- // to do.
- if ([browserController_ floatingBarShownFraction] == fraction)
- return;
-
// Create the animation and set it up.
currentAnimation_.reset(
[[DropdownAnimation alloc] initWithFraction:fraction
// Call the main status resignation code to perform the associated cleanup,
// since we will no longer be receiving actual status resignation
// notifications.
- [self hideActiveWindowUI];
+ [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
// No more calls back up to the BWC.
browserController_ = nil;
}
- (void)showActiveWindowUI {
- DCHECK_EQ(systemFullscreenMode_, base::mac::kFullScreenModeNormal);
- if (systemFullscreenMode_ != base::mac::kFullScreenModeNormal)
- return;
-
- if ([self shouldToggleMenuBar])
- [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
+ [self updateMenuBarAndDockVisibility];
// TODO(rohitrao): Insert the Exit Fullscreen button. http://crbug.com/35956
}
- (void)hideActiveWindowUI {
- if ([self shouldToggleMenuBar])
- [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
+ [self updateMenuBarAndDockVisibility];
// TODO(rohitrao): Remove the Exit Fullscreen button. http://crbug.com/35956
}
+- (BOOL)shouldShowMenubarInImmersiveFullscreen {
+ return toolbarFraction_ >= 1.0;
+}
+
@end