Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Side by Side Diff: chrome/browser/cocoa/tab_strip_controller.mm

Issue 160116: Implement feature where tabs don't resize when using the close box until the ... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/cocoa/tab_strip_controller.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 11 matching lines...) Expand all
22 #include "chrome/browser/tab_contents/navigation_controller.h" 22 #include "chrome/browser/tab_contents/navigation_controller.h"
23 #include "chrome/browser/tab_contents/navigation_entry.h" 23 #include "chrome/browser/tab_contents/navigation_entry.h"
24 #include "chrome/browser/tab_contents/tab_contents.h" 24 #include "chrome/browser/tab_contents/tab_contents.h"
25 #include "chrome/browser/tab_contents/tab_contents_view.h" 25 #include "chrome/browser/tab_contents/tab_contents_view.h"
26 #include "chrome/browser/tabs/tab_strip_model.h" 26 #include "chrome/browser/tabs/tab_strip_model.h"
27 #include "grit/generated_resources.h" 27 #include "grit/generated_resources.h"
28 #include "skia/ext/skia_utils_mac.h" 28 #include "skia/ext/skia_utils_mac.h"
29 29
30 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; 30 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged";
31 31
32 // A value to indicate tab layout should use the full available width of the
33 // view.
34 static const float kUseFullAvailableWidth = -1.0;
35
32 // A simple view class that prevents the windowserver from dragging the 36 // A simple view class that prevents the windowserver from dragging the
33 // area behind tabs. Sometimes core animation confuses it. 37 // area behind tabs. Sometimes core animation confuses it.
34 @interface TabStripControllerDragBlockingView : NSView 38 @interface TabStripControllerDragBlockingView : NSView
35 @end 39 @end
36 @implementation TabStripControllerDragBlockingView 40 @implementation TabStripControllerDragBlockingView
37 - (BOOL)mouseDownCanMoveWindow {return NO;} 41 - (BOOL)mouseDownCanMoveWindow {return NO;}
38 - (void)drawRect:(NSRect)rect {} 42 - (void)drawRect:(NSRect)rect {}
39 @end 43 @end
40 44
45 @interface TabStripController(Private)
46 - (void)installTrackingArea;
47 - (BOOL)useFullWidthForLayout;
48 @end
49
41 @implementation TabStripController 50 @implementation TabStripController
42 51
43 - (id)initWithView:(TabStripView*)view 52 - (id)initWithView:(TabStripView*)view
44 switchView:(NSView*)switchView 53 switchView:(NSView*)switchView
45 model:(TabStripModel*)model { 54 model:(TabStripModel*)model {
46 DCHECK(view && switchView && model); 55 DCHECK(view && switchView && model);
47 if ((self = [super init])) { 56 if ((self = [super init])) {
48 tabView_ = view; 57 tabView_ = view;
49 switchView_ = switchView; 58 switchView_ = switchView;
50 tabModel_ = model; 59 tabModel_ = model;
51 bridge_.reset(new TabStripModelObserverBridge(tabModel_, self)); 60 bridge_.reset(new TabStripModelObserverBridge(tabModel_, self));
52 tabContentsArray_.reset([[NSMutableArray alloc] init]); 61 tabContentsArray_.reset([[NSMutableArray alloc] init]);
53 tabArray_.reset([[NSMutableArray alloc] init]); 62 tabArray_.reset([[NSMutableArray alloc] init]);
54 // Take the only child view present in the nib as the new tab button. For 63 // Take the only child view present in the nib as the new tab button. For
55 // some reason, if the view is present in the nib apriori, it draws 64 // some reason, if the view is present in the nib apriori, it draws
56 // correctly. If we create it in code and add it to the tab view, it draws 65 // correctly. If we create it in code and add it to the tab view, it draws
57 // with all sorts of crazy artifacts. 66 // with all sorts of crazy artifacts.
58 newTabButton_ = [[tabView_ subviews] objectAtIndex:0]; 67 newTabButton_ = [[tabView_ subviews] objectAtIndex:0];
59 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); 68 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]);
60 [newTabButton_ setTarget:nil]; 69 [newTabButton_ setTarget:nil];
61 [newTabButton_ setAction:@selector(commandDispatch:)]; 70 [newTabButton_ setAction:@selector(commandDispatch:)];
62 [newTabButton_ setTag:IDC_NEW_TAB]; 71 [newTabButton_ setTag:IDC_NEW_TAB];
63 targetFrames_.reset([[NSMutableDictionary alloc] init]); 72 targetFrames_.reset([[NSMutableDictionary alloc] init]);
64 [tabView_ setWantsLayer:YES]; 73 [tabView_ setWantsLayer:YES];
65 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] 74 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc]
66 initWithFrame:NSZeroRect]); 75 initWithFrame:NSZeroRect]);
67 [view addSubview:dragBlockingView_]; 76 [view addSubview:dragBlockingView_];
68 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); 77 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0);
78 availableResizeWidth_ = kUseFullAvailableWidth;
69 79
70 // Watch for notifications that the tab strip view has changed size so 80 // Watch for notifications that the tab strip view has changed size so
71 // we can tell it to layout for the new size. 81 // we can tell it to layout for the new size.
72 [[NSNotificationCenter defaultCenter] 82 [[NSNotificationCenter defaultCenter]
73 addObserver:self 83 addObserver:self
74 selector:@selector(tabViewFrameChanged:) 84 selector:@selector(tabViewFrameChanged:)
75 name:NSViewFrameDidChangeNotification 85 name:NSViewFrameDidChangeNotification
76 object:tabView_]; 86 object:tabView_];
77 } 87 }
78 return self; 88 return self;
79 } 89 }
80 90
81 - (void)dealloc { 91 - (void)dealloc {
92 [tabView_ removeTrackingArea:closeTabTrackingArea_.get()];
82 [[NSNotificationCenter defaultCenter] removeObserver:self]; 93 [[NSNotificationCenter defaultCenter] removeObserver:self];
83 [super dealloc]; 94 [super dealloc];
84 } 95 }
85 96
86 + (CGFloat)defaultTabHeight { 97 + (CGFloat)defaultTabHeight {
87 return 24.0; 98 return 24.0;
88 } 99 }
89 100
90 // Finds the associated TabContentsController at the given |index| and swaps 101 // Finds the associated TabContentsController at the given |index| and swaps
91 // out the sole child of the contentArea to display its contents. 102 // out the sole child of the contentArea to display its contents.
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 // get the associated view. Returns nil if out of range. 161 // get the associated view. Returns nil if out of range.
151 - (NSView*)viewAtIndex:(NSUInteger)index { 162 - (NSView*)viewAtIndex:(NSUInteger)index {
152 if (index >= [tabArray_ count]) 163 if (index >= [tabArray_ count])
153 return NULL; 164 return NULL;
154 return [[tabArray_ objectAtIndex:index] view]; 165 return [[tabArray_ objectAtIndex:index] view];
155 } 166 }
156 167
157 // Called when the user clicks a tab. Tell the model the selection has changed, 168 // Called when the user clicks a tab. Tell the model the selection has changed,
158 // which feeds back into us via a notification. 169 // which feeds back into us via a notification.
159 - (void)selectTab:(id)sender { 170 - (void)selectTab:(id)sender {
171 DCHECK([sender isKindOfClass:[NSView class]]);
160 int index = [self indexForTabView:sender]; 172 int index = [self indexForTabView:sender];
161 if (index >= 0 && tabModel_->ContainsIndex(index)) 173 if (index >= 0 && tabModel_->ContainsIndex(index))
162 tabModel_->SelectTabContentsAt(index, true); 174 tabModel_->SelectTabContentsAt(index, true);
163 } 175 }
164 176
165 // Called when the user closes a tab. Asks the model to close the tab. 177 // Called when the user closes a tab. Asks the model to close the tab. |sender|
178 // is the TabView that is potentially going away.
166 - (void)closeTab:(id)sender { 179 - (void)closeTab:(id)sender {
180 DCHECK([sender isKindOfClass:[NSView class]]);
167 int index = [self indexForTabView:sender]; 181 int index = [self indexForTabView:sender];
168 if (tabModel_->ContainsIndex(index)) { 182 if (tabModel_->ContainsIndex(index)) {
169 TabContents* contents = tabModel_->GetTabContentsAt(index); 183 TabContents* contents = tabModel_->GetTabContentsAt(index);
170 if (contents) 184 if (contents)
171 UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile()); 185 UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile());
172 if ([self numberOfTabViews] > 1) { 186 if ([self numberOfTabViews] > 1) {
187 // Limit the width available for laying out tabs so that tabs are not
188 // resized until a later time (when the mouse leaves the tab strip).
189 NSView* penultimateTab = [self viewAtIndex:[tabArray_ count] - 2];
rohitrao (ping after 24h) 2009/07/24 20:35:57 Will we have to revisit this once we implement rea
190 availableResizeWidth_ = NSMaxX([penultimateTab frame]);
191 [self installTrackingArea];
173 tabModel_->CloseTabContentsAt(index); 192 tabModel_->CloseTabContentsAt(index);
174 } else { 193 } else {
175 // Use the standard window close if this is the last tab 194 // Use the standard window close if this is the last tab
176 // this prevents the tab from being removed from the model until after 195 // this prevents the tab from being removed from the model until after
177 // the window dissapears 196 // the window dissapears
178 [[tabView_ window] performClose:nil]; 197 [[tabView_ window] performClose:nil];
179 } 198 }
180 } 199 }
181 } 200 }
182 201
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 const float kTabOverlap = 20.0; 237 const float kTabOverlap = 20.0;
219 const float kNewTabButtonOffset = 8.0; 238 const float kNewTabButtonOffset = 8.0;
220 const float kMaxTabWidth = [TabController maxTabWidth]; 239 const float kMaxTabWidth = [TabController maxTabWidth];
221 const float kMinTabWidth = [TabController minTabWidth]; 240 const float kMinTabWidth = [TabController minTabWidth];
222 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; 241 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth];
223 242
224 NSRect enclosingRect = NSZeroRect; 243 NSRect enclosingRect = NSZeroRect;
225 [NSAnimationContext beginGrouping]; 244 [NSAnimationContext beginGrouping];
226 [[NSAnimationContext currentContext] setDuration:0.2]; 245 [[NSAnimationContext currentContext] setDuration:0.2];
227 246
228 // Compute the base width of tabs given how much size we have available. 247 // Compute the base width of tabs given how much room we're allowed. We
229 float availableWidth = 248 // may not be able to use the entire width if the user is quickly closing
230 NSWidth([tabView_ frame]) - NSWidth([newTabButton_ frame]) - 249 // tabs.
231 kNewTabButtonOffset - kIndentLeavingSpaceForControls; 250 float availableWidth = 0;
251 if ([self useFullWidthForLayout]) {
252 availableWidth = NSWidth([tabView_ frame]);
253 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset;
254 } else {
255 availableWidth = availableResizeWidth_;
256 }
257 availableWidth -= kIndentLeavingSpaceForControls;
258
232 // Add back in the amount we "get back" from the tabs overlapping. 259 // Add back in the amount we "get back" from the tabs overlapping.
233 availableWidth += [tabContentsArray_ count] * kTabOverlap; 260 availableWidth += ([tabContentsArray_ count] - 1) * kTabOverlap;
234 const float baseTabWidth = 261 const float baseTabWidth =
235 MAX(MIN(availableWidth / [tabContentsArray_ count], 262 MAX(MIN(availableWidth / [tabContentsArray_ count],
236 kMaxTabWidth), 263 kMaxTabWidth),
237 kMinTabWidth); 264 kMinTabWidth);
238 265
239 CGFloat minX = NSMinX(placeholderFrame_); 266 CGFloat minX = NSMinX(placeholderFrame_);
240 BOOL visible = [[tabView_ window] isVisible]; 267 BOOL visible = [[tabView_ window] isVisible];
241 268
242 float offset = kIndentLeavingSpaceForControls; 269 float offset = kIndentLeavingSpaceForControls;
243 NSUInteger i = 0; 270 NSUInteger i = 0;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 NSValue *oldTargetValue = [targetFrames_ objectForKey:identifier]; 328 NSValue *oldTargetValue = [targetFrames_ objectForKey:identifier];
302 if (!oldTargetValue || 329 if (!oldTargetValue ||
303 !NSEqualRects([oldTargetValue rectValue], tabFrame)) { 330 !NSEqualRects([oldTargetValue rectValue], tabFrame)) {
304 [frameTarget setFrame:tabFrame]; 331 [frameTarget setFrame:tabFrame];
305 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] 332 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
306 forKey:identifier]; 333 forKey:identifier];
307 } 334 }
308 enclosingRect = NSUnionRect(tabFrame, enclosingRect); 335 enclosingRect = NSUnionRect(tabFrame, enclosingRect);
309 } 336 }
310 337
338 #if 0
311 // Ensure the current tab is "below" the tab before it in z-order so that 339 // Ensure the current tab is "below" the tab before it in z-order so that
312 // all the tab overlaps are consistent. The selected tab is always the 340 // all the tab overlaps are consistent. The selected tab is always the
313 // frontmost, but it's already been made frontmost when the tab was selected 341 // frontmost, but it's already been made frontmost when the tab was selected
314 // so we don't need to do anything about it here. It will get put back into 342 // so we don't need to do anything about it here. It will get put back into
315 // place when another tab is selected. 343 // place when another tab is selected.
344 // TODO(pinkerton): this doesn't seem to work in the case where a tab
345 // is opened between existing tabs. Disabling.
316 if (![tab selected]) { 346 if (![tab selected]) {
317 [tabView_ addSubview:[tab view] 347 [tabView_ addSubview:[tab view]
318 positioned:NSWindowBelow 348 positioned:NSWindowBelow
319 relativeTo:previousTab]; 349 relativeTo:previousTab];
320 } 350 }
351 #endif
321 previousTab = [tab view]; 352 previousTab = [tab view];
322 353
323 offset += NSWidth(tabFrame); 354 offset += NSWidth(tabFrame);
324 offset -= kTabOverlap; 355 offset -= kTabOverlap;
325 i++; 356 i++;
326 } 357 }
327 358
328 NSRect newTabNewFrame = [newTabButton_ frame]; 359 NSRect newTabNewFrame = [newTabButton_ frame];
329 newTabNewFrame.origin = 360 if ([self useFullWidthForLayout])
330 NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); 361 newTabNewFrame.origin =
362 NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0);
363 else
364 newTabNewFrame.origin = NSMakePoint(offset + kNewTabButtonOffset, 0);
331 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, 365 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x,
332 NSMaxX(placeholderFrame_)); 366 NSMaxX(placeholderFrame_));
333 if (i > 0 && [newTabButton_ isHidden]) { 367 if (i > 0 && [newTabButton_ isHidden]) {
334 id target = animate ? [newTabButton_ animator] : newTabButton_; 368 id target = animate ? [newTabButton_ animator] : newTabButton_;
335 [target setHidden:NO]; 369 [target setHidden:NO];
336 } 370 }
337 371
338 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { 372 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) {
339 [newTabButton_ setFrame:newTabNewFrame]; 373 [newTabButton_ setFrame:newTabNewFrame];
340 newTabTargetFrame_ = newTabNewFrame; 374 newTabTargetFrame_ = newTabNewFrame;
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 // a little better that just sliding over (maybe?). 428 // a little better that just sliding over (maybe?).
395 [newView setFrame:NSOffsetRect([newView frame], 429 [newView setFrame:NSOffsetRect([newView frame],
396 0, -[[self class] defaultTabHeight])]; 430 0, -[[self class] defaultTabHeight])];
397 431
398 [tabView_ addSubview:newView 432 [tabView_ addSubview:newView
399 positioned:inForeground ? NSWindowAbove : NSWindowBelow 433 positioned:inForeground ? NSWindowAbove : NSWindowBelow
400 relativeTo:nil]; 434 relativeTo:nil];
401 435
402 [self setTabTitle:newController withContents:contents]; 436 [self setTabTitle:newController withContents:contents];
403 437
438 // If a tab is being inserted, we can again use the entire tab strip width
439 // for layout.
440 availableResizeWidth_ = kUseFullAvailableWidth;
441
404 // We don't need to call |-layoutTabs| if the tab will be in the foreground 442 // We don't need to call |-layoutTabs| if the tab will be in the foreground
405 // because it will get called when the new tab is selected by the tab model. 443 // because it will get called when the new tab is selected by the tab model.
406 if (!inForeground) { 444 if (!inForeground) {
407 [self layoutTabs]; 445 [self layoutTabs];
408 } 446 }
409 447
410 // Send a broadcast that the number of tabs have changed. 448 // Send a broadcast that the number of tabs have changed.
411 [[NSNotificationCenter defaultCenter] 449 [[NSNotificationCenter defaultCenter]
412 postNotificationName:kTabStripNumberOfTabsChanged 450 postNotificationName:kTabStripNumberOfTabsChanged
413 object:self]; 451 object:self];
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after
647 } 685 }
648 686
649 // Called when the tab strip view changes size. As we only registered for 687 // Called when the tab strip view changes size. As we only registered for
650 // changes on our view, we know it's only for our view. Layout w/out 688 // changes on our view, we know it's only for our view. Layout w/out
651 // animations since they are blocked by the resize nested runloop. We need 689 // animations since they are blocked by the resize nested runloop. We need
652 // the views to adjust immediately. 690 // the views to adjust immediately.
653 - (void)tabViewFrameChanged:(NSNotification*)info { 691 - (void)tabViewFrameChanged:(NSNotification*)info {
654 [self layoutTabsWithAnimation:NO]; 692 [self layoutTabsWithAnimation:NO];
655 } 693 }
656 694
695 - (BOOL)useFullWidthForLayout {
696 return availableResizeWidth_ == kUseFullAvailableWidth;
697 }
698
699 // Call to install a tracking area that reports mouseEnter/Exit messages so
700 // we can track when the mouse leaves the tab view after closing a tab with
701 // the mouse. Don't install another tracking rect if one is already there.
702 - (void)installTrackingArea {
703 if (closeTabTrackingArea_.get())
704 return;
705 // Note that we pass |NSTrackingInVisibleRect| so the rect is actually
706 // ignored.
707 closeTabTrackingArea_.reset([[NSTrackingArea alloc]
708 initWithRect:[tabView_ bounds]
709 options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
710 NSTrackingInVisibleRect
711 owner:self
712 userInfo:nil]);
713 [tabView_ addTrackingArea:closeTabTrackingArea_.get()];
714 }
715
716 - (void)mouseEntered:(NSEvent*)event {
717 // Do nothing.
718 }
719
720 // Called when the tracking area is in effect which means we're tracking to
721 // see if the user leaves the tab strip with their mouse. When they do,
722 // reset layout to use all available width.
723 - (void)mouseExited:(NSEvent*)event {
724 [tabView_ removeTrackingArea:closeTabTrackingArea_.get()];
725 closeTabTrackingArea_.reset(nil);
726 availableResizeWidth_ = kUseFullAvailableWidth;
727 [self layoutTabs];
728 }
729
657 @end 730 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/tab_strip_controller.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698