| 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 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 59 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); | 59 DCHECK([newTabButton_ isKindOfClass:[NSButton class]]); |
| 60 [newTabButton_ setTarget:nil]; | 60 [newTabButton_ setTarget:nil]; |
| 61 [newTabButton_ setAction:@selector(commandDispatch:)]; | 61 [newTabButton_ setAction:@selector(commandDispatch:)]; |
| 62 [newTabButton_ setTag:IDC_NEW_TAB]; | 62 [newTabButton_ setTag:IDC_NEW_TAB]; |
| 63 targetFrames_.reset([[NSMutableDictionary alloc] init]); | 63 targetFrames_.reset([[NSMutableDictionary alloc] init]); |
| 64 [tabView_ setWantsLayer:YES]; | 64 [tabView_ setWantsLayer:YES]; |
| 65 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] | 65 dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] |
| 66 initWithFrame:NSZeroRect]); | 66 initWithFrame:NSZeroRect]); |
| 67 [view addSubview:dragBlockingView_]; | 67 [view addSubview:dragBlockingView_]; |
| 68 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); | 68 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); |
| 69 |
| 70 // Make sure our TabStripView posts frame change notifications, and |
| 71 // register for them. |
| 72 [tabView_ setPostsFrameChangedNotifications:YES]; |
| 73 [[NSNotificationCenter defaultCenter] addObserver:self |
| 74 selector:@selector(tabViewFrameChanged:) |
| 75 name:NSViewFrameDidChangeNotification |
| 76 object:tabView_]; |
| 69 } | 77 } |
| 70 return self; | 78 return self; |
| 71 } | 79 } |
| 72 | 80 |
| 73 + (CGFloat)defaultTabHeight { | 81 + (CGFloat)defaultTabHeight { |
| 74 return 24.0; | 82 return 24.0; |
| 75 } | 83 } |
| 76 | 84 |
| 77 // Finds the associated TabContentsController at the given |index| and swaps | 85 // Finds the associated TabContentsController at the given |index| and swaps |
| 78 // out the sole child of the contentArea to display its contents. | 86 // out the sole child of the contentArea to display its contents. |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 193 | 201 |
| 194 // Lay out all tabs in the order of their TabContentsControllers, which matches | 202 // Lay out all tabs in the order of their TabContentsControllers, which matches |
| 195 // the ordering in the TabStripModel. This call isn't that expensive, though | 203 // the ordering in the TabStripModel. This call isn't that expensive, though |
| 196 // it is O(n) in the number of tabs. Tabs will animate to their new position | 204 // it is O(n) in the number of tabs. Tabs will animate to their new position |
| 197 // if the window is visible. | 205 // if the window is visible. |
| 198 // TODO(pinkerton): Handle drag placeholders via proxy objects, perhaps a | 206 // TODO(pinkerton): Handle drag placeholders via proxy objects, perhaps a |
| 199 // subclass of TabContentsController with everything stubbed out or by | 207 // subclass of TabContentsController with everything stubbed out or by |
| 200 // abstracting a base class interface. | 208 // abstracting a base class interface. |
| 201 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized | 209 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized |
| 202 // tabs would cause an overflow. | 210 // tabs would cause an overflow. |
| 203 - (void)layoutTabs { | 211 - (void)layoutTabsCurrent { |
| 204 const float kIndentLeavingSpaceForControls = 64.0; | 212 const float kIndentLeavingSpaceForControls = 64.0; |
| 205 const float kTabOverlap = 20.0; | 213 const float kTabOverlap = 20.0; |
| 206 const float kNewTabButtonOffset = 8.0; | 214 const float kNewTabButtonOffset = 8.0; |
| 207 const float kMaxTabWidth = [TabController maxTabWidth]; | 215 const float kMaxTabWidth = [TabController maxTabWidth]; |
| 208 const float kMinTabWidth = [TabController minTabWidth]; | 216 const float kMinTabWidth = [TabController minTabWidth]; |
| 209 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; | 217 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; |
| 210 | 218 |
| 211 NSRect enclosingRect = NSZeroRect; | 219 NSRect enclosingRect = NSZeroRect; |
| 212 [NSAnimationContext beginGrouping]; | 220 [NSAnimationContext beginGrouping]; |
| 213 [[NSAnimationContext currentContext] setDuration:0.2]; | 221 [[NSAnimationContext currentContext] setDuration:0.2]; |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 323 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { | 331 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { |
| 324 [newTabButton_ setFrame:newTabNewFrame]; | 332 [newTabButton_ setFrame:newTabNewFrame]; |
| 325 newTabTargetFrame_ = newTabNewFrame; | 333 newTabTargetFrame_ = newTabNewFrame; |
| 326 // Move the new tab button into place. | 334 // Move the new tab button into place. |
| 327 } | 335 } |
| 328 | 336 |
| 329 [NSAnimationContext endGrouping]; | 337 [NSAnimationContext endGrouping]; |
| 330 [dragBlockingView_ setFrame:enclosingRect]; | 338 [dragBlockingView_ setFrame:enclosingRect]; |
| 331 } | 339 } |
| 332 | 340 |
| 341 ////////////////////////////////////// |
| 342 // FIXME -- this is only temporary! // |
| 343 ////////////////////////////////////// |
| 344 - (void)layoutTabsVeryTemp { |
| 345 const float kIndentLeavingSpaceForControls = 64.0; |
| 346 const float kTabOverlap = 20.0; |
| 347 const float kNewTabButtonOffset = 8.0; |
| 348 const float kMaxTabWidth = [TabController maxTabWidth]; |
| 349 const float kMinTabWidth = [TabController minTabWidth]; |
| 350 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; |
| 351 |
| 352 NSRect enclosingRect = NSZeroRect; |
| 353 |
| 354 // Compute the base width of tabs given how much size we have available. |
| 355 float availableWidth = |
| 356 NSWidth([tabView_ frame]) - NSWidth([newTabButton_ frame]) - |
| 357 kNewTabButtonOffset - kIndentLeavingSpaceForControls; |
| 358 // Add back in the amount we "get back" from the tabs overlapping. |
| 359 availableWidth += [tabContentsArray_ count] * kTabOverlap; |
| 360 const float baseTabWidth = |
| 361 MAX(MIN(availableWidth / [tabContentsArray_ count], |
| 362 kMaxTabWidth), |
| 363 kMinTabWidth); |
| 364 |
| 365 CGFloat minX = NSMinX(placeholderFrame_); |
| 366 BOOL visible = [[tabView_ window] isVisible]; |
| 367 |
| 368 float offset = kIndentLeavingSpaceForControls; |
| 369 NSUInteger i = 0; |
| 370 NSInteger gap = -1; |
| 371 NSView* previousTab = nil; |
| 372 for (TabController* tab in tabArray_.get()) { |
| 373 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; |
| 374 NSRect tabFrame = [[tab view] frame]; |
| 375 tabFrame.size.height = [[self class] defaultTabHeight]; |
| 376 tabFrame.origin.y = 0; |
| 377 tabFrame.origin.x = offset; |
| 378 |
| 379 // If the tab is hidden, we consider it a new tab. We make it visible |
| 380 // and animate it in. |
| 381 BOOL newTab = [[tab view] isHidden]; |
| 382 if (newTab) { |
| 383 [[tab view] setHidden:NO]; |
| 384 } |
| 385 |
| 386 if (isPlaceholder) { |
| 387 // Move the current tab to the correct location intantly. |
| 388 tabFrame.origin.x = placeholderFrame_.origin.x; |
| 389 // TODO(alcor): reenable this |
| 390 //tabFrame.size.height += 10.0 * placeholderStretchiness_; |
| 391 [[tab view] setFrame:tabFrame]; |
| 392 |
| 393 // Store the frame by identifier to aviod redundant calls to animator. |
| 394 NSValue *identifier = [NSValue valueWithPointer:[tab view]]; |
| 395 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] |
| 396 forKey:identifier]; |
| 397 continue; |
| 398 } else { |
| 399 // If our left edge is to the left of the placeholder's left, but our mid |
| 400 // is to the right of it we should slide over to make space for it. |
| 401 if (placeholderTab_ && gap < 0 && NSMidX(tabFrame) > minX) { |
| 402 gap = i; |
| 403 offset += NSWidth(tabFrame); |
| 404 offset -= kTabOverlap; |
| 405 tabFrame.origin.x = offset; |
| 406 } |
| 407 |
| 408 // Animate the tab in by putting it below the horizon. |
| 409 if (newTab && visible) { |
| 410 [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; |
| 411 } |
| 412 |
| 413 // Set the width. Selected tabs are slightly wider when things get |
| 414 // really small and thus we enforce a different minimum width. |
| 415 tabFrame.size.width = |
| 416 [tab selected] ? MAX(baseTabWidth, kMinSelectedTabWidth) : |
| 417 baseTabWidth; |
| 418 |
| 419 // Check the frame by identifier to avoid redundant calls to animator. |
| 420 id frameTarget = [tab view]; |
| 421 NSValue *identifier = [NSValue valueWithPointer:[tab view]]; |
| 422 NSValue *oldTargetValue = [targetFrames_ objectForKey:identifier]; |
| 423 if (!oldTargetValue || |
| 424 !NSEqualRects([oldTargetValue rectValue], tabFrame)) { |
| 425 [frameTarget setFrame:tabFrame]; |
| 426 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] |
| 427 forKey:identifier]; |
| 428 } |
| 429 enclosingRect = NSUnionRect(tabFrame, enclosingRect); |
| 430 } |
| 431 |
| 432 // Ensure the current tab is "below" the tab before it in z-order so that |
| 433 // all the tab overlaps are consistent. The selected tab is always the |
| 434 // frontmost, but it's already been made frontmost when the tab was selected |
| 435 // so we don't need to do anything about it here. It will get put back into |
| 436 // place when another tab is selected. |
| 437 if (![tab selected]) { |
| 438 [tabView_ addSubview:[tab view] |
| 439 positioned:NSWindowBelow |
| 440 relativeTo:previousTab]; |
| 441 } |
| 442 previousTab = [tab view]; |
| 443 |
| 444 offset += NSWidth(tabFrame); |
| 445 offset -= kTabOverlap; |
| 446 i++; |
| 447 } |
| 448 |
| 449 NSRect newTabNewFrame = [newTabButton_ frame]; |
| 450 newTabNewFrame.origin = |
| 451 NSMakePoint(MIN(availableWidth, offset + kNewTabButtonOffset), 0); |
| 452 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, |
| 453 NSMaxX(placeholderFrame_)); |
| 454 if (i > 0 && [newTabButton_ isHidden]) { |
| 455 [newTabButton_ setHidden:NO]; |
| 456 } |
| 457 |
| 458 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { |
| 459 [newTabButton_ setFrame:newTabNewFrame]; |
| 460 newTabTargetFrame_ = newTabNewFrame; |
| 461 // Move the new tab button into place. |
| 462 } |
| 463 |
| 464 [dragBlockingView_ setFrame:enclosingRect]; |
| 465 } |
| 466 |
| 467 // FIXME: Properly produce a unified version of this, which handles both |
| 468 // animated and unanimated cases. |
| 469 - (void)layoutTabsInternal:(BOOL)doAnimate { |
| 470 if (doAnimate) |
| 471 [self layoutTabsCurrent]; |
| 472 else |
| 473 [self layoutTabsVeryTemp]; |
| 474 } |
| 475 |
| 476 // Layout tabs with animation. |
| 477 - (void)layoutTabs { |
| 478 [self layoutTabsInternal:YES]; |
| 479 } |
| 480 |
| 481 // Layout tabs without animation. |
| 482 - (void)layoutTabsFast { |
| 483 [self layoutTabsInternal:NO]; |
| 484 } |
| 485 |
| 333 // Handles setting the title of the tab based on the given |contents|. Uses | 486 // Handles setting the title of the tab based on the given |contents|. Uses |
| 334 // a canned string if |contents| is NULL. | 487 // a canned string if |contents| is NULL. |
| 335 - (void)setTabTitle:(NSViewController*)tab withContents:(TabContents*)contents { | 488 - (void)setTabTitle:(NSViewController*)tab withContents:(TabContents*)contents { |
| 336 NSString* titleString = nil; | 489 NSString* titleString = nil; |
| 337 if (contents) | 490 if (contents) |
| 338 titleString = base::SysUTF16ToNSString(contents->GetTitle()); | 491 titleString = base::SysUTF16ToNSString(contents->GetTitle()); |
| 339 if (![titleString length]) { | 492 if (![titleString length]) { |
| 340 titleString = | 493 titleString = |
| 341 base::SysWideToNSString( | 494 base::SysWideToNSString( |
| 342 l10n_util::GetString(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED)); | 495 l10n_util::GetString(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED)); |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 619 // inherit the current tab's group. | 772 // inherit the current tab's group. |
| 620 tabModel_->InsertTabContentsAt(index, contents, true, false); | 773 tabModel_->InsertTabContentsAt(index, contents, true, false); |
| 621 } | 774 } |
| 622 | 775 |
| 623 - (void)userChangedTheme { | 776 - (void)userChangedTheme { |
| 624 for (TabController* tab in tabArray_.get()) { | 777 for (TabController* tab in tabArray_.get()) { |
| 625 [[tab view] setNeedsDisplay:YES]; | 778 [[tab view] setNeedsDisplay:YES]; |
| 626 } | 779 } |
| 627 } | 780 } |
| 628 | 781 |
| 782 // Called when our TabStripView's frame changes size (i.e., on window resize). |
| 783 // Since resizes are tracked (and yield many events), we need to layout the |
| 784 // tabs quickly. |
| 785 - (void)tabViewFrameChanged:(NSNotification*)notify { |
| 786 [self layoutTabsFast]; |
| 787 } |
| 788 |
| 629 @end | 789 @end |
| OLD | NEW |