summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/cocoa/browser_window_controller.mm30
-rw-r--r--chrome/browser/cocoa/browser_window_controller_private.h5
-rw-r--r--chrome/browser/cocoa/browser_window_controller_private.mm44
-rwxr-xr-xchrome/browser/cocoa/side_tab_strip_controller.h19
-rwxr-xr-xchrome/browser/cocoa/side_tab_strip_controller.mm29
-rwxr-xr-xchrome/browser/cocoa/side_tab_strip_view.h15
-rwxr-xr-xchrome/browser/cocoa/side_tab_strip_view.mm43
-rw-r--r--chrome/browser/cocoa/side_tab_strip_view_unittest.mm30
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.h4
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.mm127
-rw-r--r--chrome/browser/cocoa/tab_strip_view.h10
-rw-r--r--chrome/browser/cocoa/tab_strip_view.mm35
-rw-r--r--chrome/browser/cocoa/tab_window_controller.h29
-rw-r--r--chrome/browser/cocoa/tab_window_controller.mm102
14 files changed, 429 insertions, 93 deletions
diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm
index d189313..ccd632e 100644
--- a/chrome/browser/cocoa/browser_window_controller.mm
+++ b/chrome/browser/cocoa/browser_window_controller.mm
@@ -233,16 +233,7 @@
// this window's Browser and the tab strip view. The controller will handle
// registering for the appropriate tab notifications from the back-end and
// managing the creation of new tabs.
- if (![self useVerticalTabs]) {
- tabStripController_.reset([[TabStripController alloc]
- initWithView:[self tabStripView]
- switchView:[self tabContentArea]
- browser:browser_.get()]);
- } else {
- // TODO(pinkerton): Load SideTabController when written and add it to the
- // contentView. This should be abstracted into a separate method like
- // the toolbar controller initialization below.
- }
+ [self createTabStripController];
// Create the infobar container view, so we can pass it to the
// ToolbarController.
@@ -1299,8 +1290,6 @@
// (Override of |TabWindowController| method.)
- (BOOL)hasTabStrip {
- if ([self useVerticalTabs])
- return NO;
return [self supportsWindowFeature:Browser::FEATURE_TABSTRIP];
}
@@ -1638,14 +1627,15 @@ willAnimateFromState:(bookmarks::VisualState)oldState
isShrinkingFromZoomed_ = NO;
}
+// Override to swap in the correct tab strip controller based on the new
+// tab strip mode.
- (void)toggleTabStripDisplayMode {
- // TODO(pinkerton) re-initialize tab strip. Finish writing this method.
- // Right now, it only switches one direction, which clearly isn't cool.
- // [self initTabStrip:browser_->tabstrip_model()];
- [[self tabStripView] removeFromSuperview];
-
+ [super toggleTabStripDisplayMode];
+ [self createTabStripController];
+}
- [self layoutSubviews];
+- (BOOL)useVerticalTabs {
+ return browser_->tabstrip_model()->delegate()->UseVerticalTabs();
}
@end // @implementation BrowserWindowController
@@ -1699,7 +1689,7 @@ willAnimateFromState:(bookmarks::VisualState)oldState
// Retain the tab strip view while we remove it from its superview.
scoped_nsobject<NSView> tabStripView;
- if ([self hasTabStrip]) {
+ if ([self hasTabStrip] && ![self useVerticalTabs]) {
tabStripView.reset([[self tabStripView] retain]);
[tabStripView removeFromSuperview];
}
@@ -1745,7 +1735,7 @@ willAnimateFromState:(bookmarks::VisualState)oldState
// Add the tab strip after setting the content view and moving the incognito
// badge (if any), so that the tab strip will be on top (in the z-order).
- if ([self hasTabStrip])
+ if ([self hasTabStrip] && ![self useVerticalTabs])
[[[destWindow contentView] superview] addSubview:tabStripView];
[window setWindowController:nil];
diff --git a/chrome/browser/cocoa/browser_window_controller_private.h b/chrome/browser/cocoa/browser_window_controller_private.h
index 19573bd..a9915a7 100644
--- a/chrome/browser/cocoa/browser_window_controller_private.h
+++ b/chrome/browser/cocoa/browser_window_controller_private.h
@@ -16,8 +16,9 @@
// "dependencies").
@interface BrowserWindowController(Private)
-// Returns YES if vertical tabs are enabled for this browser.
-- (BOOL)useVerticalTabs;
+// Create the appropriate tab strip controller based on whether or not side
+// tabs are enabled. Replaces the current controller.
+- (void)createTabStripController;
// Saves the window's position in the local state preferences.
- (void)saveWindowPositionIfNeeded;
diff --git a/chrome/browser/cocoa/browser_window_controller_private.mm b/chrome/browser/cocoa/browser_window_controller_private.mm
index 2c707a1..9f14c2e 100644
--- a/chrome/browser/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/cocoa/browser_window_controller_private.mm
@@ -15,6 +15,7 @@
#import "chrome/browser/cocoa/find_bar_cocoa_controller.h"
#import "chrome/browser/cocoa/floating_bar_backing_view.h"
#import "chrome/browser/cocoa/fullscreen_controller.h"
+#import "chrome/browser/cocoa/side_tab_strip_controller.h"
#import "chrome/browser/cocoa/tab_strip_controller.h"
#import "chrome/browser/cocoa/tab_strip_view.h"
#import "chrome/browser/cocoa/toolbar_controller.h"
@@ -43,8 +44,17 @@ const CGFloat kLocBarBottomInset = 1;
@implementation BrowserWindowController(Private)
-- (BOOL)useVerticalTabs {
- return browser_->tabstrip_model()->delegate()->UseVerticalTabs();
+// Create the appropriate tab strip controller based on whether or not side
+// tabs are enabled.
+- (void)createTabStripController {
+ Class factory = [TabStripController class];
+ if ([self useVerticalTabs])
+ factory = [SideTabStripController class];
+
+ tabStripController_.reset([[factory alloc]
+ initWithView:[self tabStripView]
+ switchView:[self tabContentArea]
+ browser:browser_.get()]);
}
- (void)saveWindowPositionIfNeeded {
@@ -133,7 +143,7 @@ willPositionSheet:(NSWindow*)sheet
}
- (void)layoutSubviews {
- // With the exception of the tab strip, the subviews which we lay out are
+ // With the exception of the top 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.
@@ -158,9 +168,9 @@ willPositionSheet:(NSWindow*)sheet
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.
+ if ([self hasTabStrip] && ![self useVerticalTabs]) {
+ // If we need to lay out the top tab strip, replace |maxY| and |startMaxY|
+ // with higher values, and then lay out the tab strip.
NSRect windowFrame = [contentView convertRect:[window frame] fromView:nil];
startMaxY = maxY = NSHeight(windowFrame) + yOffset;
maxY = [self layoutTabStripAtMaxY:maxY width:width fullscreen:isFullscreen];
@@ -170,22 +180,20 @@ willPositionSheet:(NSWindow*)sheet
DCHECK_GE(maxY, minY);
DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset);
- // Position the vertical tab strip on the left, taking up the entire height.
- // TODO(pinkerton): Make width not fixed.
- const CGFloat kSidebarWidth = 200.0;
+ // The base class already positions the side tab strip on the left side
+ // of the window's content area and sizes it to take the entire vertical
+ // height. All that's needed here is to push everything over to the right,
+ // if necessary.
if ([self useVerticalTabs]) {
- // TODO(pinkerton): Position the side bar at |minX| when the controller
- // is implemented.
-
- // Push everything else over to the right.
- minX += kSidebarWidth;
- width -= kSidebarWidth;
+ const CGFloat sideTabWidth = [[self tabStripView] bounds].size.width;
+ minX += sideTabWidth;
+ width -= sideTabWidth;
}
// Place the toolbar at the top of the reserved area.
maxY = [self layoutToolbarAtMinX:minX maxY:maxY width:width];
- // If we're not displaying the bookmark bar below the infobar, then it goes
+ // If we're not displaying the bookmark bar below the infobar, then it goes
// immediately below the toolbar.
BOOL placeBookmarkBarBelowInfoBar = [self placeBookmarkBarBelowInfoBar];
if (!placeBookmarkBarBelowInfoBar)
@@ -417,7 +425,9 @@ willPositionSheet:(NSWindow*)sheet
NSView* tabContentView = [self tabContentArea];
NSRect tabContentFrame = [tabContentView frame];
- bool contentShifted = NSMaxY(tabContentFrame) != NSMaxY(newFrame);
+ bool contentShifted =
+ NSMaxY(tabContentFrame) != NSMaxY(newFrame) ||
+ NSMinX(tabContentFrame) != NSMinX(newFrame);
tabContentFrame = newFrame;
[tabContentView setFrame:tabContentFrame];
diff --git a/chrome/browser/cocoa/side_tab_strip_controller.h b/chrome/browser/cocoa/side_tab_strip_controller.h
new file mode 100755
index 0000000..1961842
--- /dev/null
+++ b/chrome/browser/cocoa/side_tab_strip_controller.h
@@ -0,0 +1,19 @@
+// 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 <Cocoa/Cocoa.h>
+
+#import "chrome/browser/cocoa/tab_strip_controller.h"
+
+// A controller for the tab strip when side tabs are enabled.
+//
+// TODO(pinkerton): I'm expecting there are more things here that need
+// overriding rather than just tweaking a couple of settings, so I'm creating
+// a full-blown subclass. Clearly, very little is actually necessary at this
+// point for it to work.
+
+@interface SideTabStripController : TabStripController {
+}
+
+@end
diff --git a/chrome/browser/cocoa/side_tab_strip_controller.mm b/chrome/browser/cocoa/side_tab_strip_controller.mm
new file mode 100755
index 0000000..0f59329
--- /dev/null
+++ b/chrome/browser/cocoa/side_tab_strip_controller.mm
@@ -0,0 +1,29 @@
+// 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/side_tab_strip_controller.h"
+
+@implementation SideTabStripController
+
+// TODO(pinkerton): Still need to figure out several things:
+// - new tab button placement and layout
+// - animating tabs in and out
+// - being able to drop a tab elsewhere besides the 1st position
+// - how to load a different tab view nib for each tab.
+
+- (id)initWithView:(TabStripView*)view
+ switchView:(NSView*)switchView
+ browser:(Browser*)browser {
+ self = [super initWithView:view switchView:switchView browser:browser];
+ if (self) {
+ // Side tabs have no indent since they are not sharing space with the
+ // window controls.
+ [self setIndentForControls:0.0];
+ verticalLayout_ = YES;
+ }
+ return self;
+}
+
+@end
+
diff --git a/chrome/browser/cocoa/side_tab_strip_view.h b/chrome/browser/cocoa/side_tab_strip_view.h
new file mode 100755
index 0000000..b4b4629
--- /dev/null
+++ b/chrome/browser/cocoa/side_tab_strip_view.h
@@ -0,0 +1,15 @@
+// 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 <Cocoa/Cocoa.h>
+
+#import "chrome/browser/cocoa/tab_strip_view.h"
+
+// A class that handles drawing the background of the tab strip when side tabs
+// are enabled.
+
+@interface SideTabStripView : TabStripView {
+}
+
+@end
diff --git a/chrome/browser/cocoa/side_tab_strip_view.mm b/chrome/browser/cocoa/side_tab_strip_view.mm
new file mode 100755
index 0000000..b7bebe0
--- /dev/null
+++ b/chrome/browser/cocoa/side_tab_strip_view.mm
@@ -0,0 +1,43 @@
+// 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/side_tab_strip_view.h"
+
+#include "base/scoped_nsobject.h"
+#import "third_party/GTM/AppKit/GTMNSColor+Luminance.h"
+
+@implementation SideTabStripView
+
+- (void)drawBorder:(NSRect)bounds {
+ // Draw a border on the right side.
+ NSRect borderRect, contentRect;
+ NSDivideRect(bounds, &borderRect, &contentRect, 1, NSMaxXEdge);
+ [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
+ NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
+}
+
+// Override to prevent double-clicks from minimizing the window. The side
+// tab strip doesn't have that behavior (since it's in the window content
+// area).
+- (BOOL)doubleClickMinimizesWindow {
+ return NO;
+}
+
+- (void)drawRect:(NSRect)rect {
+ // BOOL isKey = [[self window] isKeyWindow];
+ NSColor* aColor =
+ [NSColor colorWithCalibratedRed:0.506 green:0.660 blue:0.985 alpha:1.000];
+ NSColor* bColor =
+ [NSColor colorWithCalibratedRed:0.099 green:0.140 blue:0.254 alpha:1.000];
+ scoped_nsobject<NSGradient> gradient(
+ [[NSGradient alloc] initWithStartingColor:aColor endingColor:bColor]);
+
+ NSRect gradientRect = [self bounds];
+ [gradient drawInRect:gradientRect angle:270.0];
+
+ // Draw borders and any drop feedback.
+ [super drawRect:rect];
+}
+
+@end
diff --git a/chrome/browser/cocoa/side_tab_strip_view_unittest.mm b/chrome/browser/cocoa/side_tab_strip_view_unittest.mm
new file mode 100644
index 0000000..79acebf
--- /dev/null
+++ b/chrome/browser/cocoa/side_tab_strip_view_unittest.mm
@@ -0,0 +1,30 @@
+// 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 <Cocoa/Cocoa.h>
+
+#include "base/scoped_nsobject.h"
+#import "chrome/browser/cocoa/side_tab_strip_view.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+class SideTabStripViewTest : public CocoaTest {
+ public:
+ SideTabStripViewTest() {
+ NSRect frame = NSMakeRect(0, 0, 100, 30);
+ scoped_nsobject<SideTabStripView> view(
+ [[SideTabStripView alloc] initWithFrame:frame]);
+ view_ = view.get();
+ [[test_window() contentView] addSubview:view_];
+ }
+
+ SideTabStripView* view_;
+};
+
+TEST_VIEW(SideTabStripViewTest, view_)
+
+} // namespace
diff --git a/chrome/browser/cocoa/tab_strip_controller.h b/chrome/browser/cocoa/tab_strip_controller.h
index 005b9c7..5caf381 100644
--- a/chrome/browser/cocoa/tab_strip_controller.h
+++ b/chrome/browser/cocoa/tab_strip_controller.h
@@ -35,6 +35,10 @@ class ToolbarModel;
NSObject<TabControllerTarget,
URLDropTargetController,
GTMWindowSheetControllerDelegate> {
+ @protected
+ // YES if tabs are to be laid out vertically instead of horizontally.
+ BOOL verticalLayout_;
+
@private
TabContents* currentTab_; // weak, tab for which we're showing state
scoped_nsobject<TabStripView> tabStripView_;
diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm
index 6bf651d..e721414 100644
--- a/chrome/browser/cocoa/tab_strip_controller.mm
+++ b/chrome/browser/cocoa/tab_strip_controller.mm
@@ -360,6 +360,35 @@ private:
[[newTabButton_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
+
+ // Controller may have been (re-)created by switching layout modes, which
+ // means the tab model is already fully formed with tabs. Need to walk the
+ // list and create the UI for each.
+ const int existingTabCount = tabStripModel_->count();
+ const TabContents* selection = tabStripModel_->GetSelectedTabContents();
+ for (int i = 0; i < existingTabCount; ++i) {
+ TabContents* currentContents = tabStripModel_->GetTabContentsAt(i);
+ [self insertTabWithContents:currentContents
+ atIndex:i
+ inForeground:NO];
+ if (selection == currentContents) {
+ // Must manually force a selection since the model won't send
+ // selection messages in this scenario.
+ [self selectTabWithContents:currentContents
+ previousContents:NULL
+ atIndex:i
+ userGesture:NO];
+ }
+ }
+ // Don't lay out the tabs until after the controller has been fully
+ // constructed. The |verticalLayout_| flag has not been initialized by
+ // subclasses at this point, which would cause layout to potentially use
+ // the wrong mode.
+ if (existingTabCount) {
+ [self performSelectorOnMainThread:@selector(layoutTabs)
+ withObject:nil
+ waitUntilDone:NO];
+ }
}
return self;
}
@@ -671,39 +700,47 @@ private:
// mini-tabs have a fixed width. We may not be able to use the entire width
// if the user is quickly closing tabs. This may be negative, but that's okay
// (taken care of by |MAX()| when calculating tab sizes).
- CGFloat availableWidth = 0;
- if ([self inRapidClosureMode]) {
- availableWidth = availableResizeWidth_;
+ CGFloat availableSpace = 0;
+ if (verticalLayout_) {
+ availableSpace = NSHeight([tabStripView_ bounds]);
} else {
- availableWidth = NSWidth([tabStripView_ frame]);
- // Account for the new tab button and the incognito badge.
- availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset;
- if (browser_->profile()->IsOffTheRecord())
- availableWidth -= kIncognitoBadgeTabStripShrink;
+ if ([self inRapidClosureMode]) {
+ availableSpace = availableResizeWidth_;
+ } else {
+ availableSpace = NSWidth([tabStripView_ frame]);
+ // Account for the new tab button and the incognito badge.
+ availableSpace -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset;
+ if (browser_->profile()->IsOffTheRecord())
+ availableSpace -= kIncognitoBadgeTabStripShrink;
+ }
+ availableSpace -= [self indentForControls];
}
- availableWidth -= [self indentForControls];
// This may be negative, but that's okay (taken care of by |MAX()| when
- // calculating tab sizes).
- CGFloat availableWidthForNonMini = availableWidth -
- [self numberOfOpenMiniTabs] * (kMiniTabWidth - kTabOverlap);
+ // calculating tab sizes). "mini" tabs in horizontal mode just get a special
+ // section, they don't change size.
+ CGFloat availableSpaceForNonMini = availableSpace;
+ if (!verticalLayout_) {
+ availableSpaceForNonMini -=
+ [self numberOfOpenMiniTabs] * (kMiniTabWidth - kTabOverlap);
+ }
// Initialize |nonMiniTabWidth| in case there aren't any non-mini-tabs; this
// value shouldn't actually be used.
CGFloat nonMiniTabWidth = kMaxTabWidth;
const NSInteger numberOfOpenNonMiniTabs = [self numberOfOpenNonMiniTabs];
- if (numberOfOpenNonMiniTabs) { // Find the width of a non-mini-tab.
- // Add in the amount we "get back" from the tabs overlapping.
- availableWidthForNonMini += (numberOfOpenNonMiniTabs - 1) * kTabOverlap;
+ if (!verticalLayout_ && numberOfOpenNonMiniTabs) {
+ // Find the width of a non-mini-tab. This only applies to horizontal
+ // mode. Add in the amount we "get back" from the tabs overlapping.
+ availableSpaceForNonMini += (numberOfOpenNonMiniTabs - 1) * kTabOverlap;
// Divide up the space between the non-mini-tabs.
- nonMiniTabWidth = availableWidthForNonMini / numberOfOpenNonMiniTabs;
+ nonMiniTabWidth = availableSpaceForNonMini / numberOfOpenNonMiniTabs;
// Clamp the width between the max and min.
nonMiniTabWidth = MAX(MIN(nonMiniTabWidth, kMaxTabWidth), kMinTabWidth);
}
- const CGFloat minX = NSMinX(placeholderFrame_);
BOOL visible = [[tabStripView_ window] isVisible];
CGFloat offset = [self indentForControls];
@@ -717,9 +754,13 @@ private:
BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_];
NSRect tabFrame = [[tab view] frame];
tabFrame.size.height = [[self class] defaultTabHeight] + 1;
- tabFrame.origin.y = 0;
- tabFrame.origin.x = offset;
-
+ if (verticalLayout_) {
+ tabFrame.origin.y = availableSpace - tabFrame.size.height - offset;
+ tabFrame.origin.x = 0;
+ } else {
+ tabFrame.origin.y = 0;
+ tabFrame.origin.x = offset;
+ }
// If the tab is hidden, we consider it a new tab. We make it visible
// and animate it in.
BOOL newTab = [[tab view] isHidden];
@@ -732,7 +773,10 @@ private:
// We need a duration or else it doesn't cancel an inflight animation.
ScopedNSAnimationContextGroup localAnimationGroup(animate);
localAnimationGroup.SetCurrentContextShortestDuration();
- tabFrame.origin.x = placeholderFrame_.origin.x;
+ if (verticalLayout_)
+ tabFrame.origin.y = availableSpace - tabFrame.size.height - offset;
+ else
+ tabFrame.origin.x = placeholderFrame_.origin.x;
// TODO(alcor): reenable this
//tabFrame.size.height += 10.0 * placeholderStretchiness_;
id target = animate ? [[tab view] animator] : [tab view];
@@ -745,13 +789,26 @@ private:
continue;
}
- // If our left edge is to the left of the placeholder's left, but our mid is
- // to the right of it we should slide over to make space for it.
- if (placeholderTab_ && !hasPlaceholderGap && NSMidX(tabFrame) > minX) {
- hasPlaceholderGap = true;
- offset += NSWidth(placeholderFrame_);
- offset -= kTabOverlap;
- tabFrame.origin.x = offset;
+ if (placeholderTab_ && !hasPlaceholderGap) {
+ const CGFloat placeholderMin =
+ verticalLayout_ ? NSMinY(placeholderFrame_) :
+ NSMinX(placeholderFrame_);
+ if (verticalLayout_) {
+ if (NSMidY(tabFrame) > placeholderMin) {
+ hasPlaceholderGap = true;
+ offset += NSHeight(placeholderFrame_);
+ tabFrame.origin.y = availableSpace - tabFrame.size.height - offset;
+ }
+ } else {
+ // If the left edge is to the left of the placeholder's left, but the
+ // mid is to the right of it slide over to make space for it.
+ if (NSMidX(tabFrame) > placeholderMin) {
+ hasPlaceholderGap = true;
+ offset += NSWidth(placeholderFrame_);
+ offset -= kTabOverlap;
+ tabFrame.origin.x = offset;
+ }
+ }
}
// Set the width. Selected tabs are slightly wider when things get really
@@ -762,6 +819,7 @@ private:
// Animate a new tab in by putting it below the horizon unless told to put
// it in a specific location (i.e., from a drop).
+ // TODO(pinkerton): figure out vertical tab animations.
if (newTab && visible && animate) {
if (NSEqualRects(droppedTabFrame_, NSZeroRect)) {
[[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))];
@@ -784,8 +842,12 @@ private:
enclosingRect = NSUnionRect(tabFrame, enclosingRect);
- offset += NSWidth(tabFrame);
- offset -= kTabOverlap;
+ if (verticalLayout_) {
+ offset += NSHeight(tabFrame);
+ } else {
+ offset += NSWidth(tabFrame);
+ offset -= kTabOverlap;
+ }
i++;
}
@@ -797,7 +859,7 @@ private:
} else {
NSRect newTabNewFrame = [newTabButton_ frame];
// We've already ensured there's enough space for the new tab button
- // so we don't have to check it against the available width. We do need
+ // so we don't have to check it against the available space. We do need
// to make sure we put it after any placeholder.
newTabNewFrame.origin = NSMakePoint(offset, 0);
newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x,
@@ -1457,7 +1519,8 @@ private:
// transitory subviews (tabs). |-regenerateSubviewList| must be called to
// effectuate the addition.
- (void)addSubviewToPermanentList:(NSView*)aView {
- [permanentSubviews_ addObject:aView];
+ if (aView)
+ [permanentSubviews_ addObject:aView];
}
// Update the subviews, keeping the permanent ones (or, more correctly, putting
diff --git a/chrome/browser/cocoa/tab_strip_view.h b/chrome/browser/cocoa/tab_strip_view.h
index 6816348..770e83d 100644
--- a/chrome/browser/cocoa/tab_strip_view.h
+++ b/chrome/browser/cocoa/tab_strip_view.h
@@ -10,7 +10,8 @@
#include "base/scoped_nsobject.h"
#import "chrome/browser/cocoa/url_drop_target.h"
-// A view class that handles rendering the tab strip
+// A view class that handles rendering the tab strip and drops of URLS with
+// a positioning locator for drop feedback.
@interface TabStripView : NSView<URLDropTarget> {
@private
@@ -34,4 +35,11 @@
@end
+// Protected methods subclasses can override to alter behavior. Clients should
+// not call these directly.
+@interface TabStripView(Protected)
+- (void)drawBottomBorder:(NSRect)bounds;
+- (BOOL)doubleClickMinimizesWindow;
+@end
+
#endif // CHROME_BROWSER_COCOA_TAB_STRIP_VIEW_H_
diff --git a/chrome/browser/cocoa/tab_strip_view.mm b/chrome/browser/cocoa/tab_strip_view.mm
index 25ed090..5149dbf 100644
--- a/chrome/browser/cocoa/tab_strip_view.mm
+++ b/chrome/browser/cocoa/tab_strip_view.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.
@@ -28,21 +28,26 @@
return self;
}
-- (void)drawRect:(NSRect)rect {
- NSRect boundsRect = [self bounds];
+// Draw bottom border (a dark border and light highlight). Each tab is
+// responsible for mimicing this bottom border, unless it's the selected
+// tab.
+- (void)drawBorder:(NSRect)bounds {
NSRect borderRect, contentRect;
- // Draw bottom border (a dark border and light highlight). Each tab is
- // responsible for mimicing this bottom border, unless it's the selected
- // tab.
- borderRect = boundsRect;
+ borderRect = bounds;
borderRect.origin.y = 1;
borderRect.size.height = 1;
[[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
- NSDivideRect(boundsRect, &borderRect, &contentRect, 1, NSMinYEdge);
+ NSDivideRect(bounds, &borderRect, &contentRect, 1, NSMinYEdge);
[[NSColor colorWithCalibratedWhite:0.96 alpha:1.0] set];
NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
+}
+
+- (void)drawRect:(NSRect)rect {
+ NSRect boundsRect = [self bounds];
+
+ [self drawBorder:boundsRect];
// Draw drop-indicator arrow (if appropriate).
// TODO(viettrungluu): this is all a stop-gap measure.
@@ -70,7 +75,7 @@
// Height we have to work with (insetting on the top).
CGFloat availableHeight =
- NSMaxY([self bounds]) - arrowTipPos.y - kArrowTopInset;
+ NSMaxY(boundsRect) - arrowTipPos.y - kArrowTopInset;
DCHECK(availableHeight >= 5);
// Based on the knobs above, calculate actual dimensions which we'll need
@@ -104,6 +109,12 @@
}
}
+// YES if a double-click in the background of the tab strip minimizes the
+// window.
+- (BOOL)doubleClickMinimizesWindow {
+ return YES;
+}
+
// We accept first mouse so clicks onto close/zoom/miniaturize buttons and
// title bar double-clicks are properly detected even when the window is in the
// background.
@@ -113,6 +124,12 @@
// Trap double-clicks and make them miniaturize the browser window.
- (void)mouseUp:(NSEvent*)event {
+ // Bail early if double-clicks are disabled.
+ if (![self doubleClickMinimizesWindow]) {
+ [super mouseUp:event];
+ return;
+ }
+
NSInteger clickCount = [event clickCount];
NSTimeInterval timestamp = [event timestamp];
diff --git a/chrome/browser/cocoa/tab_window_controller.h b/chrome/browser/cocoa/tab_window_controller.h
index 64fe78a..fbdb6da 100644
--- a/chrome/browser/cocoa/tab_window_controller.h
+++ b/chrome/browser/cocoa/tab_window_controller.h
@@ -17,6 +17,13 @@
// between tabs. It needs to be a regular NSView, not something like an NSBox
// because the TabStripController makes certain assumptions about how it can
// swap out subviews.
+//
+// The tab strip can exist in different orientations and window locations,
+// depending on the return value of -usesVerticalTabs. If NO (the default),
+// the tab strip is placed outside the window's content area, overlapping the
+// title area and window controls and will be stretched to fill the width
+// of the window. If YES, the tab strip is vertical and lives within the
+// window's content area. It will be stretched to fill the window's height.
#import <Cocoa/Cocoa.h>
@@ -30,13 +37,20 @@
@interface TabWindowController : NSWindowController<NSWindowDelegate> {
@private
IBOutlet FastResizeView* tabContentArea_;
- IBOutlet TabStripView* tabStripView_;
+ // TODO(pinkerton): Figure out a better way to initialize one or the other
+ // w/out needing both to be in the nib.
+ IBOutlet TabStripView* topTabStripView_;
+ IBOutlet TabStripView* sideTabStripView_;
NSWindow* overlayWindow_; // Used during dragging for window opacity tricks
NSView* cachedContentView_; // Used during dragging for identifying which
// view is the proper content area in the overlay
// (weak)
scoped_nsobject<NSMutableSet> lockedTabs_;
BOOL closeDeferred_; // If YES, call performClose: in removeOverlay:.
+ // Difference between height of window content area and height of the
+ // |tabContentArea_|. Calculated when the window is loaded from the nib and
+ // cached in order to restore the delta when switching tab modes.
+ CGFloat contentAreaHeightDelta_;
}
@property(readonly, nonatomic) TabStripView* tabStripView;
@property(readonly, nonatomic) FastResizeView* tabContentArea;
@@ -127,6 +141,11 @@
// if it does, NO otherwise). The default implementation returns YES.
- (BOOL)hasTabStrip;
+// Returns YES if the tab strip lives in the window content area alongside the
+// tab contents. Returns NO if the tab strip is outside the window content
+// area, along the top of the window.
+- (BOOL)useVerticalTabs;
+
// Get/set whether a particular tab is draggable between windows.
- (BOOL)isTabDraggable:(NSView*)tabView;
- (void)setTab:(NSView*)tabView isDraggable:(BOOL)draggable;
@@ -142,6 +161,14 @@
// Tells the tab strip to forget about this tab in preparation for it being
// put into a different tab strip, such as during a drop on another window.
- (void)detachTabView:(NSView*)view;
+
+// Toggles from one display mode of the tab strip to another. Will automatically
+// call -layoutSubviews to reposition other content.
+- (void)toggleTabStripDisplayMode;
+
+// Called when the size of the window content area has changed. Override to
+// position specific views. Base class implementation does nothing.
+- (void)layoutSubviews;
@end
#endif // CHROME_BROWSER_TAB_WINDOW_CONTROLLER_H_
diff --git a/chrome/browser/cocoa/tab_window_controller.mm b/chrome/browser/cocoa/tab_window_controller.mm
index 2338483..0a7b696 100644
--- a/chrome/browser/cocoa/tab_window_controller.mm
+++ b/chrome/browser/cocoa/tab_window_controller.mm
@@ -39,7 +39,6 @@
@end
@implementation TabWindowController
-@synthesize tabStripView = tabStripView_;
@synthesize tabContentArea = tabContentArea_;
- (id)initWithWindow:(NSWindow*)window {
@@ -49,24 +48,93 @@
return self;
}
+// Add the side tab strip to the left side of the window's content area,
+// making it fill the full height of the content area.
+- (void)addSideTabStripToWindow {
+ NSView* contentView = [[self window] contentView];
+ NSRect contentFrame = [contentView frame];
+ NSRect sideStripFrame =
+ NSMakeRect(0, 0,
+ NSWidth([sideTabStripView_ frame]),
+ NSHeight(contentFrame));
+ [sideTabStripView_ setFrame:sideStripFrame];
+ [contentView addSubview:sideTabStripView_];
+}
+
+// Add the top tab strop to the window, above the content box and add it to the
+// view hierarchy as a sibling of the content view so it can overlap with the
+// window frame.
+- (void)addTopTabStripToWindow {
+ NSRect tabFrame =
+ NSMakeRect(0, NSMaxY(tabFrame),
+ NSWidth([tabContentArea_ frame]),
+ NSHeight([topTabStripView_ frame]));
+ [topTabStripView_ setFrame:tabFrame];
+ NSView* contentParent = [[[self window] contentView] superview];
+ [contentParent addSubview:topTabStripView_];
+}
+
- (void)windowDidLoad {
+ // Cache the difference in height between the window content area and the
+ // tab content area.
+ NSRect tabFrame = [tabContentArea_ frame];
+ NSRect contentFrame = [[[self window] contentView] frame];
+ contentAreaHeightDelta_ = NSHeight(contentFrame) - NSHeight(tabFrame);
+
if ([self hasTabStrip]) {
- // Place the tab bar above the content box and add it to the view hierarchy
- // as a sibling of the content view so it can overlap with the window frame.
- NSRect tabFrame = [tabContentArea_ frame];
- tabFrame.origin = NSMakePoint(0, NSMaxY(tabFrame));
- tabFrame.size.height = NSHeight([tabStripView_ frame]);
- [tabStripView_ setFrame:tabFrame];
- [[[[self window] contentView] superview] addSubview:tabStripView_];
+ if ([self useVerticalTabs]) {
+ // No top tabstrip so remove the tabContentArea offset.
+ tabFrame.size.height = contentFrame.size.height;
+ [tabContentArea_ setFrame:tabFrame];
+ [self addSideTabStripToWindow];
+ } else {
+ [self addTopTabStripToWindow];
+ }
} else {
- // No tabstrip so remove the tabContentArea offset.
- NSRect tabFrame = [tabContentArea_ frame];
- NSRect contentFrame = [[[self window] contentView] frame];
+ // No top tabstrip so remove the tabContentArea offset.
tabFrame.size.height = contentFrame.size.height;
[tabContentArea_ setFrame:tabFrame];
}
}
+// Toggles from one display mode of the tab strip to another. Will automatically
+// call -layoutSubviews to reposition other content.
+- (void)toggleTabStripDisplayMode {
+ // Adjust the size of the tab contents to either use more or less space,
+ // depending on the direction of the toggle. This needs to be done prior to
+ // adding back in the top tab strip as its position is based off the MaxY
+ // of the tab content area.
+ BOOL useVertical = [self useVerticalTabs];
+ NSRect tabContentsFrame = [tabContentArea_ frame];
+ tabContentsFrame.size.height += useVertical ?
+ contentAreaHeightDelta_ : -contentAreaHeightDelta_;
+ [tabContentArea_ setFrame:tabContentsFrame];
+
+ if (useVertical) {
+ // Remove the top tab strip and add the sidebar in.
+ [topTabStripView_ removeFromSuperview];
+ [self addSideTabStripToWindow];
+ } else {
+ // Remove the side tab strip and add the top tab strip as a sibling of the
+ // window's content area.
+ [sideTabStripView_ removeFromSuperview];
+ NSRect tabContentsFrame = [tabContentArea_ frame];
+ tabContentsFrame.size.height -= contentAreaHeightDelta_;
+ [tabContentArea_ setFrame:tabContentsFrame];
+ [self addTopTabStripToWindow];
+ }
+
+ [self layoutSubviews];
+}
+
+// Return the appropriate tab strip based on whether or not side tabs are
+// enabled.
+- (TabStripView*)tabStripView {
+ if ([self useVerticalTabs])
+ return sideTabStripView_;
+ return topTabStripView_;
+}
+
- (void)removeOverlay {
[self setUseOverlay:NO];
if (closeDeferred_) {
@@ -237,6 +305,12 @@
return YES;
}
+- (BOOL)useVerticalTabs {
+ // Subclasses should implement this.
+ NOTIMPLEMENTED();
+ return NO;
+}
+
- (BOOL)isTabDraggable:(NSView*)tabView {
return ![lockedTabs_ containsObject:tabView];
}
@@ -255,4 +329,10 @@
closeDeferred_ = YES;
}
+// Called when the size of the window content area has changed. Override to
+// position specific views. Base class implementation does nothing.
+- (void)layoutSubviews {
+ NOTIMPLEMENTED();
+}
+
@end