diff options
Diffstat (limited to 'chrome/browser')
-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 |
8 files changed, 185 insertions, 14 deletions
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; } |