diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-18 20:19:31 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-18 20:19:31 +0000 |
commit | 5eb4265b79482c18912fa9afce49d99030450ce5 (patch) | |
tree | b3452901b2d3fbc37b85aaae95479e65bee58603 /chrome | |
parent | d1d74b9f367025fa7363189b7be012d277245341 (diff) | |
download | chromium_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.grd | 9 | ||||
-rw-r--r-- | chrome/app/nibs/TabView.xib | 44 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_controller.h | 18 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_controller.mm | 43 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.mm | 212 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_model_observer_bridge.h | 6 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_model_observer_bridge.mm | 14 |
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]; |