diff options
-rw-r--r-- | chrome/browser/cocoa/bookmark_bar_controller.h | 8 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.h | 25 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 370 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller_unittest.mm | 37 | ||||
-rw-r--r-- | chrome/browser/cocoa/floating_bar_backing_view.h | 14 | ||||
-rw-r--r-- | chrome/browser/cocoa/floating_bar_backing_view.mm | 65 | ||||
-rw-r--r-- | chrome/browser/cocoa/floating_bar_backing_view_unittest.mm | 26 | ||||
-rw-r--r-- | chrome/browser/cocoa/fullscreen_controller.h | 71 | ||||
-rw-r--r-- | chrome/browser/cocoa/fullscreen_controller.mm | 346 | ||||
-rw-r--r-- | chrome/browser/cocoa/location_bar_view_mac.h | 7 | ||||
-rw-r--r-- | chrome/browser/cocoa/location_bar_view_mac.mm | 21 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 4 | ||||
-rwxr-xr-x | chrome/chrome_tests.gypi | 1 |
13 files changed, 887 insertions, 108 deletions
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.h b/chrome/browser/cocoa/bookmark_bar_controller.h index 8ab0e8c..b68b19f 100644 --- a/chrome/browser/cocoa/bookmark_bar_controller.h +++ b/chrome/browser/cocoa/bookmark_bar_controller.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -150,9 +150,9 @@ willAnimateFromState:(bookmarks::VisualState)oldState // Update the visible state of the bookmark bar. - (void)updateVisibility; -// Turn on or off the bookmark bar and prevent or reallow its -// appearance. On disable, toggle off if shown. On enable, show only -// if needed. For fullscreen mode. +// Turn on or off the bookmark bar and prevent or reallow its appearance. On +// disable, toggle off if shown. On enable, show only if needed. App and popup +// windows do not show a bookmark bar. - (void)setBookmarkBarEnabled:(BOOL)enabled; // Returns the amount by which the toolbar above should be compressed. diff --git a/chrome/browser/cocoa/browser_window_controller.h b/chrome/browser/cocoa/browser_window_controller.h index 9a15e39..4044e5f 100644 --- a/chrome/browser/cocoa/browser_window_controller.h +++ b/chrome/browser/cocoa/browser_window_controller.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -30,6 +30,7 @@ class BrowserWindowCocoa; class ConstrainedWindowMac; @class DownloadShelfController; @class FindBarCocoaController; +@class FullscreenController; @class GTMWindowSheetController; @class InfoBarContainerController; class LocationBar; @@ -63,6 +64,7 @@ class TabStripModelObserverBridge; scoped_nsobject<InfoBarContainerController> infoBarContainerController_; scoped_nsobject<DownloadShelfController> downloadShelfController_; scoped_nsobject<BookmarkBarController> bookmarkBarController_; + scoped_nsobject<FullscreenController> fullscreenController_; // Strong. StatusBubble is a special case of a strong reference that // we don't wrap in a scoped_ptr because it is acting the same @@ -92,6 +94,17 @@ class TabStripModelObserverBridge; // an in-progress pinch gesture. CGFloat totalMagnifyGestureAmount_; NSInteger currentZoomStepDelta_; + + // Lazily created view which draws the background for the floating set of bars + // in fullscreen mode. + scoped_nsobject<NSView> floatingBarBackingView_; + + // Tracks whether the floating bar is above or below the bookmark bar, in + // terms of z-order. + BOOL floatingBarAboveBookmarkBar_; + + // The proportion of the floating bar which is shown (in fullscreen mode). + CGFloat floatingBarShownFraction_; } // Load the browser window nib and do any Cocoa-specific initialization. @@ -189,6 +202,14 @@ class TabStripModelObserverBridge; // Returns fullscreen state. - (BOOL)isFullscreen; +// Gets or sets the fraction of the floating bar (fullscreen overlay) that is +// shown. 0 is completely hidden, 1 is fully shown. +- (CGFloat)floatingBarShownFraction; +- (void)setFloatingBarShownFraction:(CGFloat)fraction; + +// Returns YES if any of the views in the floating bar currently has focus. +- (BOOL)floatingBarHasFocus; + // The user changed the theme. - (void)userChangedTheme; @@ -240,7 +261,7 @@ class TabStripModelObserverBridge; - (void)adjustWindowHeightBy:(CGFloat)deltaH; // Return an autoreleased NSWindow suitable for fullscreen use. -- (NSWindow*)fullscreenWindow; +- (NSWindow*)createFullscreenWindow; // Return a point suitable for the topLeft for a bookmark bubble. - (NSPoint)topLeftForBubble; diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index aeb5cb6..6dda638 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -1,7 +1,9 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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/cocoa/browser_window_controller.h" + #include <Carbon/Carbon.h> #include "app/l10n_util_mac.h" @@ -24,17 +26,20 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/tabs/tab_strip_model.h" +#import "chrome/browser/cocoa/autocomplete_text_field_editor.h" #import "chrome/browser/cocoa/background_gradient_view.h" #import "chrome/browser/cocoa/bookmark_bar_controller.h" #import "chrome/browser/cocoa/bookmark_editor_controller.h" #import "chrome/browser/cocoa/browser_window_cocoa.h" -#import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/chrome_browser_window.h" #import "chrome/browser/cocoa/download_shelf_controller.h" #import "chrome/browser/cocoa/event_utils.h" #import "chrome/browser/cocoa/fast_resize_view.h" #import "chrome/browser/cocoa/find_bar_cocoa_controller.h" #include "chrome/browser/cocoa/find_bar_bridge.h" +#import "chrome/browser/cocoa/floating_bar_backing_view.h" +#import "chrome/browser/cocoa/focus_tracker.h" +#import "chrome/browser/cocoa/fullscreen_controller.h" #import "chrome/browser/cocoa/fullscreen_window.h" #import "chrome/browser/cocoa/infobar_container_controller.h" #import "chrome/browser/cocoa/sad_tab_controller.h" @@ -114,6 +119,15 @@ // longer indicate that the window is shrinking from an apparent zoomed state) // and if it's set we continue to constrain the resize. +namespace { +// Insets for the location bar, used when the full toolbar is hidden. +// TODO(viettrungluu): We can argue about the "correct" insetting; I like the +// following best, though arguably 0 inset is better/more correct. +const CGFloat kLocBarLeftRightInset = 1; +const CGFloat kLocBarTopInset = 0; +const CGFloat kLocBarBottomInset = 1; +} // end namespace + @interface GTMTheme (BrowserThemeProviderInitialization) + (GTMTheme*)themeWithBrowserThemeProvider:(BrowserThemeProvider*)provider isOffTheRecord:(BOOL)offTheRecord; @@ -129,7 +143,6 @@ @end - @interface BrowserWindowController(Private) // Saves the window's position in the local state preferences. @@ -152,11 +165,26 @@ willPositionSheet:(NSWindow*)sheet // content area, download shelf (if any). - (void)layoutSubviews; +// Find the total height of the floating bar (in fullscreen mode). Safe to call +// even when not in fullscreen mode. +- (CGFloat)floatingBarHeight; + +// Lays out the tab strip at the given maximum y-coordinate, with the given +// width, possibly for fullscreen mode; returns the new maximum y (below the tab +// strip). This is safe to call even when there is no tab strip. +- (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY + width:(CGFloat)width + fullscreen:(BOOL)fullscreen; + // Lays out the toolbar (or just location bar for popups) at the given maximum // y-coordinate, with the given width; returns the new maximum y (below the // toolbar). - (CGFloat)layoutToolbarAtMaxY:(CGFloat)maxY width:(CGFloat)width; +// Returns YES if the bookmark bar should be placed below the infobar, NO +// otherwise. +- (BOOL)placeBookmarkBarBelowInfoBar; + // Lays out the bookmark bar at the given maximum y-coordinate, with the given // width; returns the new maximum y (below the bookmark bar). Note that one must // call it with the appropriate |maxY| which depends on whether or not the @@ -164,14 +192,19 @@ willPositionSheet:(NSWindow*)sheet // |-placeBookmarkBarBelowInfoBar|). - (CGFloat)layoutBookmarkBarAtMaxY:(CGFloat)maxY width:(CGFloat)width; +// Lay out the view which draws the background for the floating bar when in +// fullscreen mode, with the given (minimum) y-coordinate, width, height, and +// fullscreen-mode-status. Should be called even when not in fullscreen mode to +// hide the backing view. +- (void)layoutFloatingBarBackingViewAtY:(CGFloat)y + width:(CGFloat)width + height:(CGFloat)height + fullscreen:(BOOL)fullscreen; + // Lays out the infobar at the given maximum y-coordinate, with the given width; // returns the new maximum y (below the infobar). - (CGFloat)layoutInfoBarAtMaxY:(CGFloat)maxY width:(CGFloat)width; -// Returns YES if the bookmark bar should be placed below the infobar, NO -// otherwise. -- (BOOL)placeBookmarkBarBelowInfoBar; - // Lays out the download shelf, if there is one, at the given minimum // y-coordinate, with the given width; returns the new minimum y (above the // download shelf). This is safe to call even if there is no download shelf. @@ -195,6 +228,9 @@ willPositionSheet:(NSWindow*)sheet // keep the total height of the two views constant. - (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression; +// Adjust the UI when entering or leaving fullscreen mode. +- (void)adjustUIForFullscreen:(BOOL)fullscreen; + @end // IncognitoImageView subclasses NSImageView to allow mouse events to pass @@ -267,6 +303,7 @@ willPositionSheet:(NSWindow*)sheet // Puts the incognito badge on the window frame, if necessary. Do this // before creating the tab strip to avoid redundant tab layout. + // TODO(viettrungluu): fullscreen mode [self installIncognitoBadge]; // Create a controller for the tab strip, giving it the model object for @@ -453,12 +490,20 @@ willPositionSheet:(NSWindow*)sheet // TODO(dmaclach): Instead of redrawing the whole window, views that care // about the active window state should be registering for notifications. [[self window] setViewsNeedDisplay:YES]; + + // TODO(viettrungluu): For some reason, the above doesn't suffice. + if ([self isFullscreen]) + [floatingBarBackingView_ setNeedsDisplay:YES]; } - (void)windowDidResignMain:(NSNotification*)notification { // TODO(dmaclach): Instead of redrawing the whole window, views that care // about the active window state should be registering for notifications. [[self window] setViewsNeedDisplay:YES]; + + // TODO(viettrungluu): For some reason, the above doesn't suffice. + if ([self isFullscreen]) + [floatingBarBackingView_ setNeedsDisplay:YES]; } // Called when we are activated (when we gain focus). @@ -841,9 +886,6 @@ willPositionSheet:(NSWindow*)sheet } - (BOOL)supportsFullscreen { - // Fullscreen mode disabled for Mstone-4 / ReleaseBlock-Beta. - return NO; - // TODO(avi, thakis): GTMWindowSheetController has no api to move // tabsheets between windows. Until then, we have to prevent having to // move a tabsheet between windows, e.g. no fullscreen toggling @@ -1197,6 +1239,14 @@ willPositionSheet:(NSWindow*)sheet return [tabStripController_ tabDraggingAllowed]; } +- (BOOL)tabTearingAllowed { + return ![self isFullscreen]; +} + +- (BOOL)windowMovementAllowed { + return ![self isFullscreen]; +} + - (BOOL)isTabFullyVisible:(TabView*)tab { return [tabStripController_ isTabFullyVisible:tab]; } @@ -1244,75 +1294,98 @@ willPositionSheet:(NSWindow*)sheet positionFindBarView:[infoBarContainerController_ view]]; } -// Adjust the UI for fullscreen mode. E.g. when going fullscreen, -// remove the toolbar. When stopping fullscreen, add it back in. -- (void)adjustUIForFullscreen:(BOOL)fullscreen { - if (fullscreen) { - // Disable showing of the bookmark bar. This does not toggle the - // preference. - [bookmarkBarController_ setBookmarkBarEnabled:NO]; - // Make room for more content area. - [[toolbarController_ view] removeFromSuperview]; - // Hide the menubar, and allow it to un-hide when moving the mouse - // to the top of the screen. Does this eliminate the need for an - // info bubble describing how to exit fullscreen mode? - mac_util::RequestFullScreen(); - } else { - mac_util::ReleaseFullScreen(); - [[[self window] contentView] addSubview:[toolbarController_ view]]; - [bookmarkBarController_ setBookmarkBarEnabled:[self supportsBookmarkBar]]; - } - - // Force a relayout. - [self layoutSubviews]; -} - -- (NSWindow*)fullscreenWindow { +- (NSWindow*)createFullscreenWindow { return [[[FullscreenWindow alloc] initForScreen:[[self window] screen]] autorelease]; } - (void)setFullscreen:(BOOL)fullscreen { + // The logic in this function is a bit complicated and very carefully + // arranged. See the below comments for more details. + + if (fullscreen == [self isFullscreen]) + return; + if (![self supportsFullscreen]) - return; + return; + // Save the current first responder so we can restore after views are moved. NSWindow* window = [self window]; + scoped_nsobject<FocusTracker> focusTracker( + [[FocusTracker alloc] initWithWindow:window]); + BOOL showDropdown = [self floatingBarHasFocus]; - // Retain the contentView while we remove it from its superview. - scoped_nsobject<NSView> content([[window contentView] retain]); + // If we're entering fullscreen, create the fullscreen controller. If we're + // exiting fullscreen, kill the controller. + if (fullscreen) { + fullscreenController_.reset([[FullscreenController alloc] + initWithBrowserController:self]); + } else { + [fullscreenController_ exitFullscreen]; + fullscreenController_.reset(nil); + } + + // Retain the tab strip view while we remove it from its superview. + scoped_nsobject<NSView> tabStripView; + if ([self hasTabStrip]) { + tabStripView.reset([[self tabStripView] retain]); + [tabStripView removeFromSuperview]; + } - // Disable autoresizing of subviews while we move views around. This - // prevents spurious renderer resizes. - [content setAutoresizesSubviews:NO]; - [content removeFromSuperview]; + // Ditto for the content view. + scoped_nsobject<NSView> contentView([[window contentView] retain]); + // Disable autoresizing of subviews while we move views around. This prevents + // spurious renderer resizes. + [contentView setAutoresizesSubviews:NO]; + [contentView removeFromSuperview]; - NSWindow* dstWindow = nil; + NSWindow* destWindow = nil; if (fullscreen) { DCHECK(!savedRegularWindow_); savedRegularWindow_ = [window retain]; - dstWindow = [self fullscreenWindow]; + destWindow = [self createFullscreenWindow]; } else { DCHECK(savedRegularWindow_); - dstWindow = [savedRegularWindow_ autorelease]; + destWindow = [savedRegularWindow_ autorelease]; savedRegularWindow_ = nil; } + DCHECK(destWindow); - // With this call, valgrind yells at me about "Conditional jump or - // move depends on uninitialised value(s)". The error happens in - // -[NSThemeFrame drawOverlayRect:]. I'm pretty convinced this is - // an Apple bug, but there is no visual impact. I have been - // unable to tickle it away with other window or view manipulation - // Cocoa calls. Stack added to suppressions_mac.txt. - [content setAutoresizesSubviews:YES]; - [dstWindow setContentView:content]; - [window setWindowController:nil]; + // Have to do this here, otherwise later calls can crash because the window + // has no delegate. [window setDelegate:nil]; - [self setWindow:dstWindow]; - [dstWindow setWindowController:self]; - [dstWindow setDelegate:self]; + [destWindow setDelegate:self]; + + // With this call, valgrind complains that a "Conditional jump or move depends + // on uninitialised value(s)". The error happens in -[NSThemeFrame + // drawOverlayRect:]. I'm pretty convinced this is an Apple bug, but there is + // no visual impact. I have been unable to tickle it away with other window + // or view manipulation Cocoa calls. Stack added to suppressions_mac.txt. + [contentView setAutoresizesSubviews:YES]; + [destWindow setContentView:contentView]; + + // Add the tabstrip after setting the content view, so that the tab strip will + // be on top (in the z-order). + if ([self hasTabStrip]) + [[[destWindow contentView] superview] addSubview:tabStripView]; + + [window setWindowController:nil]; + [self setWindow:destWindow]; + [destWindow setWindowController:self]; [self adjustUIForFullscreen:fullscreen]; - [dstWindow makeKeyAndOrderFront:self]; + // When entering fullscreen mode, the controller forces a layout for us. When + // exiting, we need to call layoutSubviews manually. + if (fullscreen) { + [fullscreenController_ enterFullscreenForContentView:contentView + showDropdown:showDropdown]; + } else { + [self layoutSubviews]; + } + + // The window needs to be onscreen before we can set its first responder. + [destWindow makeKeyAndOrderFront:self]; + [focusTracker restoreFocusInWindow:destWindow]; [window orderOut:self]; } @@ -1320,6 +1393,20 @@ willPositionSheet:(NSWindow*)sheet return savedRegularWindow_ != nil; } +- (CGFloat)floatingBarShownFraction { + return floatingBarShownFraction_; +} + +- (void)setFloatingBarShownFraction:(CGFloat)fraction { + floatingBarShownFraction_ = fraction; + [self layoutSubviews]; +} + +- (BOOL)floatingBarHasFocus { + NSResponder* focused = [[self window] firstResponder]; + return [focused isKindOfClass:[AutocompleteTextFieldEditor class]]; +} + - (NSInteger)numberOfTabs { return browser_->tabstrip_model()->count(); } @@ -1372,6 +1459,8 @@ willPositionSheet:(NSWindow*)sheet windowShim_->UpdateTitleBar(); // Update the bookmark bar. + // TODO(viettrungluu): perhaps update to not terminate running animations (if + // applicable)? [self updateBookmarkBarVisibilityWithAnimation:NO]; } @@ -1701,16 +1790,20 @@ willAnimateFromState:(bookmarks::VisualState)oldState } - (void)saveWindowPositionToPrefs:(PrefService*)prefs { + // If we're in fullscreen mode, save the position of the regular window + // instead. + NSWindow* window = [self isFullscreen] ? savedRegularWindow_ : [self window]; + // Window positions are stored relative to the origin of the primary monitor. NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame]; // Start with the window's frame, which is in virtual coordinates. // Do some y twiddling to flip the coordinate system. - gfx::Rect bounds(NSRectToCGRect([[self window] frame])); + gfx::Rect bounds(NSRectToCGRect([window frame])); bounds.set_y(monitorFrame.size.height - bounds.y() - bounds.height()); // We also need to save the current work area, in flipped coordinates. - gfx::Rect workArea(NSRectToCGRect([[[self window] screen] visibleFrame])); + gfx::Rect workArea(NSRectToCGRect([[window screen] visibleFrame])); workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height()); DictionaryValue* windowPreferences = prefs->GetMutableDictionary( @@ -1771,20 +1864,38 @@ willPositionSheet:(NSWindow*)sheet } - (void)layoutSubviews { + // With the exception of the tab strip, the subviews which we lay out are + // subviews of the content view, so we mainly work in the content view's + // coordinate system. Note, however, that the content view's coordinate system + // and the window's base coordinate system should coincide. NSWindow* window = [self window]; NSView* contentView = [window contentView]; - NSRect contentFrame = [contentView frame]; - CGFloat maxY = NSMaxY(contentFrame); - CGFloat minY = NSMinY(contentFrame); - CGFloat width = NSWidth(contentFrame); - if ([self hasTabStrip]) - maxY = NSMinY([[self tabStripView] frame]); - DCHECK_GE(maxY, minY); + NSRect contentBounds = [contentView bounds]; + CGFloat minY = NSMinY(contentBounds); + CGFloat width = NSWidth(contentBounds); // Suppress title drawing if necessary. if ([window respondsToSelector:@selector(setShouldHideTitle:)]) [(id)window setShouldHideTitle:![self hasTitleBar]]; + BOOL isFullscreen = [self isFullscreen]; + CGFloat floatingBarHeight = [self floatingBarHeight]; + CGFloat yOffset = floor( + isFullscreen ? (1 - floatingBarShownFraction_) * floatingBarHeight : 0); + CGFloat maxY = NSMaxY(contentBounds) + yOffset; + CGFloat startMaxY = maxY; + + if ([self hasTabStrip]) { + // If we need to lay out the tab strip, replace |maxY| and |startMaxY| with + // higher values, and then lay out the tab strip. + startMaxY = maxY = NSHeight([window frame]) + yOffset; + maxY = [self layoutTabStripAtMaxY:maxY width:width fullscreen:isFullscreen]; + } + + // Sanity-check |maxY|. + DCHECK_GE(maxY, minY); + DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset); + // Place the toolbar at the top of the reserved area. maxY = [self layoutToolbarAtMaxY:maxY width:width]; @@ -1794,6 +1905,19 @@ willPositionSheet:(NSWindow*)sheet if (!placeBookmarkBarBelowInfoBar) maxY = [self layoutBookmarkBarAtMaxY:maxY width:width]; + // The floating bar backing view doesn't actually add any height. + [self layoutFloatingBarBackingViewAtY:maxY + width:width + height:floatingBarHeight + fullscreen:isFullscreen]; + + [fullscreenController_ overlayFrameChanged:[floatingBarBackingView_ frame]]; + + // If in fullscreen mode, reset |maxY| to top of screen, so that the floating + // bar slides over the things which appear to be in the content area. + if (isFullscreen) + maxY = NSMaxY(contentBounds); + // Place the infobar container in place below the toolbar. maxY = [self layoutInfoBarAtMaxY:maxY width:width]; @@ -1820,6 +1944,51 @@ willPositionSheet:(NSWindow*)sheet setDividerOpacity:[bookmarkBarController_ toolbarDividerOpacity]]; } +- (CGFloat)floatingBarHeight { + if (![self isFullscreen]) + return 0; + + CGFloat totalHeight = 0; + + if ([self hasTabStrip]) + totalHeight += NSHeight([[self tabStripView] frame]); + + if ([self hasToolbar]) { + totalHeight += NSHeight([[toolbarController_ view] frame]); + } else if ([self hasLocationBar]) { + totalHeight += NSHeight([[toolbarController_ view] frame]) + + kLocBarTopInset + kLocBarBottomInset; + } + + if (![self placeBookmarkBarBelowInfoBar]) + totalHeight += NSHeight([[bookmarkBarController_ view] frame]); + + return totalHeight; +} + +- (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY + width:(CGFloat)width + fullscreen:(BOOL)fullscreen { + // Nothing to do if no tab strip. + if (![self hasTabStrip]) + return maxY; + + NSView* tabStripView = [self tabStripView]; + CGFloat tabStripHeight = NSHeight([tabStripView frame]); + maxY -= tabStripHeight; + [tabStripView setFrame:NSMakeRect(0, maxY, width, tabStripHeight)]; + + // Set indentation. + [tabStripController_ setIndentForControls:(fullscreen ? 0 : + [[tabStripController_ class] defaultIndentForControls])]; + + // TODO(viettrungluu): Seems kind of bad -- shouldn't |-layoutSubviews| do + // this? Moreover, |-layoutTabs| will try to animate.... + [tabStripController_ layoutTabs]; + + return maxY; +} + - (CGFloat)layoutToolbarAtMaxY:(CGFloat)maxY width:(CGFloat)width { NSView* toolbarView = [toolbarController_ view]; NSRect toolbarFrame = [toolbarView frame]; @@ -1838,11 +2007,6 @@ willPositionSheet:(NSWindow*)sheet // really be aware of what its height should be (the way the toolbar // compression stuff is currently set up messes things up). DCHECK(![toolbarView isHidden]); - // TODO(viettrungluu): We can argue about the "correct" insetting; I like - // the following best, though arguably 0 inset is better/more correct. - const CGFloat kLocBarLeftRightInset = 1; - const CGFloat kLocBarTopInset = 0; - const CGFloat kLocBarBottomInset = 1; toolbarFrame.origin.x = kLocBarLeftRightInset; toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame) - kLocBarTopInset; toolbarFrame.size.width = width - 2 * kLocBarLeftRightInset; @@ -1855,6 +2019,15 @@ willPositionSheet:(NSWindow*)sheet return maxY; } +- (BOOL)placeBookmarkBarBelowInfoBar { + // If we are currently displaying the NTP detached bookmark bar or animating + // to/from it (from/to anything else), we display the bookmark bar below the + // infobar. + return [bookmarkBarController_ isInState:bookmarks::kDetachedState] || + [bookmarkBarController_ isAnimatingToState:bookmarks::kDetachedState] || + [bookmarkBarController_ isAnimatingFromState:bookmarks::kDetachedState]; +} + - (CGFloat)layoutBookmarkBarAtMaxY:(CGFloat)maxY width:(CGFloat)width { NSView* bookmarkBarView = [bookmarkBarController_ view]; NSRect bookmarkBarFrame = [bookmarkBarView frame]; @@ -1874,6 +2047,38 @@ willPositionSheet:(NSWindow*)sheet return maxY; } +- (void)layoutFloatingBarBackingViewAtY:(CGFloat)y + width:(CGFloat)width + height:(CGFloat)height + fullscreen:(BOOL)fullscreen { + // Only display when in fullscreen mode. + if (fullscreen) { + DCHECK(floatingBarBackingView_.get()); + BOOL aboveBookmarkBar = [self placeBookmarkBarBelowInfoBar]; + + // Insert it into the view hierarchy if necessary. + if (![floatingBarBackingView_ superview] || + aboveBookmarkBar != floatingBarAboveBookmarkBar_) { + NSView* contentView = [[self window] contentView]; + // z-order gets messed up unless we explicitly remove the floatingbar view + // and re-add it. + [floatingBarBackingView_ removeFromSuperview]; + [contentView addSubview:floatingBarBackingView_ + positioned:(aboveBookmarkBar ? + NSWindowAbove : NSWindowBelow) + relativeTo:[bookmarkBarController_ view]]; + floatingBarAboveBookmarkBar_ = aboveBookmarkBar; + } + + // Set its frame. + [floatingBarBackingView_ setFrame:NSMakeRect(0, y, width, height)]; + } else { + // Okay to call even if |floatingBarBackingView_| is nil. + if ([floatingBarBackingView_ superview]) + [floatingBarBackingView_ removeFromSuperview]; + } +} + - (CGFloat)layoutInfoBarAtMaxY:(CGFloat)maxY width:(CGFloat)width { NSView* infoBarView = [infoBarContainerController_ view]; NSRect infoBarFrame = [infoBarView frame]; @@ -1884,15 +2089,6 @@ willPositionSheet:(NSWindow*)sheet return maxY; } -- (BOOL)placeBookmarkBarBelowInfoBar { - // If we are currently displaying the NTP detached bookmark bar or animating - // to/from it (from/to anything else), we display the bookmark bar below the - // infobar. - return [bookmarkBarController_ isInState:bookmarks::kDetachedState] || - [bookmarkBarController_ isAnimatingToState:bookmarks::kDetachedState] || - [bookmarkBarController_ isAnimatingFromState:bookmarks::kDetachedState]; -} - - (CGFloat)layoutDownloadShelfAtMinY:(CGFloat)minY width:(CGFloat)width { if (downloadShelfController_.get()) { NSView* downloadView = [downloadShelfController_ view]; @@ -1945,6 +2141,20 @@ willPositionSheet:(NSWindow*)sheet [self layoutSubviews]; } +- (void)adjustUIForFullscreen:(BOOL)fullscreen { + if (fullscreen) { + mac_util::RequestFullScreen(); + + // Create the floating bar backing view if necessary. + if (!floatingBarBackingView_.get()) { + floatingBarBackingView_.reset( + [[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]); + } + } else { + mac_util::ReleaseFullScreen(); + } +} + @end // @implementation BrowserWindowController (Private) @implementation GTMTheme (BrowserThemeProviderInitialization) diff --git a/chrome/browser/cocoa/browser_window_controller_unittest.mm b/chrome/browser/cocoa/browser_window_controller_unittest.mm index 75c7e0e..e1318d1 100644 --- a/chrome/browser/cocoa/browser_window_controller_unittest.mm +++ b/chrome/browser/cocoa/browser_window_controller_unittest.mm @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -91,9 +91,9 @@ TEST_F(BrowserWindowControllerTest, TestSaveWindowPosition) { } TEST_F(BrowserWindowControllerTest, TestFullScreenWindow) { - // Confirm the fullscreen command doesn't return nil. + // Confirm that |-createFullscreenWindow| doesn't return nil. // See BrowserWindowFullScreenControllerTest for more fullscreen tests. - EXPECT_TRUE([controller_ fullscreenWindow]); + EXPECT_TRUE([controller_ createFullscreenWindow]); } TEST_F(BrowserWindowControllerTest, TestNormal) { @@ -166,9 +166,10 @@ TEST_F(BrowserWindowControllerTest, TestIncognitoWidthSpace) { namespace { // Verifies that the toolbar, infobar, tab content area, and download shelf -// completely fill their window's contentView. +// completely fill the area under the tabstrip. void CheckViewPositions(BrowserWindowController* controller) { NSRect contentView = [[[controller window] contentView] bounds]; + NSRect tabstrip = [[controller tabStripView] frame]; NSRect toolbar = [[controller toolbarView] frame]; NSRect infobar = [[controller infoBarContainerView] frame]; NSRect contentArea = [[controller tabContentArea] frame]; @@ -189,7 +190,9 @@ void CheckViewPositions(BrowserWindowController* controller) { EXPECT_TRUE([[controller bookmarkView] isHidden]); } - EXPECT_EQ(NSMaxY(contentView), NSMaxY(toolbar)); + // Toolbar should start immediately under the tabstrip, but the tabstrip is + // not necessarily fixed with respect to the content view. + EXPECT_EQ(NSMinY(tabstrip), NSMaxY(toolbar)); } } // end namespace @@ -582,13 +585,7 @@ class BrowserWindowFullScreenControllerTest : public CocoaTest { - (BOOL)supportsFullscreen; @end -// Fullscreen mode disabled for Mstone-4 / ReleaseBlock-Beta. -// Confirm we don't accidentally turn it back on. -TEST_F(BrowserWindowFullScreenControllerTest, ConfirmFullscreenDisabled) { - EXPECT_FALSE([controller_ supportsFullscreen]); -} - -TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestFullscreen) { +TEST_F(BrowserWindowFullScreenControllerTest, TestFullscreen) { EXPECT_FALSE([controller_ isFullscreen]); [controller_ setFullscreen:YES]; EXPECT_TRUE([controller_ isFullscreen]); @@ -596,7 +593,7 @@ TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestFullscreen) { EXPECT_FALSE([controller_ isFullscreen]); } -TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestActivate) { +TEST_F(BrowserWindowFullScreenControllerTest, TestActivate) { EXPECT_FALSE([controller_ isFullscreen]); [controller_ activate]; @@ -606,19 +603,19 @@ TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestActivate) { [controller_ setFullscreen:YES]; [controller_ activate]; frontmostWindow = [[NSApp orderedWindows] objectAtIndex:0]; - EXPECT_EQ(frontmostWindow, [controller_ fullscreenWindow]); + EXPECT_EQ(frontmostWindow, [controller_ createFullscreenWindow]); // We have to cleanup after ourselves by unfullscreening. [controller_ setFullscreen:NO]; } @implementation BrowserWindowControllerFakeFullscreen -// Override fullscreenWindow to return a dummy window. This isn't needed to -// pass the test, but because the dummy window is only 100x100, it prevents the -// real fullscreen window from flashing up and taking over the whole screen.. -// We have to return an actual window because layoutSubviews: looks at the -// window's frame. -- (NSWindow*)fullscreenWindow { +// Override |-createFullscreenWindow| to return a dummy window. This isn't +// needed to pass the test, but because the dummy window is only 100x100, it +// prevents the real fullscreen window from flashing up and taking over the +// whole screen. We have to return an actual window because |-layoutSubviews| +// looks at the window's frame. +- (NSWindow*)createFullscreenWindow { if (fullscreenWindow_.get()) return fullscreenWindow_.get(); diff --git a/chrome/browser/cocoa/floating_bar_backing_view.h b/chrome/browser/cocoa/floating_bar_backing_view.h new file mode 100644 index 0000000..66ee53f --- /dev/null +++ b/chrome/browser/cocoa/floating_bar_backing_view.h @@ -0,0 +1,14 @@ +// 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. + +#ifndef CHROME_BROWSER_COCOA_FLOATING_BAR_BACKING_VIEW_H_ +#define CHROME_BROWSER_COCOA_FLOATING_BAR_BACKING_VIEW_H_ + +#import <Cocoa/Cocoa.h> + +// A custom view that draws the tab strip background for fullscreen windows. +@interface FloatingBarBackingView : NSView +@end + +#endif // CHROME_BROWSER_COCOA_FLOATING_BAR_BACKING_VIEW_H_ diff --git a/chrome/browser/cocoa/floating_bar_backing_view.mm b/chrome/browser/cocoa/floating_bar_backing_view.mm new file mode 100644 index 0000000..b44c244 --- /dev/null +++ b/chrome/browser/cocoa/floating_bar_backing_view.mm @@ -0,0 +1,65 @@ +// 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. + +#include "chrome/browser/cocoa/floating_bar_backing_view.h" +#import "third_party/GTM/AppKit/GTMTheme.h" + +@implementation FloatingBarBackingView + +- (void)drawRect:(NSRect)rect { + NSWindow* window = [self window]; + BOOL isMainWindow = [window isMainWindow]; + + if (isMainWindow) + [[NSColor windowFrameColor] set]; + else + [[NSColor windowBackgroundColor] set]; + NSRectFill(rect); + + // Code basically clipped from browser_frame_view.mm, with modifications to + // not use window rect and not draw rounded corners. + NSRect bounds = [self bounds]; + + // Draw our background color if we have one, otherwise fall back on + // system drawing. + GTMTheme* theme = [self gtm_theme]; + GTMThemeState state = isMainWindow ? GTMThemeStateActiveWindow + : GTMThemeStateInactiveWindow; + NSColor* color = [theme backgroundPatternColorForStyle:GTMThemeStyleWindow + state:state]; + if (color) { + // If there is a theme pattern, draw it here. + + // To line up the background pattern with the patterns in the tabs the + // background pattern in the window frame need to be moved up by two + // pixels and left by 5. + // This will make the themes look slightly different than in Windows/Linux + // because of the differing heights between window top and tab top, but this + // has been approved by UI. + static const NSPoint kBrowserFrameViewPatternPhaseOffset = { -5, 2 }; + NSPoint phase = kBrowserFrameViewPatternPhaseOffset; + phase.y += NSHeight(bounds); + phase = [self gtm_themePatternPhase]; + [[NSGraphicsContext currentContext] setPatternPhase:phase]; + [color set]; + NSRectFill(rect); + } + + // Check to see if we have an overlay image. + NSImage* overlayImage = [theme valueForAttribute:@"overlay" + style:GTMThemeStyleWindow + state:state]; + if (overlayImage) { + // Anchor to top-left and don't scale. + NSSize overlaySize = [overlayImage size]; + NSRect imageFrame = NSMakeRect(0, 0, overlaySize.width, overlaySize.height); + [overlayImage drawAtPoint:NSMakePoint(0, NSHeight(bounds) - + overlaySize.height) + fromRect:imageFrame + operation:NSCompositeSourceOver + fraction:1.0]; + } +} + +@end // @implementation FloatingBarBackingView diff --git a/chrome/browser/cocoa/floating_bar_backing_view_unittest.mm b/chrome/browser/cocoa/floating_bar_backing_view_unittest.mm new file mode 100644 index 0000000..94d395f --- /dev/null +++ b/chrome/browser/cocoa/floating_bar_backing_view_unittest.mm @@ -0,0 +1,26 @@ +// 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/cocoa/cocoa_test_helper.h" +#import "chrome/browser/cocoa/floating_bar_backing_view.h" + +namespace { + +class FloatingBarBackingViewTest : public CocoaTest { + public: + FloatingBarBackingViewTest() { + NSRect content_frame = [[test_window() contentView] frame]; + scoped_nsobject<FloatingBarBackingView> view( + [[FloatingBarBackingView alloc] initWithFrame:content_frame]); + view_ = view.get(); + [[test_window() contentView] addSubview:view_]; + } + + FloatingBarBackingView* view_; // Weak. Owned by the view hierarchy. +}; + +// Tests display, add/remove. +TEST_VIEW(FloatingBarBackingViewTest, view_); + +} // namespace diff --git a/chrome/browser/cocoa/fullscreen_controller.h b/chrome/browser/cocoa/fullscreen_controller.h new file mode 100644 index 0000000..06f1d42 --- /dev/null +++ b/chrome/browser/cocoa/fullscreen_controller.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef CHROME_BROWSER_COCOA_FULLSCREEN_CONTROLLER_H_ +#define CHROME_BROWSER_COCOA_FULLSCREEN_CONTROLLER_H_ + +#import <Cocoa/Cocoa.h> + +#import "base/cocoa_protocols_mac.h" +#include "chrome/browser/cocoa/location_bar_view_mac.h" + +@class BrowserWindowController; + +// Provides a controller to manage fullscreen mode for a single browser window. +// This class handles running animations, showing and hiding the floating +// dropdown bar, and managing the tracking area associated with the dropdown. +// This class does not directly manage any views -- the BrowserWindowController +// is responsible for positioning and z-ordering views. +// +// Tracking areas are disabled while animations are running. If +// |overlayFrameChanged:| is called while an animation is running, the +// controller saves the new frame and installs the appropriate tracking area +// when the animation finishes. This is largely done for ease of +// implementation; it is easier to check the mouse location at each animation +// step than it is to manage a constantly-changing tracking area. +@interface FullscreenController : NSObject<NSAnimationDelegate> { + // Our parent controller. + BrowserWindowController* browserController_; // weak + + // The content view for the fullscreen window. This is nil when not in + // fullscreen mode. + NSView* contentView_; // weak + + // Whether or not we are in fullscreen mode. + BOOL isFullscreen_; + + // The tracking area associated with the floating dropdown bar. This tracking + // area is attached to |contentView_|, because when the dropdown is completely + // hidden, we still need to keep a 1px tall tracking area visible. Attaching + // to the content view allows us to do this. |trackingArea_| can be nil if + // not in fullscreen mode or during animations. + scoped_nsobject<NSTrackingArea> trackingArea_; + + // Pointer to the currently running animation. Is nil if no animation is + // running. + scoped_nsobject<NSAnimation> currentAnimation_; + + // Holds the current bounds of |trackingArea_|, even if |trackingArea_| is + // currently nil. Used to restore the tracking area when an animation + // completes. + NSRect trackingAreaBounds_; +} + +// Designated initializer. +- (id)initWithBrowserController:(BrowserWindowController*)controller; + +// Informs the controller that the browser has entered or exited fullscreen +// mode. enterFullscreenForContentView:showDropdown: should be called after the +// fullscreen window is setup, just before it is shown. exitFullscreen should +// be called before any views are moved back to the non-fullscreen window. +- (void)enterFullscreenForContentView:(NSView*)contentView + showDropdown:(BOOL)showDropdown; +- (void)exitFullscreen; + +// Informs the controller that the overlay's frame has changed. The controller +// uses this information to update its tracking areas. +- (void)overlayFrameChanged:(NSRect)frame; +@end + +#endif // CHROME_BROWSER_COCOA_FULLSCREEN_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/fullscreen_controller.mm b/chrome/browser/cocoa/fullscreen_controller.mm new file mode 100644 index 0000000..ee7bc5c --- /dev/null +++ b/chrome/browser/cocoa/fullscreen_controller.mm @@ -0,0 +1,346 @@ +// 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/cocoa/fullscreen_controller.h" + +#import "chrome/browser/cocoa/browser_window_controller.h" +#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" + + +namespace { +const CGFloat kDropdownAnimationDuration = 0.12; +const CGFloat kDropdownShowDelay = 0.2; +const CGFloat kDropdownHideDelay = 0.2; +} // end namespace + + +// Helper class to manage animations for the fullscreen dropdown bar. Calls +// back to [BrowserWindowController setFloatingBarShownFraction] once per +// animation step. +@interface DropdownAnimation : NSAnimation { + BrowserWindowController* controller_; + CGFloat startFraction_; + 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:(BrowserWindowController*)controller; +@end + +@implementation DropdownAnimation +- (id)initWithFraction:(CGFloat)toFraction + fullDuration:(CGFloat)fullDuration + animationCurve:(NSInteger)animationCurve + controller:(BrowserWindowController*)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 + 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_ setFloatingBarShownFraction:fraction]; +} +@end + + +@interface FullscreenController (PrivateMethods) +// Shows or hides the overlay, with animation. The overlay cannot be hidden if +// any of its "subviews" currently has focus or if the mouse is currently over +// the overlay. +- (void)showOverlay; +- (void)hideOverlayIfPossible; + +// Stops any running animations, removes tracking areas, etc. Common cleanup +// code shared by exitFullscreen: and dealloc:. +- (void)cleanup; +@end + + +@implementation FullscreenController + +- (id)initWithBrowserController:(BrowserWindowController*)controller { + if ((self == [super init])) { + browserController_ = controller; + } + return self; +} + +- (void)dealloc { + // Perform all the exit fullscreen steps, including stopping any running + // animations and cleaning up tracking areas. + [self cleanup]; + [super dealloc]; +} + +- (void)enterFullscreenForContentView:(NSView*)contentView + showDropdown:(BOOL)showDropdown { + DCHECK(!isFullscreen_); + isFullscreen_ = YES; + contentView_ = contentView; + [browserController_ setFloatingBarShownFraction:(showDropdown ? 1 : 0)]; + + // Since there is no way to get a pointer to the correct LocationBarViewMac to + // pass as the |object| parameter, we register for all location bar focus + // notifications and filter for those from the same window once the + // notification is received. + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self + selector:@selector(gainedFocus:) + name:kLocationBarGainedFocusNotification + object:nil]; + [nc addObserver:self + selector:@selector(lostFocus:) + name:kLocationBarLostFocusNotification + object:nil]; +} + +- (void)exitFullscreen { + DCHECK(isFullscreen_); + [self cleanup]; + isFullscreen_ = NO; +} + +- (void)overlayFrameChanged:(NSRect)frame { + if (!isFullscreen_) + return; + + if (!NSIntersectsRect(frame, [contentView_ bounds])) { + // Reset the tracking area to be a single pixel high at the top border of + // the content view. + frame = [contentView_ bounds]; + frame.origin.y = NSMaxY(frame) - 1; + frame.size.height = 1; + } + + // If an animation is currently running, do not set up a tracking area. + // Instead, save the bounds and we will create it in animationDidEnd:. + if (currentAnimation_.get()) { + trackingAreaBounds_ = frame; + return; + } + + NSWindow* window = [browserController_ window]; + NSPoint mouseLoc = + [contentView_ convertPointFromBase: + [window mouseLocationOutsideOfEventStream]]; + + if (trackingArea_.get()) { + // If the tracking rectangle is already |rect|, quit early. + NSRect oldRect = [trackingArea_ rect]; + if (NSEqualRects(frame, oldRect)) + return; + + // Otherwise, remove it. + [contentView_ removeTrackingArea:trackingArea_.get()]; + } + + // Create and add a new tracking area for |frame|. + trackingArea_.reset( + [[NSTrackingArea alloc] initWithRect:frame + options:NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow + owner:self + userInfo:nil]); + [contentView_ addTrackingArea:trackingArea_.get()]; +} + +- (void)gainedFocus:(NSNotification*)notification { + LocationBarViewMac* bar = + static_cast<LocationBarViewMac*>([[notification object] pointerValue]); + if ([bar->location_entry()->GetNativeView() window] == + [browserController_ window]) { + [self showOverlay]; + } +} + +- (void)lostFocus:(NSNotification*)notification { + LocationBarViewMac* bar = + static_cast<LocationBarViewMac*>([[notification object] pointerValue]); + if ([bar->location_entry()->GetNativeView() window] == + [browserController_ window]) { + [self hideOverlayIfPossible]; + } +} + +// Used to activate the floating bar in fullscreen mode. +- (void)mouseEntered:(NSEvent*)event { + DCHECK(isFullscreen_); + + NSTrackingArea* trackingArea = [event trackingArea]; + if (trackingArea == trackingArea_.get()) { + // Cancel any pending hides and set the bar to show after a delay. + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + if (currentAnimation_.get()) { + [self showOverlay]; + } else { + [self performSelector:@selector(showOverlay) + withObject:nil + afterDelay:kDropdownShowDelay]; + } + } +} + +// Used to deactivate the floating bar in fullscreen mode. +- (void)mouseExited:(NSEvent*)event { + DCHECK(isFullscreen_); + + NSTrackingArea* trackingArea = [event trackingArea]; + if (trackingArea == trackingArea_.get()) { + // We can get a false mouse exit when the menu slides down, so if the mouse + // is where the menu would be, ignore the mouse exit. + CGFloat mainMenuHeight = [[NSApp mainMenu] menuBarHeight]; + NSWindow* window = [browserController_ window]; + NSPoint mouseLoc = [window mouseLocationOutsideOfEventStream]; + if (mouseLoc.y >= NSHeight([window frame]) - mainMenuHeight) + return; + + // Cancel any pending shows and set the bar to hide after a delay. + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + if (currentAnimation_.get()) { + [self hideOverlayIfPossible]; + } else { + [self performSelector:@selector(hideOverlayIfPossible) + withObject:nil + afterDelay:kDropdownHideDelay]; + } + } +} + +- (void)animationDidEnd:(NSAnimation*)animation { + // Reset the |currentAnimation_| pointer now that the animation is finished. + currentAnimation_.reset(nil); + + // Invariant says that the tracking area is not installed while animations are + // in progress. Check to make sure this is true. + DCHECK(!trackingArea_.get()); + if (trackingArea_.get()) + [contentView_ removeTrackingArea:trackingArea_.get()]; + + // |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. + trackingArea_.reset( + [[NSTrackingArea alloc] initWithRect:trackingAreaBounds_ + options:NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow + owner:self + userInfo:nil]); + [contentView_ addTrackingArea:trackingArea_.get()]; +} + +@end + + +@implementation FullscreenController (PrivateMethods) + +- (void)showOverlay { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + + // This method can be called by LocationBarViewMac even when we're not in + // fullscreen mode. Do nothing in that case. + if (!isFullscreen_) + return; + + // Show the overlay with animation, first stopping any running animations. + if (currentAnimation_.get()) + [currentAnimation_ stopAnimation]; + currentAnimation_.reset( + [[DropdownAnimation alloc] initWithFraction:1 + fullDuration:kDropdownAnimationDuration + animationCurve:NSAnimationEaseIn + controller:browserController_]); + [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 .h file). + if (currentAnimation_ && trackingArea_.get()) { + [contentView_ removeTrackingArea:trackingArea_.get()]; + trackingArea_.reset(nil); + } + [currentAnimation_ startAnimation]; +} + +- (void)hideOverlayIfPossible { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + + if (!isFullscreen_) + return; + + NSWindow* window = [browserController_ window]; + + // If the floating bar currently has focus, do not hide the overlay. + if ([browserController_ floatingBarHasFocus]) + return; + + // We can get a false mouse exit when the menu slides down, so if the mouse is + // where the menu would be, ignore the mouse exit. + // TODO(rohitrao): Set a timer to check where the mouse is later, to make + // sure the floating bar doesn't get stuck. + CGFloat mainMenuHeight = [[NSApp mainMenu] menuBarHeight]; + NSPoint mouseLoc = [window mouseLocationOutsideOfEventStream]; + if (mouseLoc.y >= NSHeight([window frame]) - mainMenuHeight) + return; + + // Do not hide the bar if the mouse is currently over the dropdown view. We + // use the current tracking area as a proxy for the dropdown's location. If + // there is no current tracking area (possible because we currently do not + // always open the dropdown when the omnibox gets focus), then go ahead and + // close the dropdown. + if (trackingArea_.get() && + NSMouseInRect([contentView_ convertPointFromBase:mouseLoc], + [trackingArea_ rect], + [contentView_ isFlipped])) { + return; + } + + // Hide the overlay with animation, first stopping any running animations. + if (currentAnimation_.get()) + [currentAnimation_ stopAnimation]; + currentAnimation_.reset( + [[DropdownAnimation alloc] initWithFraction:0 + fullDuration:kDropdownAnimationDuration + animationCurve:NSAnimationEaseOut + controller:browserController_]); + [currentAnimation_ setAnimationBlockingMode:NSAnimationNonblocking]; + [currentAnimation_ setDelegate:self]; + + // If there is an existing tracking area, remove it. We do not track mouse + // movements during animations. + if (currentAnimation_ && trackingArea_.get()) { + [contentView_ removeTrackingArea:trackingArea_.get()]; + trackingArea_.reset(nil); + } + + [currentAnimation_ startAnimation]; +} + +- (void)cleanup { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [currentAnimation_ stopAnimation]; + [contentView_ removeTrackingArea:trackingArea_.get()]; + contentView_ = nil; + currentAnimation_.reset(nil); + trackingArea_.reset(nil); +} + +@end diff --git a/chrome/browser/cocoa/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar_view_mac.h index 363188c..567a9a2 100644 --- a/chrome/browser/cocoa/location_bar_view_mac.h +++ b/chrome/browser/cocoa/location_bar_view_mac.h @@ -27,6 +27,10 @@ class CommandUpdater; class Profile; class ToolbarModel; +// Notifications sent when this location bar gains or loses focus. +extern const NSString* kLocationBarGainedFocusNotification; +extern const NSString* kLocationBarLostFocusNotification; + // A C++ bridge class that represents the location bar UI element to // the portable code. Wires up an AutocompleteEditViewMac instance to // the location bar text field, which handles most of the work. @@ -327,6 +331,9 @@ class LocationBarViewMac : public AutocompleteEditController, // Sets the label for the SSL icon. void SetSecurityIconLabel(); + // Posts |notification| to the default notification center. + void PostNotification(const NSString* notification); + scoped_ptr<AutocompleteEditViewMac> edit_view_; CommandUpdater* command_updater_; // Weak, owned by Browser. diff --git a/chrome/browser/cocoa/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar_view_mac.mm index 3fd0da4..6d30820 100644 --- a/chrome/browser/cocoa/location_bar_view_mac.mm +++ b/chrome/browser/cocoa/location_bar_view_mac.mm @@ -34,6 +34,13 @@ #include "grit/theme_resources.h" #include "skia/ext/skia_utils_mac.h" + +const NSString* kLocationBarGainedFocusNotification = + @"kLocationBarGainedFocusNotification_Chrome"; +const NSString* kLocationBarLostFocusNotification = + @"kLocationBarLostFocusNotification_Chrome"; + + // TODO(shess): This code is mostly copied from the gtk // implementation. Make sure it's all appropriate and flesh it out. @@ -139,10 +146,12 @@ void LocationBarViewMac::AcceptInputWithDisposition( } void LocationBarViewMac::FocusLocation() { + PostNotification(kLocationBarGainedFocusNotification); edit_view_->FocusLocation(); } void LocationBarViewMac::FocusSearch() { + PostNotification(kLocationBarGainedFocusNotification); edit_view_->SetForcedQuery(); // TODO(pkasting): Focus the edit a la Linux/Win } @@ -299,10 +308,12 @@ void LocationBarViewMac::OnInputInProgress(bool in_progress) { Update(NULL, false); } -void LocationBarViewMac::OnKillFocus() { +void LocationBarViewMac::OnSetFocus() { + PostNotification(kLocationBarGainedFocusNotification); } -void LocationBarViewMac::OnSetFocus() { +void LocationBarViewMac::OnKillFocus() { + PostNotification(kLocationBarLostFocusNotification); } SkBitmap LocationBarViewMac::GetFavIcon() const { @@ -317,6 +328,7 @@ std::wstring LocationBarViewMac::GetTitle() const { void LocationBarViewMac::Revert() { edit_view_->RevertAll(); + PostNotification(kLocationBarLostFocusNotification); } // TODO(pamg): Change all these, here and for other platforms, to size_t. @@ -473,6 +485,11 @@ void LocationBarViewMac::Observe(NotificationType type, } } +void LocationBarViewMac::PostNotification(const NSString* notification) { + [[NSNotificationCenter defaultCenter] postNotificationName:notification + object:[NSValue valueWithPointer:this]]; +} + // LocationBarImageView--------------------------------------------------------- void LocationBarViewMac::LocationBarImageView::SetImage(NSImage* image) { diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index a85e57d..c181704e 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -496,10 +496,14 @@ 'browser/cocoa/find_pasteboard.mm', 'browser/cocoa/first_run_dialog.h', 'browser/cocoa/first_run_dialog.mm', + 'browser/cocoa/floating_bar_backing_view.h', + 'browser/cocoa/floating_bar_backing_view.mm', 'browser/cocoa/focus_tracker.h', 'browser/cocoa/focus_tracker.mm', 'browser/cocoa/font_language_settings_controller.h', 'browser/cocoa/font_language_settings_controller.mm', + 'browser/cocoa/fullscreen_controller.h', + 'browser/cocoa/fullscreen_controller.mm', 'browser/cocoa/fullscreen_window.h', 'browser/cocoa/fullscreen_window.mm', 'browser/cocoa/gradient_button_cell.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index d7d338f..066f627 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -616,6 +616,7 @@ 'browser/cocoa/find_bar_text_field_cell_unittest.mm', 'browser/cocoa/find_bar_view_unittest.mm', 'browser/cocoa/find_pasteboard_unittest.mm', + 'browser/cocoa/floating_bar_backing_view_unittest.mm', 'browser/cocoa/focus_tracker_unittest.mm', 'browser/cocoa/font_language_settings_controller_unittest.mm', 'browser/cocoa/fullscreen_window_unittest.mm', |