1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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/immersive_fullscreen_controller.h"
#import "base/mac/mac_util.h"
#include "base/mac/sdk_forward_declarations.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "ui/base/cocoa/tracking_area.h"
namespace {
// The height from the top of the screen that will show the menubar.
const CGFloat kMenubarShowZoneHeight = 4;
// The height from the top of the screen that will hide the menubar.
// The value must be greater than the menubar's height of 22px.
const CGFloat kMenubarHideZoneHeight = 28;
} // namespace
@interface ImmersiveFullscreenController () {
BrowserWindowController* browserController_; // weak
// Used to track the mouse movements to show/hide the menu.
base::scoped_nsobject<CrTrackingArea> trackingArea_;
// The content view for the window.
NSView* contentView_; // weak
// Tracks the currently requested system fullscreen mode, used to show or
// hide the menubar. Its value is as follows:
// + |kFullScreenModeNormal| - when the window is not main or not fullscreen,
// + |kFullScreenModeHideDock| - when the user interacts with the top of the
// screen
// + |kFullScreenModeHideAll| - when the conditions don't meet the first two
// modes.
base::mac::FullScreenMode systemFullscreenMode_;
// True if the menubar should be shown on the screen.
BOOL isMenubarVisible_;
}
// Whether the current screen is expected to have a menubar, regardless of
// current visibility of the menubar.
- (BOOL)doesScreenHaveMenubar;
// Adjusts the AppKit Fullscreen options of the application.
- (void)setSystemFullscreenModeTo:(base::mac::FullScreenMode)mode;
// Sets |isMenubarVisible_|. If the value has changed, update the tracking
// area, the dock, and the menubar
- (void)setMenubarVisibility:(BOOL)visible;
// Methods that update and remove the tracking area.
- (void)updateTrackingArea;
- (void)removeTrackingArea;
@end
@implementation ImmersiveFullscreenController
- (instancetype)initWithBrowserController:(BrowserWindowController*)bwc {
if ((self = [super init])) {
browserController_ = bwc;
systemFullscreenMode_ = base::mac::kFullScreenModeNormal;
contentView_ = [[bwc window] contentView];
DCHECK(contentView_);
isMenubarVisible_ = NO;
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
NSWindow* window = [browserController_ window];
[nc addObserver:self
selector:@selector(windowDidBecomeMain:)
name:NSWindowDidBecomeMainNotification
object:window];
[nc addObserver:self
selector:@selector(windowDidResignMain:)
name:NSWindowDidResignMainNotification
object:window];
[self updateTrackingArea];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self removeTrackingArea];
[self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
[super dealloc];
}
- (void)updateMenuBarAndDockVisibility {
BOOL isMouseOnScreen =
NSMouseInRect([NSEvent mouseLocation],
[[browserController_ window] screen].frame, false);
if (!isMouseOnScreen || ![browserController_ isInImmersiveFullscreen])
[self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
else if ([self shouldShowMenubar])
[self setSystemFullscreenModeTo:base::mac::kFullScreenModeHideDock];
else
[self setSystemFullscreenModeTo:base::mac::kFullScreenModeHideAll];
}
- (BOOL)shouldShowMenubar {
return [self doesScreenHaveMenubar] && isMenubarVisible_;
}
- (void)windowDidBecomeMain:(NSNotification*)notification {
[self updateMenuBarAndDockVisibility];
}
- (void)windowDidResignMain:(NSNotification*)notification {
[self updateMenuBarAndDockVisibility];
}
- (BOOL)doesScreenHaveMenubar {
NSScreen* screen = [[browserController_ window] screen];
NSScreen* primaryScreen = [[NSScreen screens] firstObject];
BOOL isWindowOnPrimaryScreen = [screen isEqual:primaryScreen];
BOOL eachScreenShouldHaveMenuBar = [NSScreen screensHaveSeparateSpaces];
return eachScreenShouldHaveMenuBar ?: isWindowOnPrimaryScreen;
}
- (void)setSystemFullscreenModeTo:(base::mac::FullScreenMode)mode {
if (mode == systemFullscreenMode_)
return;
if (systemFullscreenMode_ == base::mac::kFullScreenModeNormal)
base::mac::RequestFullScreen(mode);
else if (mode == base::mac::kFullScreenModeNormal)
base::mac::ReleaseFullScreen(systemFullscreenMode_);
else
base::mac::SwitchFullScreenModes(systemFullscreenMode_, mode);
systemFullscreenMode_ = mode;
}
- (void)setMenubarVisibility:(BOOL)visible {
if (isMenubarVisible_ == visible)
return;
isMenubarVisible_ = visible;
[self updateTrackingArea];
[self updateMenuBarAndDockVisibility];
}
- (void)updateTrackingArea {
[self removeTrackingArea];
CGFloat trackingHeight =
isMenubarVisible_ ? kMenubarHideZoneHeight : kMenubarShowZoneHeight;
NSRect trackingFrame = [contentView_ bounds];
trackingFrame.origin.y = NSMaxY(trackingFrame) - trackingHeight;
trackingFrame.size.height = trackingHeight;
// If we replace the tracking area with a new one under the cursor, the new
// tracking area might not receive a |-mouseEntered:| or |-mouseExited| call.
// As a result, we should also track the mouse's movements so that the
// so the menubar won't get stuck.
NSTrackingAreaOptions options =
NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow;
if (isMenubarVisible_)
options |= NSTrackingMouseMoved;
// Create and add a new tracking area for |frame|.
trackingArea_.reset([[CrTrackingArea alloc] initWithRect:trackingFrame
options:options
owner:self
userInfo:nil]);
[contentView_ addTrackingArea:trackingArea_];
}
- (void)removeTrackingArea {
if (trackingArea_) {
[contentView_ removeTrackingArea:trackingArea_];
trackingArea_.reset();
}
}
- (void)mouseEntered:(NSEvent*)event {
[self setMenubarVisibility:YES];
}
- (void)mouseExited:(NSEvent*)event {
[self setMenubarVisibility:NO];
}
- (void)mouseMoved:(NSEvent*)event {
[self setMenubarVisibility:[trackingArea_
mouseInsideTrackingAreaForView:contentView_]];
}
@end
|