| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/cocoa/tab_strip_controller.h" | 5 #import "chrome/browser/cocoa/tab_strip_controller.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "base/mac_util.h" | 8 #include "base/mac_util.h" |
| 9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/app/chrome_dll_resource.h" | 10 #include "chrome/app/chrome_dll_resource.h" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 // A simple view class that prevents the windowserver from dragging the | 40 // A simple view class that prevents the windowserver from dragging the |
| 41 // area behind tabs. Sometimes core animation confuses it. | 41 // area behind tabs. Sometimes core animation confuses it. |
| 42 @interface TabStripControllerDragBlockingView : NSView | 42 @interface TabStripControllerDragBlockingView : NSView |
| 43 @end | 43 @end |
| 44 @implementation TabStripControllerDragBlockingView | 44 @implementation TabStripControllerDragBlockingView |
| 45 - (BOOL)mouseDownCanMoveWindow {return NO;} | 45 - (BOOL)mouseDownCanMoveWindow {return NO;} |
| 46 - (void)drawRect:(NSRect)rect {} | 46 - (void)drawRect:(NSRect)rect {} |
| 47 @end | 47 @end |
| 48 | 48 |
| 49 @interface TabStripController(Private) | 49 @interface TabStripController(Private) |
| 50 - (void)installTrackingArea; | |
| 51 - (BOOL)useFullWidthForLayout; | 50 - (BOOL)useFullWidthForLayout; |
| 52 - (void)addSubviewToPermanentList:(NSView*)aView; | 51 - (void)addSubviewToPermanentList:(NSView*)aView; |
| 53 - (void)regenerateSubviewList; | 52 - (void)regenerateSubviewList; |
| 54 - (NSInteger)indexForContentsView:(NSView*)view; | 53 - (NSInteger)indexForContentsView:(NSView*)view; |
| 55 - (void)updateFavIconForContents:(TabContents*)contents | 54 - (void)updateFavIconForContents:(TabContents*)contents |
| 56 atIndex:(NSInteger)index; | 55 atIndex:(NSInteger)index; |
| 57 @end | 56 @end |
| 58 | 57 |
| 59 @implementation TabStripController | 58 @implementation TabStripController |
| 60 | 59 |
| 61 - (id)initWithView:(TabStripView*)view | 60 - (id)initWithView:(TabStripView*)view |
| 62 switchView:(NSView*)switchView | 61 switchView:(NSView*)switchView |
| 63 browser:(Browser*)browser { | 62 browser:(Browser*)browser { |
| 64 DCHECK(view && switchView && browser); | 63 DCHECK(view && switchView && browser); |
| 65 if ((self = [super init])) { | 64 if ((self = [super init])) { |
| 66 tabView_ = view; | 65 tabView_.reset([view retain]); |
| 67 switchView_ = switchView; | 66 switchView_ = switchView; |
| 68 browser_ = browser; | 67 browser_ = browser; |
| 69 tabModel_ = browser_->tabstrip_model(); | 68 tabModel_ = browser_->tabstrip_model(); |
| 70 bridge_.reset(new TabStripModelObserverBridge(tabModel_, self)); | 69 bridge_.reset(new TabStripModelObserverBridge(tabModel_, self)); |
| 71 tabContentsArray_.reset([[NSMutableArray alloc] init]); | 70 tabContentsArray_.reset([[NSMutableArray alloc] init]); |
| 72 tabArray_.reset([[NSMutableArray alloc] init]); | 71 tabArray_.reset([[NSMutableArray alloc] init]); |
| 73 permanentSubviews_.reset([[NSMutableArray alloc] init]); | 72 permanentSubviews_.reset([[NSMutableArray alloc] init]); |
| 74 | 73 |
| 75 // Take the only child view present in the nib as the new tab button. For | 74 // Take the only child view present in the nib as the new tab button. For |
| 76 // some reason, if the view is present in the nib apriori, it draws | 75 // some reason, if the view is present in the nib apriori, it draws |
| 77 // correctly. If we create it in code and add it to the tab view, it draws | 76 // correctly. If we create it in code and add it to the tab view, it draws |
| 78 // with all sorts of crazy artifacts. | 77 // with all sorts of crazy artifacts. |
| 79 newTabButton_ = [[tabView_ subviews] objectAtIndex:0]; | 78 newTabButton_ = [[tabView_ subviews] objectAtIndex:0]; |
| 80 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); | 79 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); |
| 81 [self addSubviewToPermanentList:newTabButton_]; | 80 [self addSubviewToPermanentList:newTabButton_]; |
| 82 [newTabButton_ setTarget:nil]; | 81 [newTabButton_ setTarget:nil]; |
| 83 [newTabButton_ setAction:@selector(commandDispatch:)]; | 82 [newTabButton_ setAction:@selector(commandDispatch:)]; |
| 84 [newTabButton_ setTag:IDC_NEW_TAB]; | 83 [newTabButton_ setTag:IDC_NEW_TAB]; |
| 85 targetFrames_.reset([[NSMutableDictionary alloc] init]); | 84 targetFrames_.reset([[NSMutableDictionary alloc] init]); |
| 86 [tabView_ setWantsLayer:YES]; | |
| 87 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] | 85 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] |
| 88 initWithFrame:NSZeroRect]); | 86 initWithFrame:NSZeroRect]); |
| 89 [self addSubviewToPermanentList:dragBlockingView_]; | 87 [self addSubviewToPermanentList:dragBlockingView_]; |
| 90 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); | 88 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); |
| 91 availableResizeWidth_ = kUseFullAvailableWidth; | 89 availableResizeWidth_ = kUseFullAvailableWidth; |
| 92 | 90 |
| 93 // Install the permanent subviews. | 91 // Install the permanent subviews. |
| 94 [self regenerateSubviewList]; | 92 [self regenerateSubviewList]; |
| 95 | 93 |
| 96 // Watch for notifications that the tab strip view has changed size so | 94 // Watch for notifications that the tab strip view has changed size so |
| 97 // we can tell it to layout for the new size. | 95 // we can tell it to layout for the new size. |
| 98 [[NSNotificationCenter defaultCenter] | 96 [[NSNotificationCenter defaultCenter] |
| 99 addObserver:self | 97 addObserver:self |
| 100 selector:@selector(tabViewFrameChanged:) | 98 selector:@selector(tabViewFrameChanged:) |
| 101 name:NSViewFrameDidChangeNotification | 99 name:NSViewFrameDidChangeNotification |
| 102 object:tabView_]; | 100 object:tabView_]; |
| 101 |
| 102 trackingArea_.reset([[NSTrackingArea alloc] |
| 103 initWithRect:NSZeroRect // Ignored by NSTrackingInVisibleRect |
| 104 options:NSTrackingMouseEnteredAndExited | |
| 105 NSTrackingMouseMoved | |
| 106 NSTrackingActiveAlways | |
| 107 NSTrackingInVisibleRect |
| 108 owner:self |
| 109 userInfo:nil]); |
| 110 [tabView_ addTrackingArea:trackingArea_.get()]; |
| 103 } | 111 } |
| 104 return self; | 112 return self; |
| 105 } | 113 } |
| 106 | 114 |
| 107 - (void)dealloc { | 115 - (void)dealloc { |
| 108 if (closeTabTrackingArea_.get()) | 116 if (trackingArea_.get()) |
| 109 [tabView_ removeTrackingArea:closeTabTrackingArea_.get()]; | 117 [tabView_ removeTrackingArea:trackingArea_.get()]; |
| 110 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 118 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 111 [super dealloc]; | 119 [super dealloc]; |
| 112 } | 120 } |
| 113 | 121 |
| 114 + (CGFloat)defaultTabHeight { | 122 + (CGFloat)defaultTabHeight { |
| 115 return 24.0; | 123 return 24.0; |
| 116 } | 124 } |
| 117 | 125 |
| 118 // Finds the associated TabContentsController at the given |index| and swaps | 126 // Finds the associated TabContentsController at the given |index| and swaps |
| 119 // out the sole child of the contentArea to display its contents. | 127 // out the sole child of the contentArea to display its contents. |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 DCHECK([sender isKindOfClass:[NSView class]]); | 230 DCHECK([sender isKindOfClass:[NSView class]]); |
| 223 int index = [self indexForTabView:sender]; | 231 int index = [self indexForTabView:sender]; |
| 224 if (index >= 0 && tabModel_->ContainsIndex(index)) | 232 if (index >= 0 && tabModel_->ContainsIndex(index)) |
| 225 tabModel_->SelectTabContentsAt(index, true); | 233 tabModel_->SelectTabContentsAt(index, true); |
| 226 } | 234 } |
| 227 | 235 |
| 228 // Called when the user closes a tab. Asks the model to close the tab. |sender| | 236 // Called when the user closes a tab. Asks the model to close the tab. |sender| |
| 229 // is the TabView that is potentially going away. | 237 // is the TabView that is potentially going away. |
| 230 - (void)closeTab:(id)sender { | 238 - (void)closeTab:(id)sender { |
| 231 DCHECK([sender isKindOfClass:[NSView class]]); | 239 DCHECK([sender isKindOfClass:[NSView class]]); |
| 240 if ([hoveredTab_ isEqual:sender]) { |
| 241 hoveredTab_ = nil; |
| 242 } |
| 232 int index = [self indexForTabView:sender]; | 243 int index = [self indexForTabView:sender]; |
| 233 if (tabModel_->ContainsIndex(index)) { | 244 if (tabModel_->ContainsIndex(index)) { |
| 234 TabContents* contents = tabModel_->GetTabContentsAt(index); | 245 TabContents* contents = tabModel_->GetTabContentsAt(index); |
| 235 if (contents) | 246 if (contents) |
| 236 UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile()); | 247 UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile()); |
| 237 if ([self numberOfTabViews] > 1) { | 248 if ([self numberOfTabViews] > 1) { |
| 238 // Limit the width available for laying out tabs so that tabs are not | 249 // Limit the width available for laying out tabs so that tabs are not |
| 239 // resized until a later time (when the mouse leaves the tab strip). | 250 // resized until a later time (when the mouse leaves the tab strip). |
| 240 // TODO(pinkerton): re-visit when handling tab overflow. | 251 // TODO(pinkerton): re-visit when handling tab overflow. |
| 241 NSView* penultimateTab = [self viewAtIndex:[tabArray_ count] - 2]; | 252 NSView* penultimateTab = [self viewAtIndex:[tabArray_ count] - 2]; |
| 242 availableResizeWidth_ = NSMaxX([penultimateTab frame]); | 253 availableResizeWidth_ = NSMaxX([penultimateTab frame]); |
| 243 [self installTrackingArea]; | |
| 244 tabModel_->CloseTabContentsAt(index); | 254 tabModel_->CloseTabContentsAt(index); |
| 245 } else { | 255 } else { |
| 246 // Use the standard window close if this is the last tab | 256 // Use the standard window close if this is the last tab |
| 247 // this prevents the tab from being removed from the model until after | 257 // this prevents the tab from being removed from the model until after |
| 248 // the window dissapears | 258 // the window dissapears |
| 249 [[tabView_ window] performClose:nil]; | 259 [[tabView_ window] performClose:nil]; |
| 250 } | 260 } |
| 251 } | 261 } |
| 252 } | 262 } |
| 253 | 263 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 | 338 |
| 329 CGFloat minX = NSMinX(placeholderFrame_); | 339 CGFloat minX = NSMinX(placeholderFrame_); |
| 330 BOOL visible = [[tabView_ window] isVisible]; | 340 BOOL visible = [[tabView_ window] isVisible]; |
| 331 | 341 |
| 332 float offset = kIndentLeavingSpaceForControls; | 342 float offset = kIndentLeavingSpaceForControls; |
| 333 NSUInteger i = 0; | 343 NSUInteger i = 0; |
| 334 NSInteger gap = -1; | 344 NSInteger gap = -1; |
| 335 for (TabController* tab in tabArray_.get()) { | 345 for (TabController* tab in tabArray_.get()) { |
| 336 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; | 346 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; |
| 337 NSRect tabFrame = [[tab view] frame]; | 347 NSRect tabFrame = [[tab view] frame]; |
| 338 tabFrame.size.height = [[self class] defaultTabHeight]; | 348 tabFrame.size.height = [[self class] defaultTabHeight] + 1; |
| 339 tabFrame.origin.y = 0; | 349 tabFrame.origin.y = 0; |
| 340 tabFrame.origin.x = offset; | 350 tabFrame.origin.x = offset; |
| 341 | 351 |
| 342 // If the tab is hidden, we consider it a new tab. We make it visible | 352 // If the tab is hidden, we consider it a new tab. We make it visible |
| 343 // and animate it in. | 353 // and animate it in. |
| 344 BOOL newTab = [[tab view] isHidden]; | 354 BOOL newTab = [[tab view] isHidden]; |
| 345 if (newTab) { | 355 if (newTab) { |
| 346 [[tab view] setHidden:NO]; | 356 [[tab view] setHidden:NO]; |
| 347 } | 357 } |
| 348 | 358 |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 556 // Release the tab contents controller so those views get destroyed. This | 566 // Release the tab contents controller so those views get destroyed. This |
| 557 // will remove all the tab content Cocoa views from the hierarchy. A | 567 // will remove all the tab content Cocoa views from the hierarchy. A |
| 558 // subsequent "select tab" notification will follow from the model. To | 568 // subsequent "select tab" notification will follow from the model. To |
| 559 // tell us what to swap in in its absence. | 569 // tell us what to swap in in its absence. |
| 560 [tabContentsArray_ removeObjectAtIndex:index]; | 570 [tabContentsArray_ removeObjectAtIndex:index]; |
| 561 | 571 |
| 562 // Remove the |index|th view from the tab strip | 572 // Remove the |index|th view from the tab strip |
| 563 NSView* tab = [self viewAtIndex:index]; | 573 NSView* tab = [self viewAtIndex:index]; |
| 564 [tab removeFromSuperview]; | 574 [tab removeFromSuperview]; |
| 565 | 575 |
| 576 if ([hoveredTab_ isEqual:tab]) { |
| 577 hoveredTab_ = nil; |
| 578 } |
| 579 |
| 566 NSValue *identifier = [NSValue valueWithPointer:tab]; | 580 NSValue *identifier = [NSValue valueWithPointer:tab]; |
| 567 [targetFrames_ removeObjectForKey:identifier]; | 581 [targetFrames_ removeObjectForKey:identifier]; |
| 568 | 582 |
| 569 // Once we're totally done with the tab, delete its controller | 583 // Once we're totally done with the tab, delete its controller |
| 570 [tabArray_ removeObjectAtIndex:index]; | 584 [tabArray_ removeObjectAtIndex:index]; |
| 571 | 585 |
| 572 // Send a broadcast that the number of tabs have changed. | 586 // Send a broadcast that the number of tabs have changed. |
| 573 [[NSNotificationCenter defaultCenter] | 587 [[NSNotificationCenter defaultCenter] |
| 574 postNotificationName:kTabStripNumberOfTabsChanged | 588 postNotificationName:kTabStripNumberOfTabsChanged |
| 575 object:self]; | 589 object:self]; |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 740 // is no placeholder, it will go at the end. Used when dragging from another | 754 // is no placeholder, it will go at the end. Used when dragging from another |
| 741 // window when we don't have access to the TabContents as part of our strip. | 755 // window when we don't have access to the TabContents as part of our strip. |
| 742 - (void)dropTabContents:(TabContents*)contents { | 756 - (void)dropTabContents:(TabContents*)contents { |
| 743 int index = [self indexOfPlaceholder]; | 757 int index = [self indexOfPlaceholder]; |
| 744 | 758 |
| 745 // Insert it into this tab strip. We want it in the foreground and to not | 759 // Insert it into this tab strip. We want it in the foreground and to not |
| 746 // inherit the current tab's group. | 760 // inherit the current tab's group. |
| 747 tabModel_->InsertTabContentsAt(index, contents, true, false); | 761 tabModel_->InsertTabContentsAt(index, contents, true, false); |
| 748 } | 762 } |
| 749 | 763 |
| 750 - (void)userChangedTheme { | 764 - (void)applyTheme { |
| 751 for (TabController* tab in tabArray_.get()) { | 765 for (TabController* tab in tabArray_.get()) { |
| 752 [[tab view] setNeedsDisplay:YES]; | 766 [tab applyTheme]; |
| 753 } | 767 } |
| 754 } | 768 } |
| 755 | 769 |
| 756 // Called when the tab strip view changes size. As we only registered for | 770 // Called when the tab strip view changes size. As we only registered for |
| 757 // changes on our view, we know it's only for our view. Layout w/out | 771 // changes on our view, we know it's only for our view. Layout w/out |
| 758 // animations since they are blocked by the resize nested runloop. We need | 772 // animations since they are blocked by the resize nested runloop. We need |
| 759 // the views to adjust immediately. Neither the tabs nor their z-order are | 773 // the views to adjust immediately. Neither the tabs nor their z-order are |
| 760 // changed, so we don't need to update the subviews. | 774 // changed, so we don't need to update the subviews. |
| 761 - (void)tabViewFrameChanged:(NSNotification*)info { | 775 - (void)tabViewFrameChanged:(NSNotification*)info { |
| 762 [self layoutTabsWithAnimation:NO regenerateSubviews:NO]; | 776 [self layoutTabsWithAnimation:NO regenerateSubviews:NO]; |
| 763 } | 777 } |
| 764 | 778 |
| 765 - (BOOL)useFullWidthForLayout { | 779 - (BOOL)useFullWidthForLayout { |
| 766 return availableResizeWidth_ == kUseFullAvailableWidth; | 780 return availableResizeWidth_ == kUseFullAvailableWidth; |
| 767 } | 781 } |
| 768 | 782 |
| 769 // Call to install a tracking area that reports mouseEnter/Exit messages so | 783 - (void)mouseMoved:(NSEvent *)event { |
| 770 // we can track when the mouse leaves the tab view after closing a tab with | 784 // Use hit test to figure out what view we are hovering over. |
| 771 // the mouse. Don't install another tracking rect if one is already there. | 785 TabView* targetView = (TabView*)[tabView_ hitTest:[event locationInWindow]]; |
| 772 - (void)installTrackingArea { | 786 if (![targetView isKindOfClass:[TabView class]]) { |
| 773 if (closeTabTrackingArea_.get()) | 787 if ([[targetView superview] isKindOfClass:[TabView class]]) { |
| 774 return; | 788 targetView = (TabView*)[targetView superview]; |
| 775 // Note that we pass |NSTrackingInVisibleRect| so the rect is actually | 789 } else { |
| 776 // ignored. | 790 targetView = nil; |
| 777 closeTabTrackingArea_.reset([[NSTrackingArea alloc] | 791 } |
| 778 initWithRect:[tabView_ bounds] | 792 } |
| 779 options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | | 793 |
| 780 NSTrackingInVisibleRect | 794 if (hoveredTab_ != targetView) { |
| 781 owner:self | 795 [hoveredTab_ mouseExited:nil]; // We don't pass event because moved events |
| 782 userInfo:nil]); | 796 [targetView mouseEntered:nil]; // don't have valid tracking areas |
| 783 [tabView_ addTrackingArea:closeTabTrackingArea_.get()]; | 797 hoveredTab_ = targetView; |
| 798 } else { |
| 799 [hoveredTab_ mouseMoved:event]; |
| 800 } |
| 784 } | 801 } |
| 785 | 802 |
| 786 - (void)mouseEntered:(NSEvent*)event { | 803 - (void)mouseEntered:(NSEvent*)event { |
| 787 // Do nothing. | 804 [self mouseMoved:event]; |
| 788 } | 805 } |
| 789 | 806 |
| 790 // Called when the tracking area is in effect which means we're tracking to | 807 // Called when the tracking area is in effect which means we're tracking to |
| 791 // see if the user leaves the tab strip with their mouse. When they do, | 808 // see if the user leaves the tab strip with their mouse. When they do, |
| 792 // reset layout to use all available width. | 809 // reset layout to use all available width. |
| 793 - (void)mouseExited:(NSEvent*)event { | 810 - (void)mouseExited:(NSEvent*)event { |
| 794 [tabView_ removeTrackingArea:closeTabTrackingArea_.get()]; | |
| 795 closeTabTrackingArea_.reset(nil); | |
| 796 availableResizeWidth_ = kUseFullAvailableWidth; | 811 availableResizeWidth_ = kUseFullAvailableWidth; |
| 812 |
| 813 [hoveredTab_ mouseExited:event]; |
| 814 hoveredTab_ = nil; |
| 797 [self layoutTabs]; | 815 [self layoutTabs]; |
| 798 } | 816 } |
| 799 | 817 |
| 800 // Adds the given subview to (the end of) the list of permanent subviews | 818 // Adds the given subview to (the end of) the list of permanent subviews |
| 801 // (specified from bottom up). These subviews will always be below the | 819 // (specified from bottom up). These subviews will always be below the |
| 802 // transitory subviews (tabs). |-regenerateSubviewList| must be called to | 820 // transitory subviews (tabs). |-regenerateSubviewList| must be called to |
| 803 // effectuate the addition. | 821 // effectuate the addition. |
| 804 - (void)addSubviewToPermanentList:(NSView*)aView { | 822 - (void)addSubviewToPermanentList:(NSView*)aView { |
| 805 [permanentSubviews_ addObject:aView]; | 823 [permanentSubviews_ addObject:aView]; |
| 806 } | 824 } |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 891 BrowserWindowController* controller = | 909 BrowserWindowController* controller = |
| 892 (BrowserWindowController*)[[switchView_ window] windowController]; | 910 (BrowserWindowController*)[[switchView_ window] windowController]; |
| 893 DCHECK(index >= 0); | 911 DCHECK(index >= 0); |
| 894 if (index >= 0) { | 912 if (index >= 0) { |
| 895 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; | 913 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; |
| 896 } | 914 } |
| 897 } | 915 } |
| 898 | 916 |
| 899 | 917 |
| 900 @end | 918 @end |
| OLD | NEW |