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