summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 20:19:31 +0000
committerviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 20:19:31 +0000
commit5eb4265b79482c18912fa9afce49d99030450ce5 (patch)
treeb3452901b2d3fbc37b85aaae95479e65bee58603 /chrome
parentd1d74b9f367025fa7363189b7be012d277245341 (diff)
downloadchromium_src-5eb4265b79482c18912fa9afce49d99030450ce5.zip
chromium_src-5eb4265b79482c18912fa9afce49d99030450ce5.tar.gz
chromium_src-5eb4265b79482c18912fa9afce49d99030450ce5.tar.bz2
Mac: implement Pin Tab.
Note that, per Apple's HIG, the context menu alternates between Pin/Unpin Tab instead of toggling a checkmark. Changes to TabView.xib: - add Pin Tab item (to match Windows) Still to do: - dragging/dropping tabs needs visual feedback to indicate how pinned state will change (need input from UI team) - on Windows, you can pin a tab by dragging it slightly to the right of the rightmost pinned tab; this needs to be done BUG=25481 TEST=not yet Review URL: http://codereview.chromium.org/375010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32384 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd9
-rw-r--r--chrome/app/nibs/TabView.xib44
-rw-r--r--chrome/browser/cocoa/tab_controller.h18
-rw-r--r--chrome/browser/cocoa/tab_controller.mm43
-rw-r--r--chrome/browser/cocoa/tab_strip_controller.mm212
-rw-r--r--chrome/browser/cocoa/tab_strip_model_observer_bridge.h6
-rw-r--r--chrome/browser/cocoa/tab_strip_model_observer_bridge.mm14
7 files changed, 247 insertions, 99 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index fb9d711..f7e0da7 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3448,6 +3448,15 @@ Keep your key file in a safe place. You will need it to create new versions of y
Bookmark All Tabs...
</message>
</if>
+ <!-- Mac-only Tab Context menu strings -->
+ <if expr="os == 'darwin'">
+ <message name="IDS_TAB_CXMENU_PIN_TAB_MAC" desc="In Title Case: The label of the tab context menu item for pinning a tab (note: not a checkmark-able menu item).">
+ Pin Tab
+ </message>
+ <message name="IDS_TAB_CXMENU_UNPIN_TAB_MAC" desc="In Title Case: The label of the tab context menu item for unpinning a tab.">
+ Unpin Tab
+ </message>
+ </if>
<!-- Items in the bookmarks destination mode -->
<message name="IDS_BOOKMARKS_DEST_MODE_BOOKMARK_BAR_ON_BOOKMARK_BAR" desc="Text shown when entry is on the bookmark bar">
diff --git a/chrome/app/nibs/TabView.xib b/chrome/app/nibs/TabView.xib
index 7072af9..299ed98 100644
--- a/chrome/app/nibs/TabView.xib
+++ b/chrome/app/nibs/TabView.xib
@@ -222,6 +222,15 @@
<reference key="NSMixedImage" ref="689949759"/>
<int key="NSTag">3</int>
</object>
+ <object class="NSMenuItem" id="95603257">
+ <reference key="NSMenu" ref="398259350"/>
+ <string key="NSTitle">^IDS_TAB_CXMENU_PIN_TAB_MAC</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="530899574"/>
+ <reference key="NSMixedImage" ref="689949759"/>
+ <int key="NSTag">9</int>
+ </object>
<object class="NSMenuItem" id="336880767">
<reference key="NSMenu" ref="398259350"/>
<bool key="NSIsDisabled">YES</bool>
@@ -489,6 +498,22 @@
</object>
<int key="connectionID">101</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">commandDispatch:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="95603257"/>
+ </object>
+ <int key="connectionID">103</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="398259350"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">104</int>
+ </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -590,6 +615,7 @@
<reference ref="716790779"/>
<reference ref="761670747"/>
<reference ref="553885136"/>
+ <reference ref="95603257"/>
</object>
<reference key="parent" ref="581261482"/>
</object>
@@ -672,6 +698,11 @@
<reference key="object" ref="553885136"/>
<reference key="parent" ref="398259350"/>
</object>
+ <object class="IBObjectRecord">
+ <int key="objectID">102</int>
+ <reference key="object" ref="95603257"/>
+ <reference key="parent" ref="398259350"/>
+ </object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -685,6 +716,7 @@
<string>1.WindowOrigin</string>
<string>1.editorWindowContentRectSynchronizationRect</string>
<string>100.IBPluginDependency</string>
+ <string>102.IBPluginDependency</string>
<string>50.IBPluginDependency</string>
<string>51.IBPluginDependency</string>
<string>55.IBPluginDependency</string>
@@ -721,7 +753,8 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>{{702, 528}, {343, 213}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{358, 508}, {343, 233}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -761,7 +794,7 @@
</object>
</object>
<nil key="sourceID"/>
- <int key="maxID">101</int>
+ <int key="maxID">104</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -822,6 +855,13 @@
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">NSMenuItem</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">browser/cocoa/nsmenuitem_additions.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
diff --git a/chrome/browser/cocoa/tab_controller.h b/chrome/browser/cocoa/tab_controller.h
index 1bc9300..6f13db5 100644
--- a/chrome/browser/cocoa/tab_controller.h
+++ b/chrome/browser/cocoa/tab_controller.h
@@ -43,9 +43,10 @@ enum TabLoadingState {
NSRect originalIconFrame_; // frame of iconView_ as loaded from nib
BOOL isIconShowing_; // last state of iconView_ in updateVisibility
BOOL selected_;
+ BOOL pinned_;
TabLoadingState loadingState_;
- float iconTitleXOffset_; // between left edges of icon and title
- float titleCloseWidthOffset_; // between right edges of icon and close button
+ CGFloat iconTitleXOffset_; // between left edges of icon and title
+ CGFloat titleCloseWidthOffset_; // between right edges of icon and close btn.
id<TabControllerTarget> target_; // weak, where actions are sent
SEL action_; // selector sent when tab is selected by clicking
}
@@ -53,15 +54,17 @@ enum TabLoadingState {
@property(assign, nonatomic) TabLoadingState loadingState;
@property(assign, nonatomic) BOOL selected;
+@property(assign, nonatomic) BOOL pinned;
@property(assign, nonatomic) id target;
@property(assign, nonatomic) SEL action;
// Minimum and maximum allowable tab width. The minimum width does not show
// the icon or the close button. The selected tab always has at least a close
// button so it has a different minimum width.
-+ (float)minTabWidth;
-+ (float)maxTabWidth;
-+ (float)minSelectedTabWidth;
++ (CGFloat)minTabWidth;
++ (CGFloat)maxTabWidth;
++ (CGFloat)minSelectedTabWidth;
++ (CGFloat)pinnedTabWidth;
// The view associated with this controller, pre-casted as a TabView
- (TabView*)tabView;
@@ -83,6 +86,11 @@ enum TabLoadingState {
// Ideally, tabs would know about their own animation and wouldn't need this.
- (BOOL)inRapidClosureMode;
+// Updates the visibility of certain subviews, such as the icon and close
+// button, based on criteria such as the tab's selected state and its current
+// width.
+- (void)updateVisibility;
+
// Update the title color to match the tabs current state.
- (void)updateTitleColor;
@end
diff --git a/chrome/browser/cocoa/tab_controller.mm b/chrome/browser/cocoa/tab_controller.mm
index 94de7cfc..36f514f 100644
--- a/chrome/browser/cocoa/tab_controller.mm
+++ b/chrome/browser/cocoa/tab_controller.mm
@@ -2,28 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "app/l10n_util_mac.h"
#include "base/mac_util.h"
#import "chrome/browser/cocoa/tab_controller.h"
#import "chrome/browser/cocoa/tab_controller_target.h"
#import "chrome/browser/cocoa/tab_view.h"
+#include "grit/generated_resources.h"
#import "third_party/GTM/AppKit/GTMTheme.h"
-@interface TabController(Private)
-- (void)updateVisibility;
-@end
-
@implementation TabController
@synthesize loadingState = loadingState_;
+@synthesize pinned = pinned_;
@synthesize target = target_;
@synthesize action = action_;
// 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 button width.
-+ (float)minTabWidth { return 31; }
-+ (float)minSelectedTabWidth { return 47; }
-+ (float)maxTabWidth { return 220; }
++ (CGFloat)minTabWidth { return 31; }
++ (CGFloat)minSelectedTabWidth { return 47; }
++ (CGFloat)maxTabWidth { return 220; }
++ (CGFloat)pinnedTabWidth { return 53; }
- (TabView*)tabView {
return static_cast<TabView*>([self view]);
@@ -141,8 +141,8 @@
// tab. We never actually do this, but it's a helpful guide for determining
// how much space we have available.
- (int)iconCapacity {
- float width = NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_);
- float iconWidth = NSWidth(originalIconFrame_);
+ CGFloat width = NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_);
+ CGFloat iconWidth = NSWidth(originalIconFrame_);
return width / iconWidth;
}
@@ -155,6 +155,9 @@
if (!iconView_)
return NO;
+ if ([self pinned])
+ return YES;
+
int iconCapacity = [self iconCapacity];
if ([self selected])
return iconCapacity >= 2;
@@ -164,12 +167,11 @@
// Returns YES if we should be showing the close button. The selected tab
// always shows the close button.
- (BOOL)shouldShowCloseButton {
- return [self selected] || [self iconCapacity] >= 3;
+ if ([self pinned])
+ return NO;
+ return ([self selected] || [self iconCapacity] >= 3);
}
-// Updates the visibility of certain subviews, such as the icon and close
-// button, based on criteria such as the tab's selected state and its current
-// width.
- (void)updateVisibility {
// iconView_ may have been replaced or it may be nil, so [iconView_ isHidden]
// won't work. Instead, the state of the icon is tracked separately in
@@ -180,6 +182,9 @@
[iconView_ setHidden:newShowIcon ? NO : YES];
isIconShowing_ = newShowIcon;
+ // If the tab is pinned, hide the title.
+ [titleView_ setHidden:[self pinned]];
+
BOOL oldShowCloseButton = [closeButton_ isHidden] ? NO : YES;
BOOL newShowCloseButton = [self shouldShowCloseButton] ? YES : NO;
@@ -254,4 +259,16 @@
return NO;
}
+// Delegate method for context menu. Called before the menu is displayed to
+// update the menu. We need to display "Pin Tab" when the tab is not pinned and
+// "Unpin Tab" when it is (this is not a checkmark menu item, per Apple's HIG).
+- (void)menuNeedsUpdate:(NSMenu*)menu {
+ const int pinTabMenuItemTag = TabStripModel::CommandTogglePinned;
+ NSMenuItem* togglePinned = [menu itemWithTag:pinTabMenuItemTag];
+ NSString* menuItemText = l10n_util::GetNSStringWithFixup(
+ [self pinned] ? IDS_TAB_CXMENU_UNPIN_TAB_MAC
+ : IDS_TAB_CXMENU_PIN_TAB_MAC);
+ [togglePinned setTitle:menuItemText];
+}
+
@end
diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm
index 9da7250..b619239 100644
--- a/chrome/browser/cocoa/tab_strip_controller.mm
+++ b/chrome/browser/cocoa/tab_strip_controller.mm
@@ -42,11 +42,11 @@ NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged";
// A value to indicate tab layout should use the full available width of the
// view.
-static const float kUseFullAvailableWidth = -1.0;
+static const CGFloat kUseFullAvailableWidth = -1.0;
// Left-side indent for tab layout so tabs don't overlap with the window
// controls.
-static const float kIndentLeavingSpaceForControls = 64.0;
+static const CGFloat kIndentLeavingSpaceForControls = 64.0;
// Time (in seconds) in which tabs animate to their final position.
static const NSTimeInterval kAnimationDuration = 0.2;
@@ -107,6 +107,9 @@ private:
- (void)animationDidStopForController:(TabController*)controller
finished:(BOOL)finished;
- (NSInteger)indexFromModelIndex:(NSInteger)index;
+- (NSInteger)numberOfOpenTabs;
+- (NSInteger)numberOfOpenPinnedTabs;
+- (NSInteger)numberOfOpenUnpinnedTabs;
- (void)mouseMoved:(NSEvent*)event;
- (void)setTabTrackingAreasEnabled:(BOOL)enabled;
@end
@@ -404,12 +407,25 @@ private:
return controller;
}
-// Returns the number of open tabs in the tab strip. This is the number
-// of TabControllers we know about (as there's a 1-to-1 mapping from these
-// controllers to a tab) less the number of closing tabs.
-- (NSInteger)numberOfTabViews {
- NSInteger number = [tabArray_ count] - [closingControllers_ count];
- DCHECK(number >= 0);
+// (Private) Returns the number of open tabs in the tab strip. This is the
+// number of TabControllers we know about (as there's a 1-to-1 mapping from
+// these controllers to a tab) less the number of closing tabs.
+- (NSInteger)numberOfOpenTabs {
+ return static_cast<NSInteger>(tabModel_->count());
+}
+
+// (Private) Returns the number of open, pinned tabs.
+- (NSInteger)numberOfOpenPinnedTabs {
+ // Ask the model for the number of pinned tabs. Note that tabs which are in
+ // the process of closing (i.e., whose controllers are in
+ // |closingControllers_|) have already been removed from the model.
+ return static_cast<NSInteger>(tabModel_->IndexOfFirstNonPinnedTab());
+}
+
+// (Private) Returns the number of open, unpinned tabs.
+- (NSInteger)numberOfOpenUnpinnedTabs {
+ NSInteger number = [self numberOfOpenTabs] - [self numberOfOpenPinnedTabs];
+ DCHECK_GE(number, 0);
return number;
}
@@ -504,20 +520,20 @@ private:
TabContents* contents = tabModel_->GetTabContentsAt(index);
if (contents)
UserMetrics::RecordAction("CloseTab_Mouse", contents->profile());
- const NSInteger numberOfTabViews = [self numberOfTabViews];
- if (numberOfTabViews > 1) {
- bool isClosingLastTab = index == numberOfTabViews - 1;
+ const NSInteger numberOfOpenTabs = [self numberOfOpenTabs];
+ if (numberOfOpenTabs > 1) {
+ bool isClosingLastTab = index == numberOfOpenTabs - 1;
if (!isClosingLastTab) {
// Limit the width available for laying out tabs so that tabs are not
// resized until a later time (when the mouse leaves the tab strip).
// TODO(pinkerton): re-visit when handling tab overflow.
- NSView* penultimateTab = [self viewAtIndex:numberOfTabViews - 2];
+ NSView* penultimateTab = [self viewAtIndex:numberOfOpenTabs - 2];
availableResizeWidth_ = NSMaxX([penultimateTab frame]);
} else {
// If the rightmost tab is closed, change the available width so that
// another tab's close button lands below the cursor (assuming the tabs
// are currently below their maximum width and can grow).
- NSView* lastTab = [self viewAtIndex:numberOfTabViews - 1];
+ NSView* lastTab = [self viewAtIndex:numberOfOpenTabs - 1];
availableResizeWidth_ = NSMaxX([lastTab frame]);
}
tabModel_->CloseTabContentsAt(index);
@@ -583,11 +599,12 @@ private:
if (![tabArray_ count])
return;
- const float kTabOverlap = 20.0;
- const float kNewTabButtonOffset = 8.0;
- const float kMaxTabWidth = [TabController maxTabWidth];
- const float kMinTabWidth = [TabController minTabWidth];
- const float kMinSelectedTabWidth = [TabController minSelectedTabWidth];
+ const CGFloat kTabOverlap = 20.0;
+ const CGFloat kNewTabButtonOffset = 8.0;
+ const CGFloat kMaxTabWidth = [TabController maxTabWidth];
+ const CGFloat kMinTabWidth = [TabController minTabWidth];
+ const CGFloat kMinSelectedTabWidth = [TabController minSelectedTabWidth];
+ const CGFloat kPinnedTabWidth = [TabController pinnedTabWidth];
NSRect enclosingRect = NSZeroRect;
ScopedNSAnimationContextGroup mainAnimationGroup(animate);
@@ -597,10 +614,11 @@ private:
if (doUpdate)
[self regenerateSubviewList];
- // Compute the base width of tabs given how much room we're allowed. We
- // may not be able to use the entire width if the user is quickly closing
- // tabs.
- float availableWidth = 0;
+ // Compute the base width of tabs given how much room we're allowed. Note that
+ // pinned 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_;
} else {
@@ -609,21 +627,30 @@ private:
}
availableWidth -= kIndentLeavingSpaceForControls;
- // Only count the number of tabs that are not in the process of closing.
- const NSUInteger numberOfOpenTabs =
- [tabContentsArray_ count] - [closingControllers_ count];
+ // This may be negative, but that's okay (taken care of by |MAX()| when
+ // calculating tab sizes).
+ CGFloat availableWidthForUnpinned = availableWidth -
+ [self numberOfOpenPinnedTabs] * (kPinnedTabWidth - kTabOverlap);
- // Add back in the amount we "get back" from the tabs overlapping.
- availableWidth += (numberOfOpenTabs - 1) * kTabOverlap;
- const float baseTabWidth =
- MAX(MIN(availableWidth / numberOfOpenTabs,
- kMaxTabWidth),
- kMinTabWidth);
+ // Initialize |unpinnedTabWidth| in case there aren't any unpinned tabs; this
+ // value shouldn't actually be used.
+ CGFloat unpinnedTabWidth = kMaxTabWidth;
+ const NSInteger numberOfOpenUnpinnedTabs = [self numberOfOpenUnpinnedTabs];
+ if (numberOfOpenUnpinnedTabs) { // Find the width of an unpinned tab.
+ // Add in the amount we "get back" from the tabs overlapping.
+ availableWidthForUnpinned += (numberOfOpenUnpinnedTabs - 1) * kTabOverlap;
- CGFloat minX = NSMinX(placeholderFrame_);
+ // Divide up the space between the unpinned tabs.
+ unpinnedTabWidth = availableWidthForUnpinned / numberOfOpenUnpinnedTabs;
+
+ // Clamp the width between the max and min.
+ unpinnedTabWidth = MAX(MIN(unpinnedTabWidth, kMaxTabWidth), kMinTabWidth);
+ }
+
+ const CGFloat minX = NSMinX(placeholderFrame_);
BOOL visible = [[tabView_ window] isVisible];
- float offset = kIndentLeavingSpaceForControls;
+ CGFloat offset = kIndentLeavingSpaceForControls;
NSUInteger i = 0;
bool hasPlaceholderGap = false;
for (TabController* tab in tabArray_.get()) {
@@ -660,47 +687,47 @@ private:
[targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
forKey:identifier];
continue;
- } else {
- // 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;
- }
+ }
- // 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;
-
- // Animate a new tab in by putting it below the horizon unless
- // told to put it in a specific location (ie, from a drop).
- if (newTab && visible && animate) {
- if (NSEqualRects(droppedTabFrame_, NSZeroRect)) {
- [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))];
- } else {
- [[tab view] setFrame:droppedTabFrame_];
- droppedTabFrame_ = NSZeroRect;
- }
- }
+ // 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;
+ }
- // Check the frame by identifier to avoid redundant calls to animator.
- id frameTarget = visible && animate ? [[tab view] animator] : [tab view];
- NSValue* identifier = [NSValue valueWithPointer:[tab view]];
- NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier];
- if (!oldTargetValue ||
- !NSEqualRects([oldTargetValue rectValue], tabFrame)) {
- [frameTarget setFrame:tabFrame];
- [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
- forKey:identifier];
+ // 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 pinned] ? kPinnedTabWidth : unpinnedTabWidth;
+ if ([tab selected])
+ tabFrame.size.width = MAX(tabFrame.size.width, kMinSelectedTabWidth);
+
+ // 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).
+ if (newTab && visible && animate) {
+ if (NSEqualRects(droppedTabFrame_, NSZeroRect)) {
+ [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))];
+ } else {
+ [[tab view] setFrame:droppedTabFrame_];
+ droppedTabFrame_ = NSZeroRect;
}
+ }
- enclosingRect = NSUnionRect(tabFrame, enclosingRect);
+ // Check the frame by identifier to avoid redundant calls to animator.
+ id frameTarget = visible && animate ? [[tab view] animator] : [tab view];
+ NSValue* identifier = [NSValue valueWithPointer:[tab view]];
+ NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier];
+ if (!oldTargetValue ||
+ !NSEqualRects([oldTargetValue rectValue], tabFrame)) {
+ [frameTarget setFrame:tabFrame];
+ [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
+ forKey:identifier];
}
+ enclosingRect = NSUnionRect(tabFrame, enclosingRect);
+
offset += NSWidth(tabFrame);
offset -= kTabOverlap;
i++;
@@ -1027,7 +1054,8 @@ private:
TabController* tabController = [tabArray_ objectAtIndex:index];
bool oldHasIcon = [tabController iconView] != nil;
- bool newHasIcon = contents->ShouldDisplayFavIcon();
+ bool newHasIcon = contents->ShouldDisplayFavIcon() ||
+ tabModel_->IsTabPinned(index); // always show an icon for pinned tabs
TabLoadingState oldState = [tabController loadingState];
TabLoadingState newState = kTabDone;
@@ -1093,23 +1121,47 @@ private:
}
// Called when a tab is moved (usually by drag&drop). Keep our parallel arrays
-// in sync with the tab strip model.
+// in sync with the tab strip model. It can also be pinned/unpinned
+// simultaneously, so we need to take care of that.
- (void)tabMovedWithContents:(TabContents*)contents
- fromIndex:(NSInteger)modelFrom
- toIndex:(NSInteger)modelTo {
+ fromIndex:(NSInteger)modelFrom
+ toIndex:(NSInteger)modelTo
+ pinnedStateChanged:(BOOL)pinnedChanged {
// Take closing tabs into account.
NSInteger from = [self indexFromModelIndex:modelFrom];
NSInteger to = [self indexFromModelIndex:modelTo];
- scoped_nsobject<TabContentsController> movedController(
+ scoped_nsobject<TabContentsController> movedTabContentsController(
[[tabContentsArray_ objectAtIndex:from] retain]);
[tabContentsArray_ removeObjectAtIndex:from];
- [tabContentsArray_ insertObject:movedController.get() atIndex:to];
- scoped_nsobject<TabView> movedView(
+ [tabContentsArray_ insertObject:movedTabContentsController.get()
+ atIndex:to];
+ scoped_nsobject<TabController> movedTabController(
[[tabArray_ objectAtIndex:from] retain]);
+ DCHECK([movedTabController isKindOfClass:[TabController class]]);
[tabArray_ removeObjectAtIndex:from];
- [tabArray_ insertObject:movedView.get() atIndex:to];
+ [tabArray_ insertObject:movedTabController.get() atIndex:to];
+ if (pinnedChanged) {
+ [movedTabController setPinned:(tabModel_->IsTabPinned(modelTo) ? YES : NO)];
+ [self updateFavIconForContents:contents atIndex:modelTo];
+ }
+
+ // TODO(viettrungluu): I don't think this is needed. Investigate. See also
+ // |-tabPinnedStateChangedWithContents:...|.
+ [self layoutTabs];
+}
+
+// Called when a tab is pinned or unpinned without moving.
+- (void)tabPinnedStateChangedWithContents:(TabContents*)contents
+ atIndex:(NSInteger)index {
+ TabController* tabController = [tabArray_ objectAtIndex:index];
+ DCHECK([tabController isKindOfClass:[TabController class]]);
+ [tabController setPinned:(tabModel_->IsTabPinned(index) ? YES : NO)];
+ [self updateFavIconForContents:contents atIndex:index];
+
+ // TODO(viettrungluu): I don't think this is needed. Investigate. See also
+ // |-tabMovedWithContents:...|.
[self layoutTabs];
}
@@ -1176,6 +1228,14 @@ private:
// Insert it into this tab strip. We want it in the foreground and to not
// inherit the current tab's group.
tabModel_->InsertTabContentsAt(index, contents, true, false);
+
+ // The tab's pinned status may have changed.
+ // TODO(viettrungluu): Improve the behaviour for drops at the dividing point
+ // between pinned and unpinned tabs.
+ TabController* tabController = [tabArray_ objectAtIndex:index];
+ DCHECK([tabController isKindOfClass:[TabController class]]);
+ [tabController setPinned:(tabModel_->IsTabPinned(index) ? YES : NO)];
+ [self updateFavIconForContents:contents atIndex:index];
}
// Called when the tab strip view changes size. As we only registered for
diff --git a/chrome/browser/cocoa/tab_strip_model_observer_bridge.h b/chrome/browser/cocoa/tab_strip_model_observer_bridge.h
index 0f3783f..61a3dd7 100644
--- a/chrome/browser/cocoa/tab_strip_model_observer_bridge.h
+++ b/chrome/browser/cocoa/tab_strip_model_observer_bridge.h
@@ -37,6 +37,7 @@ class TabStripModelObserverBridge : public TabStripModelObserver {
bool pinned_state_changed);
virtual void TabChangedAt(TabContents* contents, int index,
TabChangeType change_type);
+ virtual void TabPinnedStateChanged(TabContents* contents, int index);
virtual void TabStripEmpty();
private:
@@ -61,10 +62,13 @@ class TabStripModelObserverBridge : public TabStripModelObserver {
userGesture:(bool)wasUserGesture;
- (void)tabMovedWithContents:(TabContents*)contents
fromIndex:(NSInteger)from
- toIndex:(NSInteger)to;
+ toIndex:(NSInteger)to
+ pinnedStateChanged:(BOOL)pinnedChanged;
- (void)tabChangedWithContents:(TabContents*)contents
atIndex:(NSInteger)index
loadingOnly:(BOOL)loading;
+- (void)tabPinnedStateChangedWithContents:(TabContents*)contents
+ atIndex:(NSInteger)index;
- (void)tabStripEmpty;
@end
diff --git a/chrome/browser/cocoa/tab_strip_model_observer_bridge.mm b/chrome/browser/cocoa/tab_strip_model_observer_bridge.mm
index d11da02..cb89acc 100644
--- a/chrome/browser/cocoa/tab_strip_model_observer_bridge.mm
+++ b/chrome/browser/cocoa/tab_strip_model_observer_bridge.mm
@@ -64,10 +64,11 @@ void TabStripModelObserverBridge::TabMoved(TabContents* contents,
int to_index,
bool pinned_state_changed) {
if ([controller_ respondsToSelector:
- @selector(tabMovedWithContents:fromIndex:toIndex:)]) {
+ @selector(tabMovedWithContents:fromIndex:toIndex:pinnedStateChanged:)]) {
[controller_ tabMovedWithContents:contents
fromIndex:from_index
- toIndex:to_index];
+ toIndex:to_index
+ pinnedStateChanged:(pinned_state_changed ? YES : NO)];
}
}
@@ -87,6 +88,15 @@ void TabStripModelObserverBridge::TabChangedAt(TabContents* contents,
}
}
+void TabStripModelObserverBridge::TabPinnedStateChanged(TabContents* contents,
+ int index) {
+ if ([controller_ respondsToSelector:
+ @selector(tabPinnedStateChangedWithContents:atIndex:)]) {
+ [controller_ tabPinnedStateChangedWithContents:contents
+ atIndex:index];
+ }
+}
+
void TabStripModelObserverBridge::TabStripEmpty() {
if ([controller_ respondsToSelector:@selector(tabStripEmpty)])
[controller_ tabStripEmpty];