Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Unified Diff: chrome/browser/ui/cocoa/fullscreen_controller.mm

Issue 7566016: Fullscreen support for Lion. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/fullscreen_controller.mm
===================================================================
--- chrome/browser/ui/cocoa/fullscreen_controller.mm (revision 95517)
+++ chrome/browser/ui/cocoa/fullscreen_controller.mm (working copy)
@@ -1,634 +0,0 @@
-// Copyright (c) 2010 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_controller.h"
-
-#include <algorithm>
-
-#import "base/mac/mac_util.h"
-#import "chrome/browser/ui/cocoa/browser_window_controller.h"
-#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
-
-NSString* const kWillEnterFullscreenNotification =
- @"WillEnterFullscreenNotification";
-NSString* const kWillLeaveFullscreenNotification =
- @"WillLeaveFullscreenNotification";
-
-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 CGFloat kDropdownActivationZoneHeight = 4;
-const NSTimeInterval kDropdownAnimationDuration = 0.12;
-const NSTimeInterval kMouseExitCheckDelay = 0.1;
-// This show delay attempts to match the delay for the main menu.
-const NSTimeInterval kDropdownShowDelay = 0.3;
-const NSTimeInterval kDropdownHideDelay = 0.2;
-
-// The amount by which the floating bar is offset downwards (to avoid the menu)
-// in fullscreen mode. (We can't use |-[NSMenu menuBarHeight]| since it returns
-// 0 when the menu bar is hidden.)
-const CGFloat kFloatingBarVerticalOffset = 22;
-
-} // end namespace
-
-
-// Helper class to manage animations for the fullscreen dropdown bar. Calls
-// [FullscreenController changeFloatingBarShownFraction] once per animation
-// step.
-@interface DropdownAnimation : NSAnimation {
- @private
- FullscreenController* controller_;
- CGFloat startFraction_;
- CGFloat endFraction_;
-}
-
-@property(readonly, nonatomic) CGFloat startFraction;
-@property(readonly, nonatomic) CGFloat endFraction;
-
-// Designated initializer. Asks |controller| for the current shown fraction, so
-// if the bar is already partially shown or partially hidden, the animation
-// duration may be less than |fullDuration|.
-- (id)initWithFraction:(CGFloat)fromFraction
- fullDuration:(CGFloat)fullDuration
- animationCurve:(NSInteger)animationCurve
- controller:(FullscreenController*)controller;
-
-@end
-
-@implementation DropdownAnimation
-
-@synthesize startFraction = startFraction_;
-@synthesize endFraction = endFraction_;
-
-- (id)initWithFraction:(CGFloat)toFraction
- fullDuration:(CGFloat)fullDuration
- animationCurve:(NSInteger)animationCurve
- controller:(FullscreenController*)controller {
- // Calculate the effective duration, based on the current shown fraction.
- DCHECK(controller);
- CGFloat fromFraction = [controller floatingBarShownFraction];
- CGFloat effectiveDuration = fabs(fullDuration * (fromFraction - toFraction));
-
- if ((self = [super gtm_initWithDuration:effectiveDuration
- eventMask:NSLeftMouseDownMask
- animationCurve:animationCurve])) {
- startFraction_ = fromFraction;
- endFraction_ = toFraction;
- controller_ = controller;
- }
- return self;
-}
-
-// 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 =
- startFraction_ + (progress * (endFraction_ - startFraction_));
- [controller_ changeFloatingBarShownFraction:fraction];
-}
-
-@end
-
-
-@interface FullscreenController (PrivateMethods)
-
-// Returns YES if the fullscreen window is on the primary screen.
-- (BOOL)isWindowOnPrimaryScreen;
-
-// Returns YES if it is ok to show and hide the menu bar in response to the
-// overlay opening and closing. Will return NO if the window is not main or not
-// on the primary monitor.
-- (BOOL)shouldToggleMenuBar;
-
-// Returns |kFullScreenModeHideAll| when the overlay is hidden and
-// |kFullScreenModeHideDock| when the overlay is shown.
-- (base::mac::FullScreenMode)desiredFullscreenMode;
-
-// 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;
-
-// Schedule the floating bar to be shown/hidden because of mouse position.
-- (void)scheduleShowForMouse;
-- (void)scheduleHideForMouse;
-
-// Set up the tracking area used to activate the sliding bar or keep it active
-// using with the rectangle in |trackingAreaBounds_|, or remove the tracking
-// area if one was previously set up.
-- (void)setupTrackingArea;
-- (void)removeTrackingAreaIfNecessary;
-
-// Returns YES if the mouse is currently in any current tracking rectangle, NO
-// otherwise.
-- (BOOL)mouseInsideTrackingRect;
-
-// The tracking area can "falsely" report exits when the menu slides down over
-// it. In that case, we have to monitor for a "real" mouse exit on a timer.
-// |-setupMouseExitCheck| schedules a check; |-cancelMouseExitCheck| cancels any
-// scheduled check.
-- (void)setupMouseExitCheck;
-- (void)cancelMouseExitCheck;
-
-// Called (after a delay) by |-setupMouseExitCheck|, to check whether the mouse
-// has exited or not; if it hasn't, it will schedule another check.
-- (void)checkForMouseExit;
-
-// Start timers for showing/hiding the floating bar.
-- (void)startShowTimer;
-- (void)startHideTimer;
-- (void)cancelShowTimer;
-- (void)cancelHideTimer;
-- (void)cancelAllTimers;
-
-// Methods called when the show/hide timers fire. Do not call directly.
-- (void)showTimerFire:(NSTimer*)timer;
-- (void)hideTimerFire:(NSTimer*)timer;
-
-// Stops any running animations, removes tracking areas, etc.
-- (void)cleanup;
-
-// Shows and hides the UI associated with this window being active (having main
-// status). This includes hiding the menu bar and displaying the "Exit
-// Fullscreen" button. These functions are called when the window gains or
-// loses main status as well as in |-cleanup|.
-- (void)showActiveWindowUI;
-- (void)hideActiveWindowUI;
-
-@end
-
-
-@implementation FullscreenController
-
-@synthesize isFullscreen = isFullscreen_;
-
-- (id)initWithBrowserController:(BrowserWindowController*)controller {
- if ((self = [super init])) {
- browserController_ = controller;
- currentFullscreenMode_ = base::mac::kFullScreenModeNormal;
- }
-
- // Let the world know what we're up to.
- [[NSNotificationCenter defaultCenter]
- postNotificationName:kWillEnterFullscreenNotification
- object:nil];
-
- return self;
-}
-
-- (void)dealloc {
- DCHECK(!isFullscreen_);
- DCHECK(!trackingArea_);
- [super dealloc];
-}
-
-- (void)enterFullscreenForContentView:(NSView*)contentView
- showDropdown:(BOOL)showDropdown {
- DCHECK(!isFullscreen_);
- isFullscreen_ = YES;
- contentView_ = contentView;
- [self changeFloatingBarShownFraction:(showDropdown ? 1 : 0)];
-
- // Register for notifications. Self is removed as an observer in |-cleanup|.
- NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
- NSWindow* window = [browserController_ window];
- [nc addObserver:self
- selector:@selector(windowDidChangeScreen:)
- name:NSWindowDidChangeScreenNotification
- object:window];
-
- [nc addObserver:self
- selector:@selector(windowDidMove:)
- name:NSWindowDidMoveNotification
- object:window];
-
- [nc addObserver:self
- selector:@selector(windowDidBecomeMain:)
- name:NSWindowDidBecomeMainNotification
- object:window];
-
- [nc addObserver:self
- selector:@selector(windowDidResignMain:)
- name:NSWindowDidResignMainNotification
- object:window];
-}
-
-- (void)exitFullscreen {
- [[NSNotificationCenter defaultCenter]
- postNotificationName:kWillLeaveFullscreenNotification
- object:nil];
- DCHECK(isFullscreen_);
- [self cleanup];
- isFullscreen_ = NO;
-}
-
-- (void)windowDidChangeScreen:(NSNotification*)notification {
- [browserController_ resizeFullscreenWindow];
-}
-
-- (void)windowDidMove:(NSNotification*)notification {
- [browserController_ resizeFullscreenWindow];
-}
-
-- (void)windowDidBecomeMain:(NSNotification*)notification {
- [self showActiveWindowUI];
-}
-
-- (void)windowDidResignMain:(NSNotification*)notification {
- [self hideActiveWindowUI];
-}
-
-- (CGFloat)floatingBarVerticalOffset {
- return [self isWindowOnPrimaryScreen] ? kFloatingBarVerticalOffset : 0;
-}
-
-- (void)overlayFrameChanged:(NSRect)frame {
- if (!isFullscreen_)
- return;
-
- // Make sure |trackingAreaBounds_| always reflects either the tracking area or
- // the desired tracking area.
- trackingAreaBounds_ = frame;
- // The tracking area should always be at least the height of activation zone.
- NSRect contentBounds = [contentView_ bounds];
- trackingAreaBounds_.origin.y =
- std::min(trackingAreaBounds_.origin.y,
- NSMaxY(contentBounds) - kDropdownActivationZoneHeight);
- trackingAreaBounds_.size.height =
- NSMaxY(contentBounds) - trackingAreaBounds_.origin.y + 1;
-
- // If an animation is currently running, do not set up a tracking area now.
- // Instead, leave it to be created it in |-animationDidEnd:|.
- if (currentAnimation_)
- return;
-
- [self setupTrackingArea];
-}
-
-- (void)ensureOverlayShownWithAnimation:(BOOL)animate delay:(BOOL)delay {
- if (!isFullscreen_)
- return;
-
- if (animate) {
- if (delay) {
- [self startShowTimer];
- } else {
- [self cancelAllTimers];
- [self changeOverlayToFraction:1 withAnimation:YES];
- }
- } else {
- DCHECK(!delay);
- [self cancelAllTimers];
- [self changeOverlayToFraction:1 withAnimation:NO];
- }
-}
-
-- (void)ensureOverlayHiddenWithAnimation:(BOOL)animate delay:(BOOL)delay {
- if (!isFullscreen_)
- return;
-
- if (animate) {
- if (delay) {
- [self startHideTimer];
- } else {
- [self cancelAllTimers];
- [self changeOverlayToFraction:0 withAnimation:YES];
- }
- } else {
- DCHECK(!delay);
- [self cancelAllTimers];
- [self changeOverlayToFraction:0 withAnimation:NO];
- }
-}
-
-- (void)cancelAnimationAndTimers {
- [self cancelAllTimers];
- [currentAnimation_ stopAnimation];
- currentAnimation_.reset();
-}
-
-- (CGFloat)floatingBarShownFraction {
- return [browserController_ floatingBarShownFraction];
-}
-
-- (void)changeFloatingBarShownFraction:(CGFloat)fraction {
- [browserController_ setFloatingBarShownFraction:fraction];
-
- base::mac::FullScreenMode desiredMode = [self desiredFullscreenMode];
- if (desiredMode != currentFullscreenMode_ && [self shouldToggleMenuBar]) {
- if (currentFullscreenMode_ == base::mac::kFullScreenModeNormal)
- base::mac::RequestFullScreen(desiredMode);
- else
- base::mac::SwitchFullScreenModes(currentFullscreenMode_, desiredMode);
- currentFullscreenMode_ = desiredMode;
- }
-}
-
-// Used to activate the floating bar in fullscreen mode.
-- (void)mouseEntered:(NSEvent*)event {
- DCHECK(isFullscreen_);
-
- // Having gotten a mouse entered, we no longer need to do exit checks.
- [self cancelMouseExitCheck];
-
- NSTrackingArea* trackingArea = [event trackingArea];
- if (trackingArea == trackingArea_) {
- // The tracking area shouldn't be active during animation.
- DCHECK(!currentAnimation_);
- [self scheduleShowForMouse];
- }
-}
-
-// Used to deactivate the floating bar in fullscreen mode.
-- (void)mouseExited:(NSEvent*)event {
- DCHECK(isFullscreen_);
-
- NSTrackingArea* trackingArea = [event trackingArea];
- if (trackingArea == trackingArea_) {
- // The tracking area shouldn't be active during animation.
- DCHECK(!currentAnimation_);
-
- // We can get a false mouse exit when the menu slides down, so if the mouse
- // is still actually over the tracking area, we ignore the mouse exit, but
- // we set up to check the mouse position again after a delay.
- if ([self mouseInsideTrackingRect]) {
- [self setupMouseExitCheck];
- return;
- }
-
- [self scheduleHideForMouse];
- }
-}
-
-- (void)animationDidStop:(NSAnimation*)animation {
- // Reset the |currentAnimation_| pointer now that the animation is over.
- currentAnimation_.reset();
-
- // Invariant says that the tracking area is not installed while animations are
- // in progress. Ensure this is true.
- DCHECK(!trackingArea_);
- [self removeTrackingAreaIfNecessary]; // For paranoia.
-
- // Don't automatically set up a new tracking area. When explicitly stopped,
- // either another animation is going to start immediately or the state will be
- // changed immediately.
-}
-
-- (void)animationDidEnd:(NSAnimation*)animation {
- [self animationDidStop:animation];
-
- // |trackingAreaBounds_| contains the correct tracking area bounds, including
- // |any updates that may have come while the animation was running. Install a
- // new tracking area with these bounds.
- [self setupTrackingArea];
-
- // TODO(viettrungluu): Better would be to check during the animation; doing it
- // here means that the timing is slightly off.
- if (![self mouseInsideTrackingRect])
- [self scheduleHideForMouse];
-}
-
-@end
-
-
-@implementation FullscreenController (PrivateMethods)
-
-- (BOOL)isWindowOnPrimaryScreen {
- NSScreen* screen = [[browserController_ window] screen];
- NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0];
- return (screen == primaryScreen);
-}
-
-- (BOOL)shouldToggleMenuBar {
- return [self isWindowOnPrimaryScreen] &&
- [[browserController_ window] isMainWindow];
-}
-
-- (base::mac::FullScreenMode)desiredFullscreenMode {
- if ([browserController_ floatingBarShownFraction] >= 1.0)
- return base::mac::kFullScreenModeHideDock;
- 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 changeFloatingBarShownFraction:fraction];
- return;
- }
-
- // If we're already animating to the given fraction, then there's nothing more
- // to do.
- if (currentAnimation_ && [currentAnimation_ endFraction] == fraction)
- return;
-
- // In all other cases, we want to cancel any running animation (which may be
- // to show or to hide).
- [currentAnimation_ stopAnimation];
-
- // Now, if it happens to already be in the right state, there's nothing more
- // to do.
- if ([browserController_ floatingBarShownFraction] == fraction)
- return;
-
- // Create the animation and set it up.
- currentAnimation_.reset(
- [[DropdownAnimation alloc] initWithFraction:fraction
- fullDuration:kDropdownAnimationDuration
- animationCurve:NSAnimationEaseOut
- controller:self]);
- DCHECK(currentAnimation_);
- [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)scheduleShowForMouse {
- [browserController_ lockBarVisibilityForOwner:self
- withAnimation:YES
- delay:YES];
-}
-
-- (void)scheduleHideForMouse {
- [browserController_ releaseBarVisibilityForOwner:self
- withAnimation:YES
- delay:YES];
-}
-
-- (void)setupTrackingArea {
- if (trackingArea_) {
- // If the tracking rectangle is already |trackingAreaBounds_|, quit early.
- NSRect oldRect = [trackingArea_ rect];
- if (NSEqualRects(trackingAreaBounds_, oldRect))
- return;
-
- // Otherwise, remove it.
- [self removeTrackingAreaIfNecessary];
- }
-
- // Create and add a new tracking area for |frame|.
- trackingArea_.reset(
- [[NSTrackingArea alloc] initWithRect:trackingAreaBounds_
- 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)mouseInsideTrackingRect {
- NSWindow* window = [browserController_ window];
- NSPoint mouseLoc = [window mouseLocationOutsideOfEventStream];
- NSPoint mousePos = [contentView_ convertPoint:mouseLoc fromView:nil];
- return NSMouseInRect(mousePos, trackingAreaBounds_, [contentView_ isFlipped]);
-}
-
-- (void)setupMouseExitCheck {
- [self performSelector:@selector(checkForMouseExit)
- withObject:nil
- afterDelay:kMouseExitCheckDelay];
-}
-
-- (void)cancelMouseExitCheck {
- [NSObject cancelPreviousPerformRequestsWithTarget:self
- selector:@selector(checkForMouseExit) object:nil];
-}
-
-- (void)checkForMouseExit {
- if ([self mouseInsideTrackingRect])
- [self setupMouseExitCheck];
- else
- [self scheduleHideForMouse];
-}
-
-- (void)startShowTimer {
- // If there's already a show timer going, just keep it.
- if (showTimer_) {
- DCHECK([showTimer_ isValid]);
- DCHECK(!hideTimer_);
- return;
- }
-
- // Cancel the hide timer (if necessary) and set up the new show timer.
- [self cancelHideTimer];
- showTimer_.reset(
- [[NSTimer scheduledTimerWithTimeInterval:kDropdownShowDelay
- target:self
- selector:@selector(showTimerFire:)
- userInfo:nil
- repeats:NO] retain]);
- DCHECK([showTimer_ isValid]); // This also checks that |showTimer_ != nil|.
-}
-
-- (void)startHideTimer {
- // If there's already a hide timer going, just keep it.
- if (hideTimer_) {
- DCHECK([hideTimer_ isValid]);
- DCHECK(!showTimer_);
- return;
- }
-
- // Cancel the show timer (if necessary) and set up the new hide timer.
- [self cancelShowTimer];
- hideTimer_.reset(
- [[NSTimer scheduledTimerWithTimeInterval:kDropdownHideDelay
- target:self
- selector:@selector(hideTimerFire:)
- userInfo:nil
- repeats:NO] retain]);
- DCHECK([hideTimer_ isValid]); // This also checks that |hideTimer_ != nil|.
-}
-
-- (void)cancelShowTimer {
- [showTimer_ invalidate];
- showTimer_.reset();
-}
-
-- (void)cancelHideTimer {
- [hideTimer_ invalidate];
- hideTimer_.reset();
-}
-
-- (void)cancelAllTimers {
- [self cancelShowTimer];
- [self cancelHideTimer];
-}
-
-- (void)showTimerFire:(NSTimer*)timer {
- DCHECK_EQ(showTimer_, timer); // This better be our show timer.
- [showTimer_ invalidate]; // Make sure it doesn't repeat.
- showTimer_.reset(); // And get rid of it.
- [self changeOverlayToFraction:1 withAnimation:YES];
-}
-
-- (void)hideTimerFire:(NSTimer*)timer {
- 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];
-}
-
-- (void)cleanup {
- [self cancelMouseExitCheck];
- [self cancelAnimationAndTimers];
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [self removeTrackingAreaIfNecessary];
- contentView_ = nil;
-
- // This isn't tracked when not in fullscreen mode.
- [browserController_ releaseBarVisibilityForOwner:self
- withAnimation:NO
- delay:NO];
-
- // Call the main status resignation code to perform the associated cleanup,
- // since we will no longer be receiving actual status resignation
- // notifications.
- [self hideActiveWindowUI];
-
- // No more calls back up to the BWC.
- browserController_ = nil;
-}
-
-- (void)showActiveWindowUI {
- DCHECK_EQ(currentFullscreenMode_, base::mac::kFullScreenModeNormal);
- if (currentFullscreenMode_ != base::mac::kFullScreenModeNormal)
- return;
-
- if ([self shouldToggleMenuBar]) {
- base::mac::FullScreenMode desiredMode = [self desiredFullscreenMode];
- base::mac::RequestFullScreen(desiredMode);
- currentFullscreenMode_ = desiredMode;
- }
-
- // TODO(rohitrao): Insert the Exit Fullscreen button. http://crbug.com/35956
-}
-
-- (void)hideActiveWindowUI {
- if (currentFullscreenMode_ != base::mac::kFullScreenModeNormal) {
- base::mac::ReleaseFullScreen(currentFullscreenMode_);
- currentFullscreenMode_ = base::mac::kFullScreenModeNormal;
- }
-
- // TODO(rohitrao): Remove the Exit Fullscreen button. http://crbug.com/35956
-}
-
-@end

Powered by Google App Engine
This is Rietveld 408576698