diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 30 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller_private.h | 5 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller_private.mm | 44 | ||||
-rwxr-xr-x | chrome/browser/cocoa/side_tab_strip_controller.h | 19 | ||||
-rwxr-xr-x | chrome/browser/cocoa/side_tab_strip_controller.mm | 29 | ||||
-rwxr-xr-x | chrome/browser/cocoa/side_tab_strip_view.h | 15 | ||||
-rwxr-xr-x | chrome/browser/cocoa/side_tab_strip_view.mm | 43 | ||||
-rw-r--r-- | chrome/browser/cocoa/side_tab_strip_view_unittest.mm | 30 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.h | 4 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.mm | 127 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_view.h | 10 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_view.mm | 35 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_window_controller.h | 29 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_window_controller.mm | 102 |
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 |