Chromium Code Reviews| 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..aeb7b7ef378847734ca3e1158e61fc8ca433dac9 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 = 24; |
| // The event kind value for a undocumented menubar show/hide Carbon event. |
| const CGFloat kMenuBarRevealEventKind = 2004; |
| @@ -44,7 +53,7 @@ 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, |
| @@ -125,6 +134,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; |
| @@ -141,6 +157,9 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| // performs the show/hide (animation) immediately. It does not touch the timers. |
| - (void)changeOverlayToFraction:(CGFloat)fraction withAnimation:(BOOL)animate; |
| +// Releases the bar visibility and hides the toolbar. |
| +- (void)releaseAndHideBarVisibility; |
| + |
| // Cancels the timer for hiding the floating bar. |
| - (void)cancelHideTimer; |
| @@ -200,9 +219,13 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| [super dealloc]; |
| } |
| -- (void)setupFullscreenToolbarWithDropdown:(BOOL)showDropdown { |
| +- (void)setupFullscreenToolbarForContentView:(NSView*)contentView |
| + showDropdown:(BOOL)showDropdown { |
| DCHECK(!inFullscreenMode_); |
| + contentView_ = contentView; |
| + |
| inFullscreenMode_ = YES; |
| + |
| [self changeToolbarFraction:(showDropdown ? 1 : 0)]; |
| [self updateMenuBarAndDockVisibility]; |
| @@ -301,8 +324,22 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| systemFullscreenMode_ = mode; |
| } |
| +- (void)mouseEntered:(NSEvent*)event { |
| + // Empty implementation. Required for CrTrackingArea. |
| +} |
| + |
| +- (void)mouseExited:(NSEvent*)event { |
| + DCHECK(inFullscreenMode_); |
| + |
| + // Release the toolbar if the mouse exits the tracking area, |
| + if ([event trackingArea] == trackingArea_.get()) |
| + [self releaseAndHideBarVisibility]; |
| +} |
| + |
| - (void)changeToolbarFraction:(CGFloat)fraction { |
| - toolbarFraction_ = fraction; |
| + toolbarFraction_ = |
| + [browserController_ isBarVisibilityLockedForOwner:nil] ? 1.0 : fraction; |
|
erikchen
2016/08/23 21:19:58
This line concerns me a lot. The previous method s
|
| + |
| [browserController_ layoutSubviews]; |
| // In AppKit fullscreen, moving the mouse to the top of the screen toggles |
| @@ -335,11 +372,21 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| return [browserController_ isFullscreenTransitionInProgress]; |
| } |
| +- (BOOL)isInFullscreen { |
| + return inFullscreenMode_; |
| +} |
| + |
| - (BOOL)isMouseOnScreen { |
| return NSMouseInRect([NSEvent mouseLocation], |
| [[browserController_ window] screen].frame, false); |
| } |
| +- (void)setTrackingAreaFromOverlayFrame:(NSRect)frame { |
| + trackingAreaFrame_ = frame; |
| + trackingAreaFrame_.origin.y -= kTrackingAreaAdditionalThreshold; |
| + trackingAreaFrame_.size.height += kTrackingAreaAdditionalThreshold; |
| +} |
| + |
| - (void)animationDidStop:(NSAnimation*)animation { |
| // Reset the |currentAnimation_| pointer now that the animation is over. |
| currentAnimation_.reset(); |
| @@ -348,12 +395,12 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| if (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; |
| } |
| @@ -362,6 +409,7 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| - (void)animationDidEnd:(NSAnimation*)animation { |
| [self animationDidStop:animation]; |
| + [self setupTrackingArea]; |
| } |
| - (void)setMenuBarRevealProgress:(CGFloat)progress { |
| @@ -379,8 +427,22 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| // not changed. |
| if (!currentAnimation_.get()) { |
| if (self.slidingStyle != fullscreen_mac::OMNIBOX_TABS_NONE) |
| - toolbarFraction_ = progress; |
| - [browserController_ layoutSubviews]; |
| + [self changeToolbarFraction:progress]; |
| + else |
| + [browserController_ layoutSubviews]; |
| + } |
| + |
| + if (toolbarFraction_ == 1.0) { |
| + // Lock the toolbar's visibility and set up the tracking area. |
| + [browserController_ lockBarVisibilityForOwner:self withAnimation:NO]; |
| + [self setupTrackingArea]; |
| + |
| + // If the menubar is gone, check if the mouse is inside the new tracking |
| + // area. There's a chance that the tracking area had slipped away from the |
| + // cursor when toolbar shifted up from the menubar changes. As a result, |
| + // this would've prevent us from getting a |mouseExited:| call. |
| + if (menubarFraction_ == 0 && ![self mouseInsideTrackingArea]) |
| + [self releaseAndHideBarVisibility]; |
| } |
| } |
| @@ -404,6 +466,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)]) |
| @@ -452,9 +550,30 @@ 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]; |
| } |
| +- (void)releaseAndHideBarVisibility { |
| + // There are two ways for the toolbar to hide: |
| + // 1. Via the menubar, if the menubar is fully visible. |
| + // With its visibility released, the Carbon menubar events will hide the |
| + // toobar in sync with the menubar. For this case, we need to call |
| + // ensureOverlayShownWithAnimation: so that the toolbar won't hide before |
| + // the menubar does. |
| + // 2. Via DropdownAnimation, if the menubar is already gone. The animation is |
| + // created when we call releaseBarVisibilityForOwner:, and it will hide |
| + // the toolbar. |
| + [browserController_ releaseBarVisibilityForOwner:self withAnimation:YES]; |
| + if (menubarFraction_ == 1.0) |
| + [self ensureOverlayShownWithAnimation:NO]; |
| + |
| + [self removeTrackingAreaIfNecessary]; |
| +} |
| + |
| - (void)cancelHideTimer { |
| [hideTimer_ invalidate]; |
| hideTimer_.reset(); |
| @@ -471,6 +590,8 @@ OSStatus MenuBarRevealHandler(EventHandlerCallRef handler, |
| [self cancelAnimationAndTimer]; |
| [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| + [self removeTrackingAreaIfNecessary]; |
| + |
| // This isn't tracked when not in fullscreen mode. |
| [browserController_ releaseBarVisibilityForOwner:self withAnimation:NO]; |