| Index: chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
|
| diff --git a/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm b/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
|
| index d9d4617700b9a2463271e13337d977853b011cd6..e34cb4cb1cd6855e6bfc1b57bef253d304ea6508 100644
|
| --- a/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
|
| +++ b/chrome/browser/ui/cocoa/fullscreen_toolbar_controller.mm
|
| @@ -13,15 +13,24 @@
|
| #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"
|
| +#import "ui/base/cocoa/tracking_area.h"
|
|
|
| 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 NSTimeInterval kDropdownAnimationDuration = 0.12;
|
| +// The duration of the toolbar show/hide animation.
|
| +const NSTimeInterval kDropdownAnimationDuration = 0.20;
|
|
|
| -// The duration the toolbar is revealed for tab strip changes.
|
| -const NSTimeInterval kDropdownForTabStripChangesDuration = 0.75;
|
| +// If the fullscreen toolbar is hidden, it is difficult for the user to see
|
| +// changes in the tabstrip. As a result, if a tab is inserted or the current
|
| +// tab switched to a new one, the toolbar must animate in and out to display
|
| +// the tabstrip changes to the user. The animation drops down the toolbar and
|
| +// then wait for 0.75 seconds before it hides the toolbar.
|
| +const NSTimeInterval kTabStripChangesDelay = 0.75;
|
| +
|
| +// Additional height threshold added at the toolbar's bottom. This is to mimic
|
| +// threshold the mouse position needs to be at before the menubar automatically
|
| +// hides.
|
| +const CGFloat kTrackingAreaAdditionalThreshold = 20;
|
|
|
| // The event kind value for a undocumented menubar show/hide Carbon event.
|
| const CGFloat kMenuBarRevealEventKind = 2004;
|
| @@ -31,6 +40,15 @@ const CGFloat kMenuBarRevealEventKind = 2004;
|
| // returns 0 when the menu bar is hidden.)
|
| const CGFloat kFloatingBarVerticalOffset = 22;
|
|
|
| +// Visibility fractions for the menubar and toolbar.
|
| +const CGFloat kHideFraction = 0.0;
|
| +const CGFloat kShowFraction = 1.0;
|
| +
|
| +// Helper function for comparing CGFloat values.
|
| +BOOL IsCGFloatEqual(CGFloat a, CGFloat b) {
|
| + return fabs(a - b) <= std::numeric_limits<CGFloat>::epsilon();
|
| +}
|
| +
|
| OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| EventRef event,
|
| void* context) {
|
| @@ -44,17 +62,17 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| // As such, we should ignore the kMenuBarRevealEventKind event if it gives
|
| // us a fraction of 0.0 or 1.0, and rely on kEventMenuBarShown and
|
| // kEventMenuBarHidden to set these values.
|
| - if (![self isFullscreenTransitionInProgress]) {
|
| + if (![self isFullscreenTransitionInProgress] && [self isInFullscreen]) {
|
| if (GetEventKind(event) == kMenuBarRevealEventKind) {
|
| CGFloat revealFraction = 0;
|
| GetEventParameter(event, FOUR_CHAR_CODE('rvlf'), typeCGFloat, NULL,
|
| sizeof(CGFloat), NULL, &revealFraction);
|
| - if (revealFraction > 0.0 && revealFraction < 1.0)
|
| + if (revealFraction > kHideFraction && revealFraction < kShowFraction)
|
| [self setMenuBarRevealProgress:revealFraction];
|
| } else if (GetEventKind(event) == kEventMenuBarShown) {
|
| - [self setMenuBarRevealProgress:1.0];
|
| + [self setMenuBarRevealProgress:kShowFraction];
|
| } else {
|
| - [self setMenuBarRevealProgress:0.0];
|
| + [self setMenuBarRevealProgress:kHideFraction];
|
| }
|
| }
|
|
|
| @@ -71,10 +89,11 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| FullscreenToolbarController* controller_;
|
| CGFloat startFraction_;
|
| CGFloat endFraction_;
|
| + CGFloat toolbarFraction_;
|
| }
|
|
|
| -@property(readonly, nonatomic) CGFloat startFraction;
|
| @property(readonly, nonatomic) CGFloat endFraction;
|
| +@property(readonly, nonatomic) CGFloat toolbarFraction;
|
|
|
| // Designated initializer. Asks |controller| for the current shown fraction, so
|
| // if the bar is already partially shown or partially hidden, the animation
|
| @@ -88,8 +107,8 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
|
|
| @implementation DropdownAnimation
|
|
|
| -@synthesize startFraction = startFraction_;
|
| @synthesize endFraction = endFraction_;
|
| +@synthesize toolbarFraction = toolbarFraction_;
|
|
|
| - (id)initWithFraction:(CGFloat)toFraction
|
| fullDuration:(CGFloat)fullDuration
|
| @@ -97,7 +116,7 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| controller:(FullscreenToolbarController*)controller {
|
| // Calculate the effective duration, based on the current shown fraction.
|
| DCHECK(controller);
|
| - CGFloat fromFraction = controller.toolbarFraction;
|
| + CGFloat fromFraction = [controller toolbarFraction];
|
| CGFloat effectiveDuration = fabs(fullDuration * (fromFraction - toFraction));
|
|
|
| if ((self = [super gtm_initWithDuration:effectiveDuration
|
| @@ -113,9 +132,9 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| // Called once per animation step. Overridden to change the floating bar's
|
| // position based on the animation's progress.
|
| - (void)setCurrentProgress:(NSAnimationProgress)progress {
|
| - CGFloat fraction =
|
| + toolbarFraction_ =
|
| startFraction_ + (progress * (endFraction_ - startFraction_));
|
| - [controller_ changeToolbarFraction:fraction];
|
| + [controller_ updateToolbar];
|
| }
|
|
|
| @end
|
| @@ -125,6 +144,13 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| // Updates the visibility of the menu bar and the dock.
|
| - (void)updateMenuBarAndDockVisibility;
|
|
|
| +// Methods to set up or remove the tracking area.
|
| +- (void)setupTrackingArea;
|
| +- (void)removeTrackingAreaIfNecessary;
|
| +
|
| +// Returns YES if the mouse is inside the tracking area.
|
| +- (BOOL)mouseInsideTrackingArea;
|
| +
|
| // Whether the current screen is expected to have a menu bar, regardless of
|
| // current visibility of the menu bar.
|
| - (BOOL)doesScreenHaveMenuBar;
|
| @@ -136,10 +162,9 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| // |kFullScreenModeHideDock| when the overlay is shown.
|
| - (base::mac::FullScreenMode)desiredSystemFullscreenMode;
|
|
|
| -// Change the overlay to the given fraction, with or without animation. Only
|
| -// guaranteed to work properly with |fraction == 0| or |fraction == 1|. This
|
| -// performs the show/hide (animation) immediately. It does not touch the timers.
|
| -- (void)changeOverlayToFraction:(CGFloat)fraction withAnimation:(BOOL)animate;
|
| +// Animate the overlay to the given visibility with animation. If |visible|
|
| +// is true, animate the toolbar to a fraction of 1.0. Otherwise it's 0.0.
|
| +- (void)animateToolbarVisibility:(BOOL)visible;
|
|
|
| // Cancels the timer for hiding the floating bar.
|
| - (void)cancelHideTimer;
|
| @@ -150,12 +175,6 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| // Stops any running animations, etc.
|
| - (void)cleanup;
|
|
|
| -// Shows and hides the UI associated with this window being active (having main
|
| -// status). This includes hiding the menu bar. These functions are called when
|
| -// the window gains or loses main status as well as in |-cleanup|.
|
| -- (void)showActiveWindowUI;
|
| -- (void)hideActiveWindowUI;
|
| -
|
| // Whether the menu bar should be shown in immersive fullscreen for the screen
|
| // that contains the window.
|
| - (BOOL)shouldShowMenubarInImmersiveFullscreen;
|
| @@ -165,7 +184,6 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| @implementation FullscreenToolbarController
|
|
|
| @synthesize slidingStyle = slidingStyle_;
|
| -@synthesize toolbarFraction = toolbarFraction_;
|
|
|
| - (id)initWithBrowserController:(BrowserWindowController*)controller
|
| style:(fullscreen_mac::SlidingStyle)style {
|
| @@ -200,10 +218,11 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| [super dealloc];
|
| }
|
|
|
| -- (void)setupFullscreenToolbarWithDropdown:(BOOL)showDropdown {
|
| +- (void)setupFullscreenToolbarForContentView:(NSView*)contentView {
|
| DCHECK(!inFullscreenMode_);
|
| + contentView_ = contentView;
|
| inFullscreenMode_ = YES;
|
| - [self changeToolbarFraction:(showDropdown ? 1 : 0)];
|
| +
|
| [self updateMenuBarAndDockVisibility];
|
|
|
| // Register for notifications. Self is removed as an observer in |-cleanup|.
|
| @@ -237,11 +256,11 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| }
|
|
|
| - (void)windowDidBecomeMain:(NSNotification*)notification {
|
| - [self showActiveWindowUI];
|
| + [self updateMenuBarAndDockVisibility];
|
| }
|
|
|
| - (void)windowDidResignMain:(NSNotification*)notification {
|
| - [self hideActiveWindowUI];
|
| + [self updateMenuBarAndDockVisibility];
|
| }
|
|
|
| - (CGFloat)floatingBarVerticalOffset {
|
| @@ -255,22 +274,22 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
|
| return;
|
|
|
| - if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
|
| + if (self.slidingStyle != fullscreen_mac::OMNIBOX_TABS_HIDDEN)
|
| return;
|
|
|
| [self cancelHideTimer];
|
| - [self changeOverlayToFraction:1 withAnimation:animate];
|
| + [self animateToolbarVisibility:YES];
|
| }
|
|
|
| - (void)ensureOverlayHiddenWithAnimation:(BOOL)animate {
|
| if (!inFullscreenMode_)
|
| return;
|
|
|
| - if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
|
| + if (self.slidingStyle != fullscreen_mac::OMNIBOX_TABS_HIDDEN)
|
| return;
|
|
|
| [self cancelHideTimer];
|
| - [self changeOverlayToFraction:0 withAnimation:animate];
|
| + [self animateToolbarVisibility:NO];
|
| }
|
|
|
| - (void)cancelAnimationAndTimer {
|
| @@ -285,8 +304,11 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| return;
|
| }
|
|
|
| - revealToolbarForTabStripChanges_ = YES;
|
| - [self ensureOverlayShownWithAnimation:YES];
|
| + // Reveal the toolbar for tabstrip changes if the toolbar is hidden.
|
| + if (IsCGFloatEqual([self toolbarFraction], kHideFraction)) {
|
| + isRevealingToolbarForTabStripChanges_ = YES;
|
| + [self ensureOverlayShownWithAnimation:YES];
|
| + }
|
| }
|
|
|
| - (void)setSystemFullscreenModeTo:(base::mac::FullScreenMode)mode {
|
| @@ -301,8 +323,22 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| systemFullscreenMode_ = mode;
|
| }
|
|
|
| -- (void)changeToolbarFraction:(CGFloat)fraction {
|
| - toolbarFraction_ = fraction;
|
| +- (void)mouseEntered:(NSEvent*)event {
|
| + // Empty implementation. Required for CrTrackingArea.
|
| +}
|
| +
|
| +- (void)mouseExited:(NSEvent*)event {
|
| + DCHECK(inFullscreenMode_);
|
| + DCHECK_EQ([event trackingArea], trackingArea_.get());
|
| +
|
| + // If the menubar is gone, animate the toolbar out.
|
| + if (IsCGFloatEqual(menubarFraction_, kHideFraction))
|
| + [self ensureOverlayHiddenWithAnimation:YES];
|
| +
|
| + [self removeTrackingAreaIfNecessary];
|
| +}
|
| +
|
| +- (void)updateToolbar {
|
| [browserController_ layoutSubviews];
|
|
|
| // In AppKit fullscreen, moving the mouse to the top of the screen toggles
|
| @@ -331,37 +367,73 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| : 0;
|
| }
|
|
|
| +- (CGFloat)toolbarFraction {
|
| + if ([browserController_ isBarVisibilityLockedForOwner:nil])
|
| + return kShowFraction;
|
| +
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
|
| + return kHideFraction;
|
| +
|
| + switch (slidingStyle_) {
|
| + case fullscreen_mac::OMNIBOX_TABS_PRESENT:
|
| + return kShowFraction;
|
| + case fullscreen_mac::OMNIBOX_TABS_NONE:
|
| + return kHideFraction;
|
| + case fullscreen_mac::OMNIBOX_TABS_HIDDEN:
|
| + if (currentAnimation_.get())
|
| + return [currentAnimation_ toolbarFraction];
|
| +
|
| + if (hideTimer_.get() || shouldAnimateToolbarOut_)
|
| + return kShowFraction;
|
| +
|
| + return toolbarFractionFromMenuProgress_;
|
| + }
|
| +}
|
| +
|
| - (BOOL)isFullscreenTransitionInProgress {
|
| return [browserController_ isFullscreenTransitionInProgress];
|
| }
|
|
|
| +- (BOOL)isInFullscreen {
|
| + return inFullscreenMode_;
|
| +}
|
| +
|
| - (BOOL)isMouseOnScreen {
|
| return NSMouseInRect([NSEvent mouseLocation],
|
| [[browserController_ window] screen].frame, false);
|
| }
|
|
|
| -- (void)animationDidStop:(NSAnimation*)animation {
|
| - // Reset the |currentAnimation_| pointer now that the animation is over.
|
| - currentAnimation_.reset();
|
| +- (void)setTrackingAreaFromOverlayFrame:(NSRect)frame {
|
| + NSRect contentBounds = [contentView_ bounds];
|
| + trackingAreaFrame_ = frame;
|
| + trackingAreaFrame_.origin.y -= kTrackingAreaAdditionalThreshold;
|
| + trackingAreaFrame_.size.height =
|
| + NSMaxY(contentBounds) - trackingAreaFrame_.origin.y;
|
| +}
|
|
|
| - if (revealToolbarForTabStripChanges_) {
|
| - if (toolbarFraction_ > 0.0) {
|
| +- (void)animationDidStop:(NSAnimation*)animation {
|
| + if (isRevealingToolbarForTabStripChanges_) {
|
| + if ([self toolbarFraction] > 0.0) {
|
| // Set the timer to hide the toolbar.
|
| [hideTimer_ invalidate];
|
| - hideTimer_.reset([[NSTimer
|
| - scheduledTimerWithTimeInterval:kDropdownForTabStripChangesDuration
|
| - target:self
|
| - selector:@selector(hideTimerFire:)
|
| - userInfo:nil
|
| - repeats:NO] retain]);
|
| + hideTimer_.reset(
|
| + [[NSTimer scheduledTimerWithTimeInterval:kTabStripChangesDelay
|
| + target:self
|
| + selector:@selector(hideTimerFire:)
|
| + userInfo:nil
|
| + repeats:NO] retain]);
|
| } else {
|
| - revealToolbarForTabStripChanges_ = NO;
|
| + isRevealingToolbarForTabStripChanges_ = NO;
|
| }
|
| }
|
| +
|
| + // Reset the |currentAnimation_| pointer now that the animation is over.
|
| + currentAnimation_.reset();
|
| }
|
|
|
| - (void)animationDidEnd:(NSAnimation*)animation {
|
| [self animationDidStop:animation];
|
| + [self setupTrackingArea];
|
| }
|
|
|
| - (void)setMenuBarRevealProgress:(CGFloat)progress {
|
| @@ -372,16 +444,27 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
|
|
| menubarFraction_ = progress;
|
|
|
| + if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_HIDDEN) {
|
| + if (IsCGFloatEqual(menubarFraction_, kShowFraction))
|
| + [self setupTrackingArea];
|
| +
|
| + // If the menubar is disappearing from the screen, check if the mouse
|
| + // is still interacting with the toolbar. If it is, don't set
|
| + // |toolbarFractionFromMenuProgress_| so that the the toolbar will remain
|
| + // on the screen.
|
| + BOOL isMenuBarDisappearing =
|
| + menubarFraction_ < toolbarFractionFromMenuProgress_;
|
| + if (!(isMenuBarDisappearing && [self mouseInsideTrackingArea]))
|
| + toolbarFractionFromMenuProgress_ = 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()) {
|
| - if (self.slidingStyle != fullscreen_mac::OMNIBOX_TABS_NONE)
|
| - toolbarFraction_ = progress;
|
| + if (!currentAnimation_.get())
|
| [browserController_ layoutSubviews];
|
| - }
|
| }
|
|
|
| @end
|
| @@ -404,6 +487,42 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
|
| }
|
|
|
| +- (void)setupTrackingArea {
|
| + if (trackingArea_) {
|
| + // If the tracking rectangle is already |trackingAreaBounds_|, quit early.
|
| + NSRect oldRect = [trackingArea_ rect];
|
| + if (NSEqualRects(trackingAreaFrame_, oldRect))
|
| + return;
|
| +
|
| + // Otherwise, remove it.
|
| + [self removeTrackingAreaIfNecessary];
|
| + }
|
| +
|
| + // Create and add a new tracking area for |frame|.
|
| + trackingArea_.reset([[CrTrackingArea alloc]
|
| + initWithRect:trackingAreaFrame_
|
| + options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow
|
| + owner:self
|
| + userInfo:nil]);
|
| + DCHECK(contentView_);
|
| + [contentView_ addTrackingArea:trackingArea_];
|
| +}
|
| +
|
| +- (void)removeTrackingAreaIfNecessary {
|
| + if (trackingArea_) {
|
| + DCHECK(contentView_); // |contentView_| better be valid.
|
| + [contentView_ removeTrackingArea:trackingArea_];
|
| + trackingArea_.reset();
|
| + }
|
| +}
|
| +
|
| +- (BOOL)mouseInsideTrackingArea {
|
| + NSWindow* window = [browserController_ window];
|
| + NSPoint mouseLoc = [window mouseLocationOutsideOfEventStream];
|
| + NSPoint mousePos = [contentView_ convertPoint:mouseLoc fromView:nil];
|
| + return NSMouseInRect(mousePos, trackingAreaFrame_, [contentView_ isFlipped]);
|
| +}
|
| +
|
| - (BOOL)doesScreenHaveMenuBar {
|
| if (![[NSScreen class]
|
| respondsToSelector:@selector(screensHaveSeparateSpaces)])
|
| @@ -425,16 +544,11 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| return base::mac::kFullScreenModeHideAll;
|
| }
|
|
|
| -- (void)changeOverlayToFraction:(CGFloat)fraction withAnimation:(BOOL)animate {
|
| - // The non-animated case is really simple, so do it and return.
|
| - if (!animate) {
|
| - [currentAnimation_ stopAnimation];
|
| - [self changeToolbarFraction:fraction];
|
| - return;
|
| - }
|
| +- (void)animateToolbarVisibility:(BOOL)visible {
|
| + CGFloat fraction = visible ? kShowFraction : kHideFraction;
|
|
|
| - // If we're already animating to the given fraction, then there's nothing more
|
| - // to do.
|
| + // If we're already animating to the given fraction, then there's nothing
|
| + // more to do.
|
| if (currentAnimation_ && [currentAnimation_ endFraction] == fraction)
|
| return;
|
|
|
| @@ -452,6 +566,10 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| [currentAnimation_ setAnimationBlockingMode:NSAnimationNonblocking];
|
| [currentAnimation_ setDelegate:self];
|
|
|
| + // If there is an existing tracking area, remove it. We do not track mouse
|
| + // movements during animations (see class comment in the header file).
|
| + [self removeTrackingAreaIfNecessary];
|
| +
|
| [currentAnimation_ startAnimation];
|
| }
|
|
|
| @@ -464,15 +582,16 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| DCHECK_EQ(hideTimer_, timer); // This better be our hide timer.
|
| [hideTimer_ invalidate]; // Make sure it doesn't repeat.
|
| hideTimer_.reset(); // And get rid of it.
|
| - [self changeOverlayToFraction:0 withAnimation:YES];
|
| + shouldAnimateToolbarOut_ = YES;
|
| + [self animateToolbarVisibility:NO];
|
| + shouldAnimateToolbarOut_ = NO;
|
| }
|
|
|
| - (void)cleanup {
|
| [self cancelAnimationAndTimer];
|
| [[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
| - // This isn't tracked when not in fullscreen mode.
|
| - [browserController_ releaseBarVisibilityForOwner:self withAnimation:NO];
|
| + [self removeTrackingAreaIfNecessary];
|
|
|
| // Call the main status resignation code to perform the associated cleanup,
|
| // since we will no longer be receiving actual status resignation
|
| @@ -483,16 +602,8 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
|
| browserController_ = nil;
|
| }
|
|
|
| -- (void)showActiveWindowUI {
|
| - [self updateMenuBarAndDockVisibility];
|
| -}
|
| -
|
| -- (void)hideActiveWindowUI {
|
| - [self updateMenuBarAndDockVisibility];
|
| -}
|
| -
|
| - (BOOL)shouldShowMenubarInImmersiveFullscreen {
|
| - return [self doesScreenHaveMenuBar] && toolbarFraction_ > 0.99;
|
| + return [self doesScreenHaveMenuBar] && [self toolbarFraction] > 0.99;
|
| }
|
|
|
| @end
|
|
|