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]; |