| 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 #import <QuartzCore/QuartzCore.h> | 7 #import <QuartzCore/QuartzCore.h> |
| 8 | 8 |
| 9 #include <limits> | 9 #include <limits> |
| 10 | 10 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 // view. | 44 // view. |
| 45 static const float kUseFullAvailableWidth = -1.0; | 45 static const float kUseFullAvailableWidth = -1.0; |
| 46 | 46 |
| 47 // Left-side indent for tab layout so tabs don't overlap with the window | 47 // Left-side indent for tab layout so tabs don't overlap with the window |
| 48 // controls. | 48 // controls. |
| 49 static const float kIndentLeavingSpaceForControls = 64.0; | 49 static const float kIndentLeavingSpaceForControls = 64.0; |
| 50 | 50 |
| 51 // Time (in seconds) in which tabs animate to their final position. | 51 // Time (in seconds) in which tabs animate to their final position. |
| 52 static const NSTimeInterval kAnimationDuration = 0.2; | 52 static const NSTimeInterval kAnimationDuration = 0.2; |
| 53 | 53 |
| 54 namespace { |
| 55 |
| 56 // Helper class for doing NSAnimationContext calls that takes a bool to disable |
| 57 // all the work. Useful for code that wants to conditionally animate. |
| 58 class ScopedNSAnimationContextGroup { |
| 59 public: |
| 60 explicit ScopedNSAnimationContextGroup(bool animate) |
| 61 : animate_(animate) { |
| 62 if (animate_) { |
| 63 [NSAnimationContext beginGrouping]; |
| 64 } |
| 65 } |
| 66 |
| 67 ~ScopedNSAnimationContextGroup() { |
| 68 if (animate_) { |
| 69 [NSAnimationContext endGrouping]; |
| 70 } |
| 71 } |
| 72 |
| 73 void SetCurrentContextDuration(NSTimeInterval duration) { |
| 74 if (animate_) { |
| 75 [[NSAnimationContext currentContext] gtm_setDuration:duration]; |
| 76 } |
| 77 } |
| 78 |
| 79 void SetCurrentContextShortestDuration() { |
| 80 if (animate_) { |
| 81 // The minimum representable time interval. This used to stop an |
| 82 // in-progress animation as quickly as possible. |
| 83 const NSTimeInterval kMinimumTimeInterval = |
| 84 std::numeric_limits<NSTimeInterval>::min(); |
| 85 // Directly set the duration to be short, avoiding the Steve slowmotion |
| 86 // ettect the gtm_setDuration: provides. |
| 87 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; |
| 88 } |
| 89 } |
| 90 private: |
| 91 bool animate_; |
| 92 DISALLOW_COPY_AND_ASSIGN(ScopedNSAnimationContextGroup); |
| 93 }; |
| 94 |
| 95 } // namespace |
| 96 |
| 54 @interface TabStripController(Private) | 97 @interface TabStripController(Private) |
| 55 - (void)installTrackingArea; | 98 - (void)installTrackingArea; |
| 56 - (void)addSubviewToPermanentList:(NSView*)aView; | 99 - (void)addSubviewToPermanentList:(NSView*)aView; |
| 57 - (void)regenerateSubviewList; | 100 - (void)regenerateSubviewList; |
| 58 - (NSInteger)indexForContentsView:(NSView*)view; | 101 - (NSInteger)indexForContentsView:(NSView*)view; |
| 59 - (void)updateFavIconForContents:(TabContents*)contents | 102 - (void)updateFavIconForContents:(TabContents*)contents |
| 60 atIndex:(NSInteger)index; | 103 atIndex:(NSInteger)index; |
| 61 - (void)layoutTabsWithAnimation:(BOOL)animate | 104 - (void)layoutTabsWithAnimation:(BOOL)animate |
| 62 regenerateSubviews:(BOOL)doUpdate; | 105 regenerateSubviews:(BOOL)doUpdate; |
| 63 - (void)animationDidStopForController:(TabController*)controller | 106 - (void)animationDidStopForController:(TabController*)controller |
| (...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 // the ordering in the TabStripModel. This call isn't that expensive, though | 567 // the ordering in the TabStripModel. This call isn't that expensive, though |
| 525 // it is O(n) in the number of tabs. Tabs will animate to their new position | 568 // it is O(n) in the number of tabs. Tabs will animate to their new position |
| 526 // if the window is visible and |animate| is YES. | 569 // if the window is visible and |animate| is YES. |
| 527 // TODO(pinkerton): Handle drag placeholders via proxy objects, perhaps a | 570 // TODO(pinkerton): Handle drag placeholders via proxy objects, perhaps a |
| 528 // subclass of TabContentsController with everything stubbed out or by | 571 // subclass of TabContentsController with everything stubbed out or by |
| 529 // abstracting a base class interface. | 572 // abstracting a base class interface. |
| 530 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized | 573 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized |
| 531 // tabs would cause an overflow. | 574 // tabs would cause an overflow. |
| 532 - (void)layoutTabsWithAnimation:(BOOL)animate | 575 - (void)layoutTabsWithAnimation:(BOOL)animate |
| 533 regenerateSubviews:(BOOL)doUpdate { | 576 regenerateSubviews:(BOOL)doUpdate { |
| 577 DCHECK([NSThread isMainThread]); |
| 534 if (![tabArray_ count]) | 578 if (![tabArray_ count]) |
| 535 return; | 579 return; |
| 536 | 580 |
| 537 // The minimum representable time interval. This can be used as the value | |
| 538 // passed to +[NSAnimationContext setDuration:] to stop an in-progress | |
| 539 // animation as quickly as possible. | |
| 540 const NSTimeInterval kMinimumTimeInterval = | |
| 541 std::numeric_limits<NSTimeInterval>::min(); | |
| 542 const float kTabOverlap = 20.0; | 581 const float kTabOverlap = 20.0; |
| 543 const float kNewTabButtonOffset = 8.0; | 582 const float kNewTabButtonOffset = 8.0; |
| 544 const float kMaxTabWidth = [TabController maxTabWidth]; | 583 const float kMaxTabWidth = [TabController maxTabWidth]; |
| 545 const float kMinTabWidth = [TabController minTabWidth]; | 584 const float kMinTabWidth = [TabController minTabWidth]; |
| 546 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; | 585 const float kMinSelectedTabWidth = [TabController minSelectedTabWidth]; |
| 547 | 586 |
| 548 NSRect enclosingRect = NSZeroRect; | 587 NSRect enclosingRect = NSZeroRect; |
| 549 [NSAnimationContext beginGrouping]; | 588 ScopedNSAnimationContextGroup mainAnimationGroup(animate); |
| 550 [[NSAnimationContext currentContext] gtm_setDuration:kAnimationDuration]; | 589 mainAnimationGroup.SetCurrentContextDuration(kAnimationDuration); |
| 551 | 590 |
| 552 // Update the current subviews and their z-order if requested. | 591 // Update the current subviews and their z-order if requested. |
| 553 if (doUpdate) | 592 if (doUpdate) |
| 554 [self regenerateSubviewList]; | 593 [self regenerateSubviewList]; |
| 555 | 594 |
| 556 // Compute the base width of tabs given how much room we're allowed. We | 595 // Compute the base width of tabs given how much room we're allowed. We |
| 557 // may not be able to use the entire width if the user is quickly closing | 596 // may not be able to use the entire width if the user is quickly closing |
| 558 // tabs. | 597 // tabs. |
| 559 float availableWidth = 0; | 598 float availableWidth = 0; |
| 560 if ([self inRapidClosureMode]) { | 599 if ([self inRapidClosureMode]) { |
| 561 availableWidth = availableResizeWidth_; | 600 availableWidth = availableResizeWidth_; |
| 562 } else { | 601 } else { |
| 563 availableWidth = NSWidth([tabView_ frame]); | 602 availableWidth = NSWidth([tabView_ frame]); |
| 564 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; | 603 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 // If the tab is hidden, we consider it a new tab. We make it visible | 635 // If the tab is hidden, we consider it a new tab. We make it visible |
| 597 // and animate it in. | 636 // and animate it in. |
| 598 BOOL newTab = [[tab view] isHidden]; | 637 BOOL newTab = [[tab view] isHidden]; |
| 599 if (newTab) { | 638 if (newTab) { |
| 600 [[tab view] setHidden:NO]; | 639 [[tab view] setHidden:NO]; |
| 601 } | 640 } |
| 602 | 641 |
| 603 if (isPlaceholder) { | 642 if (isPlaceholder) { |
| 604 // Move the current tab to the correct location instantly. | 643 // Move the current tab to the correct location instantly. |
| 605 // We need a duration or else it doesn't cancel an inflight animation. | 644 // We need a duration or else it doesn't cancel an inflight animation. |
| 606 [NSAnimationContext beginGrouping]; | 645 ScopedNSAnimationContextGroup localAnimationGroup(animate); |
| 607 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; | 646 localAnimationGroup.SetCurrentContextShortestDuration(); |
| 608 tabFrame.origin.x = placeholderFrame_.origin.x; | 647 tabFrame.origin.x = placeholderFrame_.origin.x; |
| 609 // TODO(alcor): reenable this | 648 // TODO(alcor): reenable this |
| 610 //tabFrame.size.height += 10.0 * placeholderStretchiness_; | 649 //tabFrame.size.height += 10.0 * placeholderStretchiness_; |
| 611 id target = animate ? [[tab view] animator] : [tab view]; | 650 id target = animate ? [[tab view] animator] : [tab view]; |
| 612 [target setFrame:tabFrame]; | 651 [target setFrame:tabFrame]; |
| 613 [NSAnimationContext endGrouping]; | |
| 614 | 652 |
| 615 // Store the frame by identifier to aviod redundant calls to animator. | 653 // Store the frame by identifier to aviod redundant calls to animator. |
| 616 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; | 654 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; |
| 617 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] | 655 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] |
| 618 forKey:identifier]; | 656 forKey:identifier]; |
| 619 continue; | 657 continue; |
| 620 } else { | 658 } else { |
| 621 // If our left edge is to the left of the placeholder's left, but our mid | 659 // If our left edge is to the left of the placeholder's left, but our mid |
| 622 // is to the right of it we should slide over to make space for it. | 660 // is to the right of it we should slide over to make space for it. |
| 623 if (placeholderTab_ && !hasPlaceholderGap && NSMidX(tabFrame) > minX) { | 661 if (placeholderTab_ && !hasPlaceholderGap && NSMidX(tabFrame) > minX) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 692 imageName = @"newtab"; | 730 imageName = @"newtab"; |
| 693 } | 731 } |
| 694 [newTabButton_ setImage:nsimage_cache::ImageNamed(imageName)]; | 732 [newTabButton_ setImage:nsimage_cache::ImageNamed(imageName)]; |
| 695 | 733 |
| 696 // Move the new tab button into place. We want to animate the new tab | 734 // Move the new tab button into place. We want to animate the new tab |
| 697 // button if it's moving to the left (closing a tab), but not when it's | 735 // button if it's moving to the left (closing a tab), but not when it's |
| 698 // moving to the right (inserting a new tab). If moving right, we need | 736 // moving to the right (inserting a new tab). If moving right, we need |
| 699 // to use a very small duration to make sure we cancel any in-flight | 737 // to use a very small duration to make sure we cancel any in-flight |
| 700 // animation to the left. | 738 // animation to the left. |
| 701 if (visible && animate) { | 739 if (visible && animate) { |
| 702 [NSAnimationContext beginGrouping]; | 740 ScopedNSAnimationContextGroup localAnimationGroup(true); |
| 703 BOOL movingLeft = NSMinX(newTabNewFrame) < NSMinX(newTabTargetFrame_); | 741 BOOL movingLeft = NSMinX(newTabNewFrame) < NSMinX(newTabTargetFrame_); |
| 704 if (!movingLeft) { | 742 if (!movingLeft) { |
| 705 [[NSAnimationContext currentContext] | 743 localAnimationGroup.SetCurrentContextShortestDuration(); |
| 706 setDuration:kMinimumTimeInterval]; | |
| 707 } | 744 } |
| 708 [[newTabButton_ animator] setFrame:newTabNewFrame]; | 745 [[newTabButton_ animator] setFrame:newTabNewFrame]; |
| 709 newTabTargetFrame_ = newTabNewFrame; | 746 newTabTargetFrame_ = newTabNewFrame; |
| 710 [NSAnimationContext endGrouping]; | |
| 711 } else { | 747 } else { |
| 712 [newTabButton_ setFrame:newTabNewFrame]; | 748 [newTabButton_ setFrame:newTabNewFrame]; |
| 713 newTabTargetFrame_ = newTabNewFrame; | 749 newTabTargetFrame_ = newTabNewFrame; |
| 714 } | 750 } |
| 715 } | 751 } |
| 716 } | 752 } |
| 717 | 753 |
| 718 [NSAnimationContext endGrouping]; | |
| 719 [dragBlockingView_ setFrame:enclosingRect]; | 754 [dragBlockingView_ setFrame:enclosingRect]; |
| 720 | 755 |
| 721 // Mark that we've successfully completed layout of at least one tab. | 756 // Mark that we've successfully completed layout of at least one tab. |
| 722 initialLayoutComplete_ = YES; | 757 initialLayoutComplete_ = YES; |
| 723 } | 758 } |
| 724 | 759 |
| 725 // When we're told to layout from the public API we usually want to animate, | 760 // When we're told to layout from the public API we usually want to animate, |
| 726 // except when it's the first time. | 761 // except when it's the first time. |
| 727 - (void)layoutTabs { | 762 - (void)layoutTabs { |
| 728 [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:YES]; | 763 [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:YES]; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 880 - (void)animationDidStopForController:(TabController*)controller | 915 - (void)animationDidStopForController:(TabController*)controller |
| 881 finished:(BOOL)finished { | 916 finished:(BOOL)finished { |
| 882 [closingControllers_ removeObject:controller]; | 917 [closingControllers_ removeObject:controller]; |
| 883 [self removeTab:controller]; | 918 [self removeTab:controller]; |
| 884 } | 919 } |
| 885 | 920 |
| 886 // Save off which TabController is closing and tell its view's animator | 921 // Save off which TabController is closing and tell its view's animator |
| 887 // where to move the tab to. Registers a delegate to call back when the | 922 // where to move the tab to. Registers a delegate to call back when the |
| 888 // animation is complete in order to remove the tab from the model. | 923 // animation is complete in order to remove the tab from the model. |
| 889 - (void)startClosingTabWithAnimation:(TabController*)closingTab { | 924 - (void)startClosingTabWithAnimation:(TabController*)closingTab { |
| 925 DCHECK([NSThread isMainThread]); |
| 890 // Save off the controller into the set of animating tabs. This alerts | 926 // Save off the controller into the set of animating tabs. This alerts |
| 891 // the layout method to not do anything with it and allows us to correctly | 927 // the layout method to not do anything with it and allows us to correctly |
| 892 // calculate offsets when working with indices into the model. | 928 // calculate offsets when working with indices into the model. |
| 893 [closingControllers_ addObject:closingTab]; | 929 [closingControllers_ addObject:closingTab]; |
| 894 | 930 |
| 895 // Mark the tab as closing. This prevents it from generating any drags or | 931 // Mark the tab as closing. This prevents it from generating any drags or |
| 896 // selections while it's animating closed. | 932 // selections while it's animating closed. |
| 897 [(TabView*)[closingTab view] setIsClosing:YES]; | 933 [(TabView*)[closingTab view] setIsClosing:YES]; |
| 898 | 934 |
| 899 // Register delegate (owned by the animation system). | 935 // Register delegate (owned by the animation system). |
| 900 NSView* tabView = [closingTab view]; | 936 NSView* tabView = [closingTab view]; |
| 901 CAAnimation* animation = [[tabView animationForKey:@"frameOrigin"] copy]; | 937 CAAnimation* animation = [[tabView animationForKey:@"frameOrigin"] copy]; |
| 902 [animation autorelease]; | 938 [animation autorelease]; |
| 903 scoped_nsobject<TabCloseAnimationDelegate> delegate( | 939 scoped_nsobject<TabCloseAnimationDelegate> delegate( |
| 904 [[TabCloseAnimationDelegate alloc] initWithTabStrip:self | 940 [[TabCloseAnimationDelegate alloc] initWithTabStrip:self |
| 905 tabController:closingTab]); | 941 tabController:closingTab]); |
| 906 [animation setDelegate:delegate.get()]; // Retains delegate. | 942 [animation setDelegate:delegate.get()]; // Retains delegate. |
| 907 NSMutableDictionary* animationDictionary = | 943 NSMutableDictionary* animationDictionary = |
| 908 [NSMutableDictionary dictionaryWithDictionary:[tabView animations]]; | 944 [NSMutableDictionary dictionaryWithDictionary:[tabView animations]]; |
| 909 [animationDictionary setObject:animation forKey:@"frameOrigin"]; | 945 [animationDictionary setObject:animation forKey:@"frameOrigin"]; |
| 910 [tabView setAnimations:animationDictionary]; | 946 [tabView setAnimations:animationDictionary]; |
| 911 | 947 |
| 912 // Periscope down! Animate the tab. | 948 // Periscope down! Animate the tab. |
| 913 NSRect newFrame = [tabView frame]; | 949 NSRect newFrame = [tabView frame]; |
| 914 newFrame = NSOffsetRect(newFrame, 0, -newFrame.size.height); | 950 newFrame = NSOffsetRect(newFrame, 0, -newFrame.size.height); |
| 915 [NSAnimationContext beginGrouping]; | 951 ScopedNSAnimationContextGroup animationGroup(true); |
| 916 [[NSAnimationContext currentContext] gtm_setDuration:kAnimationDuration]; | 952 animationGroup.SetCurrentContextDuration(kAnimationDuration); |
| 917 [[tabView animator] setFrame:newFrame]; | 953 [[tabView animator] setFrame:newFrame]; |
| 918 [NSAnimationContext endGrouping]; | |
| 919 } | 954 } |
| 920 | 955 |
| 921 // Called when a notification is received from the model that the given tab | 956 // Called when a notification is received from the model that the given tab |
| 922 // has gone away. Start an animation then force a layout to put everything | 957 // has gone away. Start an animation then force a layout to put everything |
| 923 // in motion. | 958 // in motion. |
| 924 - (void)tabDetachedWithContents:(TabContents*)contents | 959 - (void)tabDetachedWithContents:(TabContents*)contents |
| 925 atIndex:(NSInteger)modelIndex { | 960 atIndex:(NSInteger)modelIndex { |
| 926 // Take closing tabs into account. | 961 // Take closing tabs into account. |
| 927 NSInteger index = [self indexFromModelIndex:modelIndex]; | 962 NSInteger index = [self indexFromModelIndex:modelIndex]; |
| 928 | 963 |
| (...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1296 NSInteger index = [self indexFromModelIndex:modelIndex]; | 1331 NSInteger index = [self indexFromModelIndex:modelIndex]; |
| 1297 BrowserWindowController* controller = | 1332 BrowserWindowController* controller = |
| 1298 (BrowserWindowController*)[[switchView_ window] windowController]; | 1333 (BrowserWindowController*)[[switchView_ window] windowController]; |
| 1299 DCHECK(index >= 0); | 1334 DCHECK(index >= 0); |
| 1300 if (index >= 0) { | 1335 if (index >= 0) { |
| 1301 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; | 1336 [controller setTab:[self viewAtIndex:index] isDraggable:YES]; |
| 1302 } | 1337 } |
| 1303 } | 1338 } |
| 1304 | 1339 |
| 1305 @end | 1340 @end |
| OLD | NEW |