summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/cocoa/browser_window_controller.h4
-rw-r--r--chrome/browser/cocoa/browser_window_controller.mm8
-rw-r--r--chrome/browser/cocoa/browser_window_controller_unittest.mm22
-rw-r--r--chrome/browser/cocoa/tab_controller.h10
-rw-r--r--chrome/browser/cocoa/tab_controller.mm63
-rw-r--r--chrome/browser/cocoa/tab_controller_unittest.mm51
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.mm38
-rw-r--r--chrome/browser/cocoa/tab_view.mm3
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;
}