Index: chrome/browser/cocoa/tab_strip_controller.mm |
=================================================================== |
--- chrome/browser/cocoa/tab_strip_controller.mm (revision 21532) |
+++ chrome/browser/cocoa/tab_strip_controller.mm (working copy) |
@@ -29,6 +29,10 @@ |
NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; |
+// A value to indicate tab layout should use the full available width of the |
+// view. |
+static const float kUseFullAvailableWidth = -1.0; |
+ |
// A simple view class that prevents the windowserver from dragging the |
// area behind tabs. Sometimes core animation confuses it. |
@interface TabStripControllerDragBlockingView : NSView |
@@ -38,6 +42,11 @@ |
- (void)drawRect:(NSRect)rect {} |
@end |
+@interface TabStripController(Private) |
+- (void)installTrackingArea; |
+- (BOOL)useFullWidthForLayout; |
+@end |
+ |
@implementation TabStripController |
- (id)initWithView:(TabStripView*)view |
@@ -66,6 +75,7 @@ |
initWithFrame:NSZeroRect]); |
[view addSubview:dragBlockingView_]; |
newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); |
+ availableResizeWidth_ = kUseFullAvailableWidth; |
// Watch for notifications that the tab strip view has changed size so |
// we can tell it to layout for the new size. |
@@ -79,6 +89,7 @@ |
} |
- (void)dealloc { |
+ [tabView_ removeTrackingArea:closeTabTrackingArea_.get()]; |
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
[super dealloc]; |
} |
@@ -157,19 +168,27 @@ |
// Called when the user clicks a tab. Tell the model the selection has changed, |
// which feeds back into us via a notification. |
- (void)selectTab:(id)sender { |
+ DCHECK([sender isKindOfClass:[NSView class]]); |
int index = [self indexForTabView:sender]; |
if (index >= 0 && tabModel_->ContainsIndex(index)) |
tabModel_->SelectTabContentsAt(index, true); |
} |
-// Called when the user closes a tab. Asks the model to close the tab. |
+// Called when the user closes a tab. Asks the model to close the tab. |sender| |
+// is the TabView that is potentially going away. |
- (void)closeTab:(id)sender { |
+ DCHECK([sender isKindOfClass:[NSView class]]); |
int index = [self indexForTabView:sender]; |
if (tabModel_->ContainsIndex(index)) { |
TabContents* contents = tabModel_->GetTabContentsAt(index); |
if (contents) |
UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile()); |
if ([self numberOfTabViews] > 1) { |
+ // Limit the width available for laying out tabs so that tabs are not |
+ // resized until a later time (when the mouse leaves the tab strip). |
+ NSView* penultimateTab = [self viewAtIndex:[tabArray_ count] - 2]; |
rohitrao (ping after 24h)
2009/07/24 20:35:57
Will we have to revisit this once we implement rea
|
+ availableResizeWidth_ = NSMaxX([penultimateTab frame]); |
+ [self installTrackingArea]; |
tabModel_->CloseTabContentsAt(index); |
} else { |
// Use the standard window close if this is the last tab |
@@ -225,12 +244,20 @@ |
[NSAnimationContext beginGrouping]; |
[[NSAnimationContext currentContext] setDuration:0.2]; |
- // Compute the base width of tabs given how much size we have available. |
- float availableWidth = |
- NSWidth([tabView_ frame]) - NSWidth([newTabButton_ frame]) - |
- kNewTabButtonOffset - kIndentLeavingSpaceForControls; |
+ // Compute the base width of tabs given how much room we're allowed. We |
+ // may not be able to use the entire width if the user is quickly closing |
+ // tabs. |
+ float availableWidth = 0; |
+ if ([self useFullWidthForLayout]) { |
+ availableWidth = NSWidth([tabView_ frame]); |
+ availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; |
+ } else { |
+ availableWidth = availableResizeWidth_; |
+ } |
+ availableWidth -= kIndentLeavingSpaceForControls; |
+ |
// Add back in the amount we "get back" from the tabs overlapping. |
- availableWidth += [tabContentsArray_ count] * kTabOverlap; |
+ availableWidth += ([tabContentsArray_ count] - 1) * kTabOverlap; |
const float baseTabWidth = |
MAX(MIN(availableWidth / [tabContentsArray_ count], |
kMaxTabWidth), |
@@ -308,16 +335,20 @@ |
enclosingRect = NSUnionRect(tabFrame, enclosingRect); |
} |
+#if 0 |
// Ensure the current tab is "below" the tab before it in z-order so that |
// all the tab overlaps are consistent. The selected tab is always the |
// frontmost, but it's already been made frontmost when the tab was selected |
// so we don't need to do anything about it here. It will get put back into |
// place when another tab is selected. |
+ // TODO(pinkerton): this doesn't seem to work in the case where a tab |
+ // is opened between existing tabs. Disabling. |
if (![tab selected]) { |
[tabView_ addSubview:[tab view] |
positioned:NSWindowBelow |
relativeTo:previousTab]; |
} |
+#endif |
previousTab = [tab view]; |
offset += NSWidth(tabFrame); |
@@ -326,8 +357,11 @@ |
} |
NSRect newTabNewFrame = [newTabButton_ frame]; |
- newTabNewFrame.origin = |
- NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); |
+ if ([self useFullWidthForLayout]) |
+ newTabNewFrame.origin = |
+ NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); |
+ else |
+ newTabNewFrame.origin = NSMakePoint(offset + kNewTabButtonOffset, 0); |
newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, |
NSMaxX(placeholderFrame_)); |
if (i > 0 && [newTabButton_ isHidden]) { |
@@ -401,6 +435,10 @@ |
[self setTabTitle:newController withContents:contents]; |
+ // If a tab is being inserted, we can again use the entire tab strip width |
+ // for layout. |
+ availableResizeWidth_ = kUseFullAvailableWidth; |
+ |
// We don't need to call |-layoutTabs| if the tab will be in the foreground |
// because it will get called when the new tab is selected by the tab model. |
if (!inForeground) { |
@@ -654,4 +692,39 @@ |
[self layoutTabsWithAnimation:NO]; |
} |
+- (BOOL)useFullWidthForLayout { |
+ return availableResizeWidth_ == kUseFullAvailableWidth; |
+} |
+ |
+// Call to install a tracking area that reports mouseEnter/Exit messages so |
+// we can track when the mouse leaves the tab view after closing a tab with |
+// the mouse. Don't install another tracking rect if one is already there. |
+- (void)installTrackingArea { |
+ if (closeTabTrackingArea_.get()) |
+ return; |
+ // Note that we pass |NSTrackingInVisibleRect| so the rect is actually |
+ // ignored. |
+ closeTabTrackingArea_.reset([[NSTrackingArea alloc] |
+ initWithRect:[tabView_ bounds] |
+ options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | |
+ NSTrackingInVisibleRect |
+ owner:self |
+ userInfo:nil]); |
+ [tabView_ addTrackingArea:closeTabTrackingArea_.get()]; |
+} |
+ |
+- (void)mouseEntered:(NSEvent*)event { |
+ // Do nothing. |
+} |
+ |
+// Called when the tracking area is in effect which means we're tracking to |
+// see if the user leaves the tab strip with their mouse. When they do, |
+// reset layout to use all available width. |
+- (void)mouseExited:(NSEvent*)event { |
+ [tabView_ removeTrackingArea:closeTabTrackingArea_.get()]; |
+ closeTabTrackingArea_.reset(nil); |
+ availableResizeWidth_ = kUseFullAvailableWidth; |
+ [self layoutTabs]; |
+} |
+ |
@end |