Chromium Code Reviews| 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 "app/resource_bundle.h" | 8 #include "app/resource_bundle.h" |
| 9 #include "base/mac_util.h" | 9 #include "base/mac_util.h" |
| 10 #include "base/nsimage_cache_mac.h" | 10 #include "base/nsimage_cache_mac.h" |
| (...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 353 // the ordering in the TabStripModel. This call isn't that expensive, though | 353 // the ordering in the TabStripModel. This call isn't that expensive, though |
| 354 // it is O(n) in the number of tabs. Tabs will animate to their new position | 354 // it is O(n) in the number of tabs. Tabs will animate to their new position |
| 355 // if the window is visible and |animate| is YES. | 355 // if the window is visible and |animate| is YES. |
| 356 // TODO(pinkerton): Handle drag placeholders via proxy objects, perhaps a | 356 // TODO(pinkerton): Handle drag placeholders via proxy objects, perhaps a |
| 357 // subclass of TabContentsController with everything stubbed out or by | 357 // subclass of TabContentsController with everything stubbed out or by |
| 358 // abstracting a base class interface. | 358 // abstracting a base class interface. |
| 359 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized | 359 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized |
| 360 // tabs would cause an overflow. | 360 // tabs would cause an overflow. |
| 361 - (void)layoutTabsWithAnimation:(BOOL)animate | 361 - (void)layoutTabsWithAnimation:(BOOL)animate |
| 362 regenerateSubviews:(BOOL)doUpdate { | 362 regenerateSubviews:(BOOL)doUpdate { |
| 363 // A very short duration used to cancel in-progress animations by making the | |
| 364 // new animation happen "instantly". | |
| 365 const float kInstantly = 0.000001; | |
|
Mark Mentovai
2009/10/13 17:30:23
Can you #include <limits> and use the std::numeric
| |
| 363 const float kTabOverlap = 20.0; | 366 const float kTabOverlap = 20.0; |
| 364 const float kNewTabButtonOffset = 8.0; | 367 const float kNewTabButtonOffset = 8.0; |
| 365 const float kMaxTabWidth = [TabController maxTabWidth]; | 368 const float kMaxTabWidth = [TabController maxTabWidth]; |
| 366 const float kMinTabWidth = [TabController minTabWidth]; | 369 const float kMinTabWidth = [TabController minTabWidth]; |
| 367 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; | 370 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; |
| 368 | 371 |
| 369 NSRect enclosingRect = NSZeroRect; | 372 NSRect enclosingRect = NSZeroRect; |
| 370 [NSAnimationContext beginGrouping]; | 373 [NSAnimationContext beginGrouping]; |
| 371 [[NSAnimationContext currentContext] setDuration:0.2]; | 374 [[NSAnimationContext currentContext] setDuration:0.2]; |
| 372 | 375 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 410 // and animate it in. | 413 // and animate it in. |
| 411 BOOL newTab = [[tab view] isHidden]; | 414 BOOL newTab = [[tab view] isHidden]; |
| 412 if (newTab) { | 415 if (newTab) { |
| 413 [[tab view] setHidden:NO]; | 416 [[tab view] setHidden:NO]; |
| 414 } | 417 } |
| 415 | 418 |
| 416 if (isPlaceholder) { | 419 if (isPlaceholder) { |
| 417 // Move the current tab to the correct location instantly. | 420 // Move the current tab to the correct location instantly. |
| 418 // We need a duration or else it doesn't cancel an inflight animation. | 421 // We need a duration or else it doesn't cancel an inflight animation. |
| 419 [NSAnimationContext beginGrouping]; | 422 [NSAnimationContext beginGrouping]; |
| 420 [[NSAnimationContext currentContext] setDuration:0.000001]; | 423 [[NSAnimationContext currentContext] setDuration:kInstantly]; |
|
Mark Mentovai
2009/10/13 17:30:23
Seems kind of funny to say "set duration to instan
| |
| 421 tabFrame.origin.x = placeholderFrame_.origin.x; | 424 tabFrame.origin.x = placeholderFrame_.origin.x; |
| 422 // TODO(alcor): reenable this | 425 // TODO(alcor): reenable this |
| 423 //tabFrame.size.height += 10.0 * placeholderStretchiness_; | 426 //tabFrame.size.height += 10.0 * placeholderStretchiness_; |
| 424 id target = animate ? [[tab view] animator] : [tab view]; | 427 id target = animate ? [[tab view] animator] : [tab view]; |
| 425 [target setFrame:tabFrame]; | 428 [target setFrame:tabFrame]; |
| 426 [NSAnimationContext endGrouping]; | 429 [NSAnimationContext endGrouping]; |
| 427 | 430 |
| 428 // Store the frame by identifier to aviod redundant calls to animator. | 431 // Store the frame by identifier to aviod redundant calls to animator. |
| 429 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; | 432 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; |
| 430 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] | 433 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] |
| 431 forKey:identifier]; | 434 forKey:identifier]; |
| 432 continue; | 435 continue; |
| 433 } else { | 436 } else { |
| 434 // If our left edge is to the left of the placeholder's left, but our mid | 437 // If our left edge is to the left of the placeholder's left, but our mid |
| 435 // is to the right of it we should slide over to make space for it. | 438 // is to the right of it we should slide over to make space for it. |
| 436 if (placeholderTab_ && gap < 0 && NSMidX(tabFrame) > minX) { | 439 if (placeholderTab_ && gap < 0 && NSMidX(tabFrame) > minX) { |
| 437 gap = i; | 440 gap = i; |
| 438 offset += NSWidth(tabFrame); | 441 offset += NSWidth(tabFrame); |
| 439 offset -= kTabOverlap; | 442 offset -= kTabOverlap; |
| 440 tabFrame.origin.x = offset; | 443 tabFrame.origin.x = offset; |
| 441 } | 444 } |
| 442 | 445 |
| 443 // Animate the tab in by putting it below the horizon, but don't bother | |
| 444 // if we only have 1 tab. | |
| 445 BOOL shouldAnimate = animate && [tabContentsArray_ count] > 1; | |
| 446 if (newTab && visible && shouldAnimate) { | |
| 447 [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; | |
| 448 } | |
| 449 | |
| 450 // Set the width. Selected tabs are slightly wider when things get | 446 // Set the width. Selected tabs are slightly wider when things get |
| 451 // really small and thus we enforce a different minimum width. | 447 // really small and thus we enforce a different minimum width. |
| 452 tabFrame.size.width = | 448 tabFrame.size.width = |
| 453 [tab selected] ? MAX(baseTabWidth, kMinSelectedTabWidth) : | 449 [tab selected] ? MAX(baseTabWidth, kMinSelectedTabWidth) : |
| 454 baseTabWidth; | 450 baseTabWidth; |
| 455 | 451 |
| 452 // Animate a new tab in by putting it below the horizon, but don't bother | |
| 453 // if we only have 1 tab. | |
| 454 BOOL shouldAnimate = animate && [tabContentsArray_ count] > 1; | |
| 455 if (newTab && visible && shouldAnimate) { | |
| 456 [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; | |
| 457 } | |
| 458 | |
| 456 // Check the frame by identifier to avoid redundant calls to animator. | 459 // Check the frame by identifier to avoid redundant calls to animator. |
| 457 id frameTarget = visible && animate ? [[tab view] animator] : [tab view]; | 460 id frameTarget = visible && animate ? [[tab view] animator] : [tab view]; |
| 458 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; | 461 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; |
| 459 NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier]; | 462 NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier]; |
| 460 if (!oldTargetValue || | 463 if (!oldTargetValue || |
| 461 !NSEqualRects([oldTargetValue rectValue], tabFrame)) { | 464 !NSEqualRects([oldTargetValue rectValue], tabFrame)) { |
| 462 [frameTarget setFrame:tabFrame]; | 465 [frameTarget setFrame:tabFrame]; |
| 463 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] | 466 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] |
| 464 forKey:identifier]; | 467 forKey:identifier]; |
| 465 } | 468 } |
| 469 | |
| 466 enclosingRect = NSUnionRect(tabFrame, enclosingRect); | 470 enclosingRect = NSUnionRect(tabFrame, enclosingRect); |
| 467 } | 471 } |
| 468 | 472 |
| 469 offset += NSWidth(tabFrame); | 473 offset += NSWidth(tabFrame); |
| 470 offset -= kTabOverlap; | 474 offset -= kTabOverlap; |
| 471 i++; | 475 i++; |
| 472 } | 476 } |
| 473 | 477 |
| 474 // Hide the new tab button if we're explicitly told to. It may already | 478 // Hide the new tab button if we're explicitly told to. It may already |
| 475 // be hidden, doing it again doesn't hurt. Otherwise position it | 479 // be hidden, doing it again doesn't hurt. Otherwise position it |
| 476 // appropriately, showing it if necessary. | 480 // appropriately, showing it if necessary. |
| 477 if (forceNewTabButtonHidden_) { | 481 if (forceNewTabButtonHidden_) { |
| 478 [newTabButton_ setHidden:YES]; | 482 [newTabButton_ setHidden:YES]; |
| 479 } else { | 483 } else { |
| 480 NSRect newTabNewFrame = [newTabButton_ frame]; | 484 NSRect newTabNewFrame = [newTabButton_ frame]; |
| 481 // We've already ensured there's enough space for the new tab button | 485 // We've already ensured there's enough space for the new tab button |
| 482 // so we don't have to check it against the available width. We do need | 486 // so we don't have to check it against the available width. We do need |
| 483 // to make sure we put it after any placeholder. | 487 // to make sure we put it after any placeholder. |
| 484 newTabNewFrame.origin = NSMakePoint(offset, 0); | 488 newTabNewFrame.origin = NSMakePoint(offset, 0); |
| 485 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, | 489 newTabNewFrame.origin.x = MAX(newTabNewFrame.origin.x, |
| 486 NSMaxX(placeholderFrame_)) + | 490 NSMaxX(placeholderFrame_)) + |
| 487 kNewTabButtonOffset; | 491 kNewTabButtonOffset; |
| 488 if ([tabContentsArray_ count]) | 492 if ([tabContentsArray_ count]) |
| 489 [newTabButton_ setHidden:NO]; | 493 [newTabButton_ setHidden:NO]; |
| 490 | 494 |
| 491 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { | 495 if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) { |
| 492 [newTabButton_ setFrame:newTabNewFrame]; | 496 // Move the new tab button into place. We want to animate the new tab |
| 497 // button if it's moving to the left (closing a tab), but not when it's | |
| 498 // moving to the right (inserting a new tab). If moving right, we need | |
| 499 // to use a very small duration to make sure we cancel any in-flight | |
| 500 // animation to the left. | |
| 501 BOOL movingLeft = NSMinX(newTabNewFrame) < NSMinX(newTabTargetFrame_); | |
| 502 id target = animate ? [newTabButton_ animator] : newTabButton_; | |
| 503 [NSAnimationContext beginGrouping]; | |
|
Mark Mentovai
2009/10/13 17:30:23
Adding this is what fixed the bug I was seeing ear
| |
| 504 if (!movingLeft) | |
| 505 [[NSAnimationContext currentContext] setDuration:kInstantly]; | |
| 506 [target setFrame:newTabNewFrame]; | |
| 507 [NSAnimationContext endGrouping]; | |
| 493 newTabTargetFrame_ = newTabNewFrame; | 508 newTabTargetFrame_ = newTabNewFrame; |
| 494 // Move the new tab button into place. | |
| 495 } | 509 } |
| 496 } | 510 } |
| 497 | 511 |
| 498 [NSAnimationContext endGrouping]; | 512 [NSAnimationContext endGrouping]; |
| 499 [dragBlockingView_ setFrame:enclosingRect]; | 513 [dragBlockingView_ setFrame:enclosingRect]; |
| 500 } | 514 } |
| 501 | 515 |
| 502 // When we're told to layout from the public API we always want to animate. | 516 // When we're told to layout from the public API we always want to animate. |
| 503 - (void)layoutTabs { | 517 - (void)layoutTabs { |
| 504 [self layoutTabsWithAnimation:YES regenerateSubviews:YES]; | 518 [self layoutTabsWithAnimation:YES regenerateSubviews:YES]; |
| (...skipping 474 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 979 BrowserWindowController* controller = | 993 BrowserWindowController* controller = |
| 980 (BrowserWindowController*)[[switchView_ window] windowController]; | 994 (BrowserWindowController*)[[switchView_ window] windowController]; |
| 981 DCHECK(index >= 0); | 995 DCHECK(index >= 0); |
| 982 if (index >= 0) { | 996 if (index >= 0) { |
| 983 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; | 997 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; |
| 984 } | 998 } |
| 985 } | 999 } |
| 986 | 1000 |
| 987 | 1001 |
| 988 @end | 1002 @end |
| OLD | NEW |