// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.h"
#include <memory>
#import "chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller.h"
#include "chrome/browser/ui/cocoa/scoped_menu_bar_lock.h"
#import "ui/base/cocoa/tracking_area.h"
namespace {
// 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 = 50;
} // namespace
@implementation FullscreenToolbarMouseTracker {
// The frame for the tracking area. The value is the toolbar's frame with
// additional height added at the bottom.
NSRect _trackingAreaFrame;
// The tracking area associated with the toolbar. This tracking area is used
// to keep the toolbar active if the menubar had animated out but the mouse
// is still on the toolbar.
CrTrackingArea* __strong _trackingArea;
// Keeps the menu bar from hiding until the mouse exits the tracking area.
std::unique_ptr<ScopedMenuBarLock> _menuBarLock;
// The content view for the window.
// TODO(lgrey): Instead of retaining this, we should refrain from trying to
// remove the tracking area when the content view has already dealloced.
// Unfortunately, until we have a repro for https://crbug.com/1064911, we
// can't verify more targeted fixes (for example, only removing the tracking
// area if |_controller| has a window).
NSView* __strong _contentView;
FullscreenToolbarController* _controller; // weak
}
- (instancetype)initWithFullscreenToolbarController:
(FullscreenToolbarController*)controller {
if ((self = [super init])) {
_controller = controller;
}
return self;
}
- (void)dealloc {
[self removeTrackingArea];
}
- (void)updateTrackingArea {
// Remove the tracking area if the toolbar and menu bar aren't both visible.
if ([_controller toolbarFraction] == 0 || ![NSMenu menuBarVisible]) {
[self removeTrackingArea];
_menuBarLock.reset();
return;
}
if (_trackingArea) {
// If |trackingArea_|'s rect matches |trackingAreaFrame_|, quit early.
if (NSEqualRects(_trackingAreaFrame, [_trackingArea rect]))
return;
[self removeTrackingArea];
}
_contentView = [[_controller window] contentView];
_trackingArea = [[CrTrackingArea alloc]
initWithRect:_trackingAreaFrame
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow
owner:self
userInfo:nil];
[_contentView addTrackingArea:_trackingArea];
}
- (void)updateToolbarFrame:(NSRect)frame {
NSRect contentBounds = [[[_controller window] contentView] bounds];
_trackingAreaFrame = frame;
_trackingAreaFrame.origin.y -= kTrackingAreaAdditionalThreshold;
_trackingAreaFrame.size.height =
NSMaxY(contentBounds) - _trackingAreaFrame.origin.y;
[self updateTrackingArea];
}
- (void)removeTrackingArea {
if (!_trackingArea)
return;
// TODO(https://crbug.com/1063417, https://crbug.com/1064911): This DCHECK
// is hit when closing a fullscreen window using the traffic lights. This is
// because removeTrackingArea should be removed in response to the window
// closing, but isn't.
// DCHECK(_contentView);
[_contentView removeTrackingArea:_trackingArea];
_trackingArea = nil;
_contentView = nil;
}
- (void)mouseEntered:(NSEvent*)event {
_menuBarLock = std::make_unique<ScopedMenuBarLock>();
}
- (void)mouseExited:(NSEvent*)event {
_menuBarLock = nil;
}
@end