diff options
author | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-23 20:12:50 +0000 |
---|---|---|
committer | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-23 20:12:50 +0000 |
commit | c4f0ed1562c5f4f021b17f1d371f77f955968561 (patch) | |
tree | 3a9724883d519a2b2be43e795cababb1fdfe8758 /chrome | |
parent | dabc3e84a78f0438785e892cf56b4c94cc8d22c3 (diff) | |
download | chromium_src-c4f0ed1562c5f4f021b17f1d371f77f955968561.zip chromium_src-c4f0ed1562c5f4f021b17f1d371f77f955968561.tar.gz chromium_src-c4f0ed1562c5f4f021b17f1d371f77f955968561.tar.bz2 |
Make tabs go all the way to the right edge and stop using less of the strip as more tabs are added. Don't resize on selection when tabs are very small, except just enough to show a close box. Clip closebox and favicon as tab gets very small. Fix z-order to be consistent among all unselected tabs. Fix incognito man disappearing when dragging window via the tab. Tabs can now get about as small as they do on windows allowing many more to fit.
BUG=14911, 14913, 17372
TEST=adding and removing lots of tabs and making sure nothing goes wrong.
Review URL: http://codereview.chromium.org/159206
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21432 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/nibs/TabView.xib | 45 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.h | 4 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 8 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller_unittest.mm | 22 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_controller.h | 10 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_controller.mm | 63 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_controller_unittest.mm | 51 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.mm | 38 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_view.mm | 3 |
9 files changed, 218 insertions, 26 deletions
diff --git a/chrome/app/nibs/TabView.xib b/chrome/app/nibs/TabView.xib index 4351b93..14ff731 100644 --- a/chrome/app/nibs/TabView.xib +++ b/chrome/app/nibs/TabView.xib @@ -2,13 +2,13 @@ <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03"> <data> <int key="IBDocument.SystemTarget">1050</int> - <string key="IBDocument.SystemVersion">9J61</string> + <string key="IBDocument.SystemVersion">9F33</string> <string key="IBDocument.InterfaceBuilderVersion">677</string> - <string key="IBDocument.AppKitVersion">949.46</string> - <string key="IBDocument.HIToolboxVersion">353.00</string> + <string key="IBDocument.AppKitVersion">949.34</string> + <string key="IBDocument.HIToolboxVersion">352.00</string> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <bool key="EncodedWithXMLCoder">YES</bool> - <integer value="59"/> + <integer value="1"/> </object> <object class="NSArray" key="IBDocument.PluginDependencies"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -45,7 +45,6 @@ <int key="NSvFlags">274</int> <string key="NSFrameSize">{160, 25}</string> <reference key="NSSuperview" ref="1005"/> - <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="707804163"> <int key="NSCellFlags">-2080244160</int> @@ -82,7 +81,6 @@ </object> <string key="NSFrame">{{5, 46}, {32, 32}}</string> <reference key="NSSuperview" ref="1005"/> - <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="601843736"> <int key="NSCellFlags">130560</int> @@ -115,7 +113,6 @@ </object> <string key="NSFrame">{{18, 5}, {16, 16}}</string> <reference key="NSSuperview" ref="1005"/> - <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="1061639670"> <int key="NSCellFlags">537001472</int> @@ -136,7 +133,6 @@ <int key="NSvFlags">297</int> <string key="NSFrame">{{131, 5}, {15, 15}}</string> <reference key="NSSuperview" ref="1005"/> - <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="348599947"> <int key="NSCellFlags">67239424</int> @@ -167,7 +163,6 @@ </object> <string key="NSFrameSize">{160, 25}</string> <reference key="NSSuperview"/> - <reference key="NSWindow"/> <string key="NSClassName">TabView</string> </object> <object class="NSMenu" id="398259350"> @@ -438,6 +433,14 @@ </object> <int key="connectionID">88</int> </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">closeButton_</string> + <reference key="source" ref="1001"/> + <reference key="destination" ref="1054640993"/> + </object> + <int key="connectionID">89</int> + </object> </object> <object class="IBMutableOrderedSet" key="objectRecords"> <object class="NSArray" key="orderedObjects"> @@ -691,13 +694,22 @@ </object> </object> <nil key="sourceID"/> - <int key="maxID">88</int> + <int key="maxID">89</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <object class="NSMutableArray" key="referencedPartialClassDescriptions"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="IBPartialClassDescription"> + <string key="className">BackgroundGradientView</string> + <string key="superclassName">NSView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">browser/cocoa/background_gradient_view.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> <string key="className">FirstResponder</string> + <string key="superclassName">NSObject</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> <string key="majorKey">IBUserSource</string> <string key="minorKey"/> @@ -707,6 +719,13 @@ <string key="className">NSObject</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> <string key="majorKey">IBProjectSource</string> + <string key="minorKey">browser/cocoa/status_bubble_mac.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> <string key="minorKey">browser/cocoa/tab_strip_model_observer_bridge.h</string> </object> </object> @@ -739,6 +758,7 @@ <object class="NSMutableArray" key="dict.sortedKeys"> <bool key="EncodedWithXMLCoder">YES</bool> <string>backgroundButton_</string> + <string>closeButton_</string> <string>contextMenu_</string> <string>iconView_</string> <string>target_</string> @@ -746,6 +766,7 @@ <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> <string>NSButton</string> + <string>NSButton</string> <string>NSMenu</string> <string>NSView</string> <string>id</string> @@ -758,7 +779,7 @@ </object> <object class="IBPartialClassDescription"> <string key="className">TabView</string> - <string key="superclassName">NSView</string> + <string key="superclassName">BackgroundGradientView</string> <object class="NSMutableDictionary" key="outlets"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSMutableArray" key="dict.sortedKeys"> @@ -780,7 +801,7 @@ </object> </object> <int key="IBDocument.localizationMode">0</int> - <string key="IBDocument.LastKnownRelativeProjectPath">../../../chrome.xcodeproj</string> + <string key="IBDocument.LastKnownRelativeProjectPath">../../chrome.xcodeproj</string> <int key="IBDocument.defaultPropertyAccessControl">3</int> </data> </archive> diff --git a/chrome/browser/cocoa/browser_window_controller.h b/chrome/browser/cocoa/browser_window_controller.h index 39fee3a..382acf2 100644 --- a/chrome/browser/cocoa/browser_window_controller.h +++ b/chrome/browser/cocoa/browser_window_controller.h @@ -144,6 +144,10 @@ class TabStripModelObserverBridge; @interface BrowserWindowController(TestingAPI) +// Put the incognito badge on the browser and adjust the tab strip +// accordingly. +- (void)installIncognitoBadge; + // Allows us to initWithBrowser withOUT taking ownership of the browser. - (id)initWithBrowser:(Browser*)browser takeOwnership:(BOOL)ownIt; diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index ac8278e..ce68077 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -69,7 +69,6 @@ const int kWindowGradientHeight = 24; - (void)positionInfoBar; - (void)positionBar; // toolbar or URL bar - (void)removeBar; // toolbar or URL bar -- (void)installIncognitoBadge; // Leopard's gradient heuristic gets confused by our tabs and makes the title // gradient jump when creating a tab that is less than a tab width from the @@ -933,7 +932,7 @@ willPositionSheet:(NSWindow*)sheet scoped_nsobject<NSImage> incognitoImage( [[NSImage alloc] initWithContentsOfFile:incognitoPath]); const NSSize imageSize = [incognitoImage size]; - const NSRect tabFrame = [[self tabStripView] frame]; + NSRect tabFrame = [[self tabStripView] frame]; NSRect incognitoFrame = tabFrame; incognitoFrame.origin.x = NSMaxX(incognitoFrame) - imageSize.width - kOffset; @@ -948,6 +947,11 @@ willPositionSheet:(NSWindow*)sheet [shadow setShadowOffset:NSMakeSize(0, -1)]; [shadow setShadowBlurRadius:2.0]; [incognitoView setShadow:shadow]; + + // Shrink the tab strip's width so there's no overlap and install the + // view. + tabFrame.size.width -= incognitoFrame.size.width + kOffset; + [[self tabStripView] setFrame:tabFrame]; [[[[self window] contentView] superview] addSubview:incognitoView.get()]; } diff --git a/chrome/browser/cocoa/browser_window_controller_unittest.mm b/chrome/browser/cocoa/browser_window_controller_unittest.mm index be30d9a..f816a76 100644 --- a/chrome/browser/cocoa/browser_window_controller_unittest.mm +++ b/chrome/browser/cocoa/browser_window_controller_unittest.mm @@ -12,6 +12,7 @@ #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/test/testing_browser_process.h" +#include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" @interface BrowserWindowController (ExposedForTesting) @@ -110,4 +111,25 @@ TEST_F(BrowserWindowControllerTest, BookmarkBarControllerIndirection) { EXPECT_TRUE([controller_ isBookmarkBarVisible]); } +#if 0 +// TODO(jrg): This crashes trying to create the BookmarkBarController, adding +// an observer to the BookmarkModel. +TEST_F(BrowserWindowControllerTest, TestIncognitoWidthSpace) { + scoped_ptr<TestingProfile> incognito_profile(new TestingProfile()); + incognito_profile->set_off_the_record(true); + scoped_ptr<Browser> browser(new Browser(Browser::TYPE_NORMAL, + incognito_profile.get())); + controller_.reset([[BrowserWindowController alloc] + initWithBrowser:browser.get() + takeOwnership:NO]); + + NSRect tabFrame = [[controller_ tabStripView] frame]; + [controller_ installIncognitoBadge]; + NSRect newTabFrame = [[controller_ tabStripView] frame]; + EXPECT_GT(tabFrame.size.width, newTabFrame.size.width); + + controller_.release(); +} +#endif + /* TODO(???): test other methods of BrowserWindowController */ diff --git a/chrome/browser/cocoa/tab_controller.h b/chrome/browser/cocoa/tab_controller.h index b49da4f..ba1d626 100644 --- a/chrome/browser/cocoa/tab_controller.h +++ b/chrome/browser/cocoa/tab_controller.h @@ -37,6 +37,8 @@ enum TabLoadingState { IBOutlet NSButton* backgroundButton_; IBOutlet NSView* iconView_; IBOutlet NSMenu* contextMenu_; + IBOutlet NSButton* closeButton_; + BOOL selected_; TabLoadingState loadingState_; id<TabControllerTarget> target_; // weak, where actions are sent @@ -49,9 +51,12 @@ enum TabLoadingState { @property(assign, nonatomic) id target; @property(assign, nonatomic) SEL action; -// Minimum and maximum allowable tab width. +// Minimum and maximum allowable tab width. The minimum width does not show +// the icon or the close box. The selected tab always has at least a close box +// so it has a different minimum width. + (float)minTabWidth; + (float)maxTabWidth; ++ (float)minSelectedTabWidth; // The view associated with this controller, pre-casted as a TabView - (TabView *)tabView; @@ -72,6 +77,9 @@ enum TabLoadingState { @interface TabController(TestingAPI) - (NSString *)toolTip; +- (int)iconCapacity; +- (BOOL)shouldShowIcon; +- (BOOL)shouldShowCloseBox; @end // TabController(TestingAPI) #endif // CHROME_BROWSER_COCOA_TAB_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/tab_controller.mm b/chrome/browser/cocoa/tab_controller.mm index 5c47fb5..bb9f0c3 100644 --- a/chrome/browser/cocoa/tab_controller.mm +++ b/chrome/browser/cocoa/tab_controller.mm @@ -8,13 +8,21 @@ #import "chrome/browser/cocoa/tab_controller_target.h" #import "chrome/browser/cocoa/tab_view.h" +@interface TabController(Private) +- (void)updateVisibility; +@end + @implementation TabController @synthesize loadingState = loadingState_; @synthesize target = target_; @synthesize action = action_; -+ (float)minTabWidth { return 64.0; } +// The min widths match the windows values and are sums of left + right +// padding, of which we have no comparable constants (we draw using paths, not +// images). The selected tab width includes the close box width. ++ (float)minTabWidth { return 31; } ++ (float)minSelectedTabWidth { return 47; } + (float)maxTabWidth { return 220.0; } - (TabView*)tabView { @@ -24,11 +32,17 @@ - (id)init { self = [super initWithNibName:@"TabView" bundle:mac_util::MainAppBundle()]; if (self != nil) { + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(viewResized:) + name:NSViewFrameDidChangeNotification + object:[self view]]; } return self; } - (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } @@ -38,6 +52,7 @@ - (void)internalSetSelected:(BOOL)selected { selected_ = selected; [(TabView *)[self view] setState:selected]; + [self updateVisibility]; [[self view] setNeedsDisplay:YES]; } @@ -103,4 +118,50 @@ return [backgroundButton_ toolTip]; } +// Return a rough approximation of the number of icons we could fit in the +// tab. We never actually do this, but it's a helpful guide for determining +// how much space we have available. +- (int)iconCapacity { + float width = NSWidth([[self view] frame]); + float leftPadding = NSMinX([iconView_ frame]); + float rightPadding = width - NSMaxX([closeButton_ frame]); + float iconWidth = NSWidth([iconView_ frame]); + + width -= leftPadding + rightPadding; + return width / iconWidth; +} + +// Returns YES if we should show the icon. When tabs get too small, we clip +// the favicon before the close box for selected tabs, and prefer the favicon +// for unselected tabs. +// TODO(pinkerton): don't show the icon if there's no image data (eg, NTP). +- (BOOL)shouldShowIcon { + int iconCapacity = [self iconCapacity]; + if ([self selected]) + return iconCapacity >= 2; + return iconCapacity >= 1; +} + +// Returns YES if we should be showing the close box. The selected tab always +// shows the close box. +- (BOOL)shouldShowCloseBox { + return [self selected] || [self iconCapacity] >= 3; +} + +// Call to update the visibility of certain subviews, such as the icon or +// close box, based on criteria like if the tab is selected and the current +// tab width. +- (void)updateVisibility { + [iconView_ setHidden:[self shouldShowIcon] ? NO : YES]; + [closeButton_ setHidden:[self shouldShowCloseBox] ? NO : YES]; +} + +// Called when our view is resized. If it gets too small, start by hiding +// the close box and only show it if tab is selected. Eventually, hide the +// icon as well. We know that this is for our view because we only registered +// for notifications from our specific view. +- (void)viewResized:(NSNotification*)info { + [self updateVisibility]; +} + @end diff --git a/chrome/browser/cocoa/tab_controller_unittest.mm b/chrome/browser/cocoa/tab_controller_unittest.mm index b489235..b38ae91 100644 --- a/chrome/browser/cocoa/tab_controller_unittest.mm +++ b/chrome/browser/cocoa/tab_controller_unittest.mm @@ -200,4 +200,55 @@ TEST_F(TabControllerTest, UserSelection) { [[controller view] removeFromSuperview]; } +TEST_F(TabControllerTest, IconCapacity) { + NSWindow* window = cocoa_helper_.window(); + scoped_nsobject<TabController> controller([[TabController alloc] init]); + [[window contentView] addSubview:[controller view]]; + int cap = [controller iconCapacity]; + EXPECT_GE(cap, 1); + + NSRect frame = [[controller view] frame]; + frame.size.width += 500; + [[controller view] setFrame:frame]; + int newcap = [controller iconCapacity]; + EXPECT_GT(newcap, cap); +} + +TEST_F(TabControllerTest, ShouldShowIcon) { + NSWindow* window = cocoa_helper_.window(); + scoped_nsobject<TabController> controller([[TabController alloc] init]); + [[window contentView] addSubview:[controller view]]; + int cap = [controller iconCapacity]; + EXPECT_GT(cap, 0); + + // Tab is minimum width, both icon and close box should be hidden. + NSRect frame = [[controller view] frame]; + frame.size.width = [TabController minTabWidth]; + [[controller view] setFrame:frame]; + EXPECT_FALSE([controller shouldShowIcon]); + EXPECT_FALSE([controller shouldShowCloseBox]); + + // Tab is at selected minimum width. Since it's selected, the close box + // should be visible. + [controller setSelected:YES]; + frame = [[controller view] frame]; + frame.size.width = [TabController minSelectedTabWidth]; + [[controller view] setFrame:frame]; + EXPECT_FALSE([controller shouldShowIcon]); + EXPECT_TRUE([controller shouldShowCloseBox]); + + // Test expanding the tab to max width and ensure the icon and close box + // get put back, even when de-selected. + frame.size.width = [TabController maxTabWidth]; + [[controller view] setFrame:frame]; + EXPECT_TRUE([controller shouldShowIcon]); + EXPECT_TRUE([controller shouldShowCloseBox]); + [controller setSelected:NO]; + EXPECT_TRUE([controller shouldShowIcon]); + EXPECT_TRUE([controller shouldShowCloseBox]); + + cap = [controller iconCapacity]; + EXPECT_GT(cap, 0); +} + } // namespace diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm index 90a1c5b..5057603 100644 --- a/chrome/browser/cocoa/tab_strip_controller.mm +++ b/chrome/browser/cocoa/tab_strip_controller.mm @@ -206,24 +206,30 @@ NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; const float kNewTabButtonOffset = 8.0; const float kMaxTabWidth = [TabController maxTabWidth]; const float kMinTabWidth = [TabController minTabWidth]; + const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; NSRect enclosingRect = NSZeroRect; [NSAnimationContext beginGrouping]; [[NSAnimationContext currentContext] setDuration:0.2]; - BOOL visible = [[tabView_ window] isVisible]; + // Compute the base width of tabs given how much size we have available. float availableWidth = - NSWidth([tabView_ frame]) - NSWidth([newTabButton_ frame]); - float offset = kIndentLeavingSpaceForControls; + NSWidth([tabView_ frame]) - NSWidth([newTabButton_ frame]) - + kNewTabButtonOffset - kIndentLeavingSpaceForControls; + // Add back in the amount we "get back" from the tabs overlapping. + availableWidth += [tabContentsArray_ count] * kTabOverlap; const float baseTabWidth = - MAX(MIN((availableWidth - offset) / [tabContentsArray_ count], + MAX(MIN(availableWidth / [tabContentsArray_ count], kMaxTabWidth), kMinTabWidth); CGFloat minX = NSMinX(placeholderFrame_); + BOOL visible = [[tabView_ window] isVisible]; + float offset = kIndentLeavingSpaceForControls; NSUInteger i = 0; NSInteger gap = -1; + NSView* previousTab = nil; for (TabController* tab in tabArray_.get()) { BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; NSRect tabFrame = [[tab view] frame]; @@ -269,10 +275,14 @@ NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; } - id frameTarget = visible ? [[tab view] animator] : [tab view]; - tabFrame.size.width = [tab selected] ? kMaxTabWidth : baseTabWidth; + // Set the width. Selected tabs are slightly wider when things get + // really small and thus we enforce a different minimum width. + tabFrame.size.width = + [tab selected] ? MAX(baseTabWidth, kMinSelectedTabWidth) : + baseTabWidth; // Check the frame by identifier to avoid redundant calls to animator. + id frameTarget = visible ? [[tab view] animator] : [tab view]; NSValue *identifier = [NSValue valueWithPointer:[tab view]]; NSValue *oldTargetValue = [targetFrames_ objectForKey:identifier]; if (!oldTargetValue || @@ -284,10 +294,20 @@ NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; enclosingRect = NSUnionRect(tabFrame, enclosingRect); } - if (offset < availableWidth) { - offset += NSWidth(tabFrame); - offset -= kTabOverlap; + // 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. + if (![tab selected]) { + [tabView_ addSubview:[tab view] + positioned:NSWindowBelow + relativeTo:previousTab]; } + previousTab = [tab view]; + + offset += NSWidth(tabFrame); + offset -= kTabOverlap; i++; } diff --git a/chrome/browser/cocoa/tab_view.mm b/chrome/browser/cocoa/tab_view.mm index 4f956e2..5d09f2c 100644 --- a/chrome/browser/cocoa/tab_view.mm +++ b/chrome/browser/cocoa/tab_view.mm @@ -83,7 +83,8 @@ static const CGFloat kToolbarMaxHeight = 128; // that here. This doesn't completely eliminate the overlap, but it // works well enough. NSRect hitRect = NSInsetRect(frame, frame.size.height / 3.0f, 0); - if (NSPointInRect(viewPoint, [closeButton_ frame])) return closeButton_; + if (![closeButton_ isHidden]) + if (NSPointInRect(viewPoint, [closeButton_ frame])) return closeButton_; if (NSPointInRect(aPoint, hitRect)) return self; return nil; } |