OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h" | 5 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h" |
6 | 6 |
7 #import <QuartzCore/QuartzCore.h> | 7 #import <QuartzCore/QuartzCore.h> |
8 | 8 |
9 #include <limits> | 9 #include <limits> |
10 #include <string> | 10 #include <string> |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 | 90 |
91 // The amount by which tabs overlap. | 91 // The amount by which tabs overlap. |
92 const CGFloat kTabOverlap = 20.0; | 92 const CGFloat kTabOverlap = 20.0; |
93 | 93 |
94 // The width and height for a tab's icon. | 94 // The width and height for a tab's icon. |
95 const CGFloat kIconWidthAndHeight = 16.0; | 95 const CGFloat kIconWidthAndHeight = 16.0; |
96 | 96 |
97 // The amount by which the new tab button is offset (from the tabs). | 97 // The amount by which the new tab button is offset (from the tabs). |
98 const CGFloat kNewTabButtonOffset = 8.0; | 98 const CGFloat kNewTabButtonOffset = 8.0; |
99 | 99 |
100 // The amount by which to shrink the tab strip (on the right) when the | |
101 // incognito badge is present. | |
102 const CGFloat kAvatarTabStripShrink = 18; | |
103 | |
104 // Time (in seconds) in which tabs animate to their final position. | 100 // Time (in seconds) in which tabs animate to their final position. |
105 const NSTimeInterval kAnimationDuration = 0.125; | 101 const NSTimeInterval kAnimationDuration = 0.125; |
106 | 102 |
107 // The amount by wich the profile menu button is offset (from tab tabs or new | 103 // The amount by wich the profile menu button is offset (from tab tabs or new |
108 // tab button). | 104 // tab button). |
109 const CGFloat kProfileMenuButtonOffset = 6.0; | 105 const CGFloat kProfileMenuButtonOffset = 6.0; |
110 | 106 |
111 // Helper class for doing NSAnimationContext calls that takes a bool to disable | 107 // Helper class for doing NSAnimationContext calls that takes a bool to disable |
112 // all the work. Useful for code that wants to conditionally animate. | 108 // all the work. Useful for code that wants to conditionally animate. |
113 class ScopedNSAnimationContextGroup { | 109 class ScopedNSAnimationContextGroup { |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
302 // in individual tab positioning (to avoid moving them right back to where they | 298 // in individual tab positioning (to avoid moving them right back to where they |
303 // were). | 299 // were). |
304 // | 300 // |
305 // In order to prevent actions being taken on tabs which are closing, the tab | 301 // In order to prevent actions being taken on tabs which are closing, the tab |
306 // itself gets marked as such so it no longer will send back its select action | 302 // itself gets marked as such so it no longer will send back its select action |
307 // or allow itself to be dragged. In addition, drags on the tab strip as a | 303 // or allow itself to be dragged. In addition, drags on the tab strip as a |
308 // whole are disabled while there are tabs closing. | 304 // whole are disabled while there are tabs closing. |
309 | 305 |
310 @implementation TabStripController | 306 @implementation TabStripController |
311 | 307 |
312 @synthesize indentForControls = indentForControls_; | 308 @synthesize leftIndentForControls = leftIndentForControls_; |
| 309 @synthesize rightIndentForControls = rightIndentForControls_; |
313 | 310 |
314 - (id)initWithView:(TabStripView*)view | 311 - (id)initWithView:(TabStripView*)view |
315 switchView:(NSView*)switchView | 312 switchView:(NSView*)switchView |
316 browser:(Browser*)browser | 313 browser:(Browser*)browser |
317 delegate:(id<TabStripControllerDelegate>)delegate { | 314 delegate:(id<TabStripControllerDelegate>)delegate { |
318 DCHECK(view && switchView && browser && delegate); | 315 DCHECK(view && switchView && browser && delegate); |
319 if ((self = [super init])) { | 316 if ((self = [super init])) { |
320 tabStripView_.reset([view retain]); | 317 tabStripView_.reset([view retain]); |
321 switchView_ = switchView; | 318 switchView_ = switchView; |
322 browser_ = browser; | 319 browser_ = browser; |
323 tabStripModel_ = browser_->tabstrip_model(); | 320 tabStripModel_ = browser_->tabstrip_model(); |
324 hoverTabSelector_.reset(new HoverTabSelector(tabStripModel_)); | 321 hoverTabSelector_.reset(new HoverTabSelector(tabStripModel_)); |
325 delegate_ = delegate; | 322 delegate_ = delegate; |
326 bridge_.reset(new TabStripModelObserverBridge(tabStripModel_, self)); | 323 bridge_.reset(new TabStripModelObserverBridge(tabStripModel_, self)); |
327 dragController_.reset( | 324 dragController_.reset( |
328 [[TabStripDragController alloc] initWithTabStripController:self]); | 325 [[TabStripDragController alloc] initWithTabStripController:self]); |
329 tabContentsArray_.reset([[NSMutableArray alloc] init]); | 326 tabContentsArray_.reset([[NSMutableArray alloc] init]); |
330 tabArray_.reset([[NSMutableArray alloc] init]); | 327 tabArray_.reset([[NSMutableArray alloc] init]); |
331 NSWindow* browserWindow = [view window]; | 328 NSWindow* browserWindow = [view window]; |
332 | 329 |
333 // Important note: any non-tab subviews not added to |permanentSubviews_| | 330 // Important note: any non-tab subviews not added to |permanentSubviews_| |
334 // (see |-addSubviewToPermanentList:|) will be wiped out. | 331 // (see |-addSubviewToPermanentList:|) will be wiped out. |
335 permanentSubviews_.reset([[NSMutableArray alloc] init]); | 332 permanentSubviews_.reset([[NSMutableArray alloc] init]); |
336 | 333 |
337 defaultFavicon_.reset( | 334 defaultFavicon_.reset( |
338 [gfx::GetCachedImageWithName(@"nav.pdf") retain]); | 335 [gfx::GetCachedImageWithName(@"nav.pdf") retain]); |
339 | 336 |
340 [self setIndentForControls:[[self class] defaultIndentForControls]]; | 337 [self setLeftIndentForControls:[[self class] defaultLeftIndentForControls]]; |
| 338 [self setRightIndentForControls:0]; |
341 | 339 |
342 // TODO(viettrungluu): WTF? "For some reason, if the view is present in the | 340 // TODO(viettrungluu): WTF? "For some reason, if the view is present in the |
343 // nib a priori, it draws correctly. If we create it in code and add it to | 341 // nib a priori, it draws correctly. If we create it in code and add it to |
344 // the tab view, it draws with all sorts of crazy artifacts." | 342 // the tab view, it draws with all sorts of crazy artifacts." |
345 newTabButton_ = [view getNewTabButton]; | 343 newTabButton_ = [view getNewTabButton]; |
346 [self addSubviewToPermanentList:newTabButton_]; | 344 [self addSubviewToPermanentList:newTabButton_]; |
347 [newTabButton_ setTarget:nil]; | 345 [newTabButton_ setTarget:nil]; |
348 [newTabButton_ setAction:@selector(commandDispatch:)]; | 346 [newTabButton_ setAction:@selector(commandDispatch:)]; |
349 [newTabButton_ setTag:IDC_NEW_TAB]; | 347 [newTabButton_ setTag:IDC_NEW_TAB]; |
350 | 348 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
462 [[[view animationForKey:@"frameOrigin"] delegate] invalidate]; | 460 [[[view animationForKey:@"frameOrigin"] delegate] invalidate]; |
463 } | 461 } |
464 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 462 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
465 [super dealloc]; | 463 [super dealloc]; |
466 } | 464 } |
467 | 465 |
468 + (CGFloat)defaultTabHeight { | 466 + (CGFloat)defaultTabHeight { |
469 return 25.0; | 467 return 25.0; |
470 } | 468 } |
471 | 469 |
472 + (CGFloat)defaultIndentForControls { | 470 + (CGFloat)defaultLeftIndentForControls { |
473 // Default indentation leaves enough room so tabs don't overlap with the | 471 // Default indentation leaves enough room so tabs don't overlap with the |
474 // window controls. | 472 // window controls. |
475 return 70.0; | 473 return 70.0; |
476 } | 474 } |
477 | 475 |
478 // Finds the TabContentsController associated with the given index into the tab | 476 // Finds the TabContentsController associated with the given index into the tab |
479 // model and swaps out the sole child of the contentArea to display its | 477 // model and swaps out the sole child of the contentArea to display its |
480 // contents. | 478 // contents. |
481 - (void)swapInTabAtIndex:(NSInteger)modelIndex { | 479 - (void)swapInTabAtIndex:(NSInteger)modelIndex { |
482 DCHECK(modelIndex >= 0 && modelIndex < tabStripModel_->count()); | 480 DCHECK(modelIndex >= 0 && modelIndex < tabStripModel_->count()); |
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
783 placeholderStretchiness_ = yStretchiness; | 781 placeholderStretchiness_ = yStretchiness; |
784 [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:NO]; | 782 [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:NO]; |
785 } | 783 } |
786 | 784 |
787 - (BOOL)isDragSessionActive { | 785 - (BOOL)isDragSessionActive { |
788 return placeholderTab_ != nil; | 786 return placeholderTab_ != nil; |
789 } | 787 } |
790 | 788 |
791 - (BOOL)isTabFullyVisible:(TabView*)tab { | 789 - (BOOL)isTabFullyVisible:(TabView*)tab { |
792 NSRect frame = [tab frame]; | 790 NSRect frame = [tab frame]; |
793 return NSMinX(frame) >= [self indentForControls] && | 791 return NSMinX(frame) >= [self leftIndentForControls] && |
794 NSMaxX(frame) <= NSMaxX([tabStripView_ frame]); | 792 NSMaxX(frame) <= (NSMaxX([tabStripView_ frame]) - |
| 793 [self rightIndentForControls]); |
795 } | 794 } |
796 | 795 |
797 - (void)showNewTabButton:(BOOL)show { | 796 - (void)showNewTabButton:(BOOL)show { |
798 forceNewTabButtonHidden_ = show ? NO : YES; | 797 forceNewTabButtonHidden_ = show ? NO : YES; |
799 if (forceNewTabButtonHidden_) | 798 if (forceNewTabButtonHidden_) |
800 [newTabButton_ setHidden:YES]; | 799 [newTabButton_ setHidden:YES]; |
801 } | 800 } |
802 | 801 |
803 // Lay out all tabs in the order of their TabContentsControllers, which matches | 802 // Lay out all tabs in the order of their TabContentsControllers, which matches |
804 // the ordering in the TabStripModel. This call isn't that expensive, though | 803 // the ordering in the TabStripModel. This call isn't that expensive, though |
(...skipping 27 matching lines...) Expand all Loading... |
832 // (taken care of by |MAX()| when calculating tab sizes). | 831 // (taken care of by |MAX()| when calculating tab sizes). |
833 CGFloat availableSpace = 0; | 832 CGFloat availableSpace = 0; |
834 if (verticalLayout_) { | 833 if (verticalLayout_) { |
835 availableSpace = NSHeight([tabStripView_ bounds]); | 834 availableSpace = NSHeight([tabStripView_ bounds]); |
836 } else { | 835 } else { |
837 if ([self inRapidClosureMode]) { | 836 if ([self inRapidClosureMode]) { |
838 availableSpace = availableResizeWidth_; | 837 availableSpace = availableResizeWidth_; |
839 } else { | 838 } else { |
840 availableSpace = NSWidth([tabStripView_ frame]); | 839 availableSpace = NSWidth([tabStripView_ frame]); |
841 | 840 |
842 BrowserWindowController* controller = | 841 // Account for the width of the new tab button. |
843 (BrowserWindowController*)[[tabStripView_ window] windowController]; | |
844 | |
845 // Account for the widths of the new tab button or the avatar, if any/all | |
846 // are present. | |
847 availableSpace -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; | 842 availableSpace -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; |
848 if ([controller respondsToSelector:@selector(shouldShowAvatar)] && | |
849 [controller shouldShowAvatar]) { | |
850 availableSpace -= kAvatarTabStripShrink; | |
851 } | |
852 } | 843 } |
853 availableSpace -= [self indentForControls]; | 844 availableSpace -= [self leftIndentForControls]; |
| 845 availableSpace -= [self rightIndentForControls]; |
854 } | 846 } |
855 | 847 |
856 // This may be negative, but that's okay (taken care of by |MAX()| when | 848 // This may be negative, but that's okay (taken care of by |MAX()| when |
857 // calculating tab sizes). "mini" tabs in horizontal mode just get a special | 849 // calculating tab sizes). "mini" tabs in horizontal mode just get a special |
858 // section, they don't change size. | 850 // section, they don't change size. |
859 CGFloat availableSpaceForNonMini = availableSpace; | 851 CGFloat availableSpaceForNonMini = availableSpace; |
860 if (!verticalLayout_) { | 852 if (!verticalLayout_) { |
861 availableSpaceForNonMini -= | 853 availableSpaceForNonMini -= |
862 [self numberOfOpenMiniTabs] * (kMiniTabWidth - kTabOverlap); | 854 [self numberOfOpenMiniTabs] * (kMiniTabWidth - kTabOverlap); |
863 } | 855 } |
864 | 856 |
865 // Initialize |nonMiniTabWidth| in case there aren't any non-mini-tabs; this | 857 // Initialize |nonMiniTabWidth| in case there aren't any non-mini-tabs; this |
866 // value shouldn't actually be used. | 858 // value shouldn't actually be used. |
867 CGFloat nonMiniTabWidth = kMaxTabWidth; | 859 CGFloat nonMiniTabWidth = kMaxTabWidth; |
868 const NSInteger numberOfOpenNonMiniTabs = [self numberOfOpenNonMiniTabs]; | 860 const NSInteger numberOfOpenNonMiniTabs = [self numberOfOpenNonMiniTabs]; |
869 if (!verticalLayout_ && numberOfOpenNonMiniTabs) { | 861 if (!verticalLayout_ && numberOfOpenNonMiniTabs) { |
870 // Find the width of a non-mini-tab. This only applies to horizontal | 862 // Find the width of a non-mini-tab. This only applies to horizontal |
871 // mode. Add in the amount we "get back" from the tabs overlapping. | 863 // mode. Add in the amount we "get back" from the tabs overlapping. |
872 availableSpaceForNonMini += (numberOfOpenNonMiniTabs - 1) * kTabOverlap; | 864 availableSpaceForNonMini += (numberOfOpenNonMiniTabs - 1) * kTabOverlap; |
873 | 865 |
874 // Divide up the space between the non-mini-tabs. | 866 // Divide up the space between the non-mini-tabs. |
875 nonMiniTabWidth = availableSpaceForNonMini / numberOfOpenNonMiniTabs; | 867 nonMiniTabWidth = availableSpaceForNonMini / numberOfOpenNonMiniTabs; |
876 | 868 |
877 // Clamp the width between the max and min. | 869 // Clamp the width between the max and min. |
878 nonMiniTabWidth = MAX(MIN(nonMiniTabWidth, kMaxTabWidth), kMinTabWidth); | 870 nonMiniTabWidth = MAX(MIN(nonMiniTabWidth, kMaxTabWidth), kMinTabWidth); |
879 } | 871 } |
880 | 872 |
881 BOOL visible = [[tabStripView_ window] isVisible]; | 873 BOOL visible = [[tabStripView_ window] isVisible]; |
882 | 874 |
883 CGFloat offset = [self indentForControls]; | 875 CGFloat offset = [self leftIndentForControls]; |
884 bool hasPlaceholderGap = false; | 876 bool hasPlaceholderGap = false; |
885 for (TabController* tab in tabArray_.get()) { | 877 for (TabController* tab in tabArray_.get()) { |
886 // Ignore a tab that is going through a close animation. | 878 // Ignore a tab that is going through a close animation. |
887 if ([closingControllers_ containsObject:tab]) | 879 if ([closingControllers_ containsObject:tab]) |
888 continue; | 880 continue; |
889 | 881 |
890 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; | 882 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; |
891 NSRect tabFrame = [[tab view] frame]; | 883 NSRect tabFrame = [[tab view] frame]; |
892 tabFrame.size.height = [[self class] defaultTabHeight] + 1; | 884 tabFrame.size.height = [[self class] defaultTabHeight] + 1; |
893 if (verticalLayout_) { | 885 if (verticalLayout_) { |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1035 // Mark that we've successfully completed layout of at least one tab. | 1027 // Mark that we've successfully completed layout of at least one tab. |
1036 initialLayoutComplete_ = YES; | 1028 initialLayoutComplete_ = YES; |
1037 } | 1029 } |
1038 | 1030 |
1039 // When we're told to layout from the public API we usually want to animate, | 1031 // When we're told to layout from the public API we usually want to animate, |
1040 // except when it's the first time. | 1032 // except when it's the first time. |
1041 - (void)layoutTabs { | 1033 - (void)layoutTabs { |
1042 [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:YES]; | 1034 [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:YES]; |
1043 } | 1035 } |
1044 | 1036 |
| 1037 - (void)layoutTabsWithoutAnimation { |
| 1038 [self layoutTabsWithAnimation:NO regenerateSubviews:YES]; |
| 1039 } |
| 1040 |
1045 // Handles setting the title of the tab based on the given |contents|. Uses | 1041 // Handles setting the title of the tab based on the given |contents|. Uses |
1046 // a canned string if |contents| is NULL. | 1042 // a canned string if |contents| is NULL. |
1047 - (void)setTabTitle:(NSViewController*)tab withContents:(TabContents*)contents { | 1043 - (void)setTabTitle:(NSViewController*)tab withContents:(TabContents*)contents { |
1048 NSString* titleString = nil; | 1044 NSString* titleString = nil; |
1049 if (contents) | 1045 if (contents) |
1050 titleString = base::SysUTF16ToNSString(contents->GetTitle()); | 1046 titleString = base::SysUTF16ToNSString(contents->GetTitle()); |
1051 if (![titleString length]) { | 1047 if (![titleString length]) { |
1052 titleString = l10n_util::GetNSString(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED); | 1048 titleString = l10n_util::GetNSString(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED); |
1053 } | 1049 } |
1054 [tab setTitle:titleString]; | 1050 [tab setTitle:titleString]; |
(...skipping 970 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2025 NSInteger index = [self indexFromModelIndex:modelIndex]; | 2021 NSInteger index = [self indexFromModelIndex:modelIndex]; |
2026 BrowserWindowController* controller = | 2022 BrowserWindowController* controller = |
2027 (BrowserWindowController*)[[switchView_ window] windowController]; | 2023 (BrowserWindowController*)[[switchView_ window] windowController]; |
2028 DCHECK(index >= 0); | 2024 DCHECK(index >= 0); |
2029 if (index >= 0) { | 2025 if (index >= 0) { |
2030 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; | 2026 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; |
2031 } | 2027 } |
2032 } | 2028 } |
2033 | 2029 |
2034 @end | 2030 @end |
OLD | NEW |