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 |