| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 #include <string> | 10 #include <string> |
| (...skipping 558 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 | 569 |
| 570 TabContents* contents = tabStripModel_->GetTabContentsAt(index); | 570 TabContents* contents = tabStripModel_->GetTabContentsAt(index); |
| 571 if (contents) | 571 if (contents) |
| 572 UserMetrics::RecordAction("CloseTab_Mouse", contents->profile()); | 572 UserMetrics::RecordAction("CloseTab_Mouse", contents->profile()); |
| 573 const NSInteger numberOfOpenTabs = [self numberOfOpenTabs]; | 573 const NSInteger numberOfOpenTabs = [self numberOfOpenTabs]; |
| 574 if (numberOfOpenTabs > 1) { | 574 if (numberOfOpenTabs > 1) { |
| 575 bool isClosingLastTab = index == numberOfOpenTabs - 1; | 575 bool isClosingLastTab = index == numberOfOpenTabs - 1; |
| 576 if (!isClosingLastTab) { | 576 if (!isClosingLastTab) { |
| 577 // Limit the width available for laying out tabs so that tabs are not | 577 // Limit the width available for laying out tabs so that tabs are not |
| 578 // resized until a later time (when the mouse leaves the tab strip). | 578 // resized until a later time (when the mouse leaves the tab strip). |
| 579 // TODO(pinkerton): re-visit when handling tab overflow. | |
| 580 // http://crbug.com/188 | |
| 581 NSView* penultimateTab = [self viewAtIndex:numberOfOpenTabs - 2]; | 579 NSView* penultimateTab = [self viewAtIndex:numberOfOpenTabs - 2]; |
| 582 availableResizeWidth_ = NSMaxX([penultimateTab frame]); | 580 availableResizeWidth_ = NSMaxX([penultimateTab frame]); |
| 583 } else { | 581 } else { |
| 584 // If the rightmost tab is closed, change the available width so that | 582 // If the rightmost tab is closed, change the available width so that |
| 585 // another tab's close button lands below the cursor (assuming the tabs | 583 // another tab's close button lands below the cursor (assuming the tabs |
| 586 // are currently below their maximum width and can grow). | 584 // are currently below their maximum width and can grow) if possible. |
| 587 NSView* lastTab = [self viewAtIndex:numberOfOpenTabs - 1]; | 585 NSView* lastTab = [self viewAtIndex:numberOfOpenTabs - 1]; |
| 588 availableResizeWidth_ = NSMaxX([lastTab frame]); | 586 availableResizeWidth_ = NSMaxX([lastTab frame]); |
| 587 |
| 588 // Since close buttons are on the left, this is a bit tricky. To make this |
| 589 // at all possible, the rightmost tab keeps its narrow pre-close width |
| 590 // and the other tabs expand to push it over. |
| 591 // Only do this if the other tabs can be pushed far enough, though. |
| 592 lastClosedTabWidth_.reset(); |
| 593 const CGFloat kPinnedTabWidth = [TabController pinnedTabWidth]; |
| 594 const CGFloat pinnedWidth = |
| 595 [self numberOfOpenPinnedTabs] * (kPinnedTabWidth - kTabOverlap); |
| 596 const CGFloat kMaxTabWidth = [TabController maxTabWidth]; |
| 597 const CGFloat unpinnedWidth = // 1 tab is candidate for being shorter |
| 598 ([self numberOfOpenUnpinnedTabs] - 1) * (kMaxTabWidth - kTabOverlap); |
| 599 CGFloat candidateWidth = NSWidth([lastTab frame]); |
| 600 // Technically, it should be |
| 601 // |pinnedWidth + unpinnedWidth + widthOfPartOfLastTabThatContainsClose|, |
| 602 // but that's complicated _and_ makes the last tab look very thin. |
| 603 if (pinnedWidth + unpinnedWidth >= availableResizeWidth_) |
| 604 lastClosedTabWidth_.reset(new CGFloat(candidateWidth)); |
| 589 } | 605 } |
| 590 tabStripModel_->CloseTabContentsAt(index); | 606 tabStripModel_->CloseTabContentsAt(index); |
| 591 } else { | 607 } else { |
| 592 // Use the standard window close if this is the last tab | 608 // Use the standard window close if this is the last tab |
| 593 // this prevents the tab from being removed from the model until after | 609 // this prevents the tab from being removed from the model until after |
| 594 // the window dissapears | 610 // the window dissapears |
| 595 [[tabStripView_ window] performClose:nil]; | 611 [[tabStripView_ window] performClose:nil]; |
| 596 } | 612 } |
| 597 } | 613 } |
| 598 | 614 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 660 if (doUpdate) | 676 if (doUpdate) |
| 661 [self regenerateSubviewList]; | 677 [self regenerateSubviewList]; |
| 662 | 678 |
| 663 // Compute the base width of tabs given how much room we're allowed. Note that | 679 // Compute the base width of tabs given how much room we're allowed. Note that |
| 664 // pinned tabs have a fixed width. We may not be able to use the entire width | 680 // pinned tabs have a fixed width. We may not be able to use the entire width |
| 665 // if the user is quickly closing tabs. This may be negative, but that's okay | 681 // if the user is quickly closing tabs. This may be negative, but that's okay |
| 666 // (taken care of by |MAX()| when calculating tab sizes). | 682 // (taken care of by |MAX()| when calculating tab sizes). |
| 667 CGFloat availableWidth = 0; | 683 CGFloat availableWidth = 0; |
| 668 if ([self inRapidClosureMode]) { | 684 if ([self inRapidClosureMode]) { |
| 669 availableWidth = availableResizeWidth_; | 685 availableWidth = availableResizeWidth_; |
| 686 if (lastClosedTabWidth_.get()) |
| 687 availableWidth -= *lastClosedTabWidth_.get(); |
| 670 } else { | 688 } else { |
| 671 availableWidth = NSWidth([tabStripView_ frame]); | 689 availableWidth = NSWidth([tabStripView_ frame]); |
| 672 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; | 690 availableWidth -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset; |
| 673 } | 691 } |
| 674 availableWidth -= [self indentForControls]; | 692 availableWidth -= [self indentForControls]; |
| 675 | 693 |
| 676 // This may be negative, but that's okay (taken care of by |MAX()| when | 694 // This may be negative, but that's okay (taken care of by |MAX()| when |
| 677 // calculating tab sizes). | 695 // calculating tab sizes). |
| 678 CGFloat availableWidthForUnpinned = availableWidth - | 696 CGFloat availableWidthForUnpinned = availableWidth - |
| 679 [self numberOfOpenPinnedTabs] * (kPinnedTabWidth - kTabOverlap); | 697 [self numberOfOpenPinnedTabs] * (kPinnedTabWidth - kTabOverlap); |
| 680 | 698 |
| 681 // Initialize |unpinnedTabWidth| in case there aren't any unpinned tabs; this | 699 // Initialize |unpinnedTabWidth| in case there aren't any unpinned tabs; this |
| 682 // value shouldn't actually be used. | 700 // value shouldn't actually be used. |
| 683 CGFloat unpinnedTabWidth = kMaxTabWidth; | 701 CGFloat unpinnedTabWidth = kMaxTabWidth; |
| 684 const NSInteger numberOfOpenUnpinnedTabs = [self numberOfOpenUnpinnedTabs]; | 702 const NSInteger numberOfOpenUnpinnedTabs = [self numberOfOpenUnpinnedTabs] |
| 703 - (lastClosedTabWidth_.get() ? 1 : 0); |
| 685 if (numberOfOpenUnpinnedTabs) { // Find the width of an unpinned tab. | 704 if (numberOfOpenUnpinnedTabs) { // Find the width of an unpinned tab. |
| 686 // Add in the amount we "get back" from the tabs overlapping. | 705 // Add in the amount we "get back" from the tabs overlapping. |
| 687 availableWidthForUnpinned += (numberOfOpenUnpinnedTabs - 1) * kTabOverlap; | 706 availableWidthForUnpinned += (numberOfOpenUnpinnedTabs - 1) * kTabOverlap; |
| 707 if (lastClosedTabWidth_.get()) |
| 708 availableWidthForUnpinned += kTabOverlap; |
| 688 | 709 |
| 689 // Divide up the space between the unpinned tabs. | 710 // Divide up the space between the unpinned tabs. |
| 690 unpinnedTabWidth = availableWidthForUnpinned / numberOfOpenUnpinnedTabs; | 711 unpinnedTabWidth = availableWidthForUnpinned / numberOfOpenUnpinnedTabs; |
| 691 | 712 |
| 692 // Clamp the width between the max and min. | 713 // Clamp the width between the max and min. |
| 693 unpinnedTabWidth = MAX(MIN(unpinnedTabWidth, kMaxTabWidth), kMinTabWidth); | 714 unpinnedTabWidth = MAX(MIN(unpinnedTabWidth, kMaxTabWidth), kMinTabWidth); |
| 694 } | 715 } |
| 695 | 716 |
| 696 const CGFloat minX = NSMinX(placeholderFrame_); | 717 const CGFloat minX = NSMinX(placeholderFrame_); |
| 697 BOOL visible = [[tabStripView_ window] isVisible]; | 718 BOOL visible = [[tabStripView_ window] isVisible]; |
| 698 | 719 |
| 699 CGFloat offset = [self indentForControls]; | 720 CGFloat offset = [self indentForControls]; |
| 700 NSUInteger i = 0; | |
| 701 bool hasPlaceholderGap = false; | 721 bool hasPlaceholderGap = false; |
| 722 int openTabCount = 0; |
| 702 for (TabController* tab in tabArray_.get()) { | 723 for (TabController* tab in tabArray_.get()) { |
| 703 // Ignore a tab that is going through a close animation. | 724 // Ignore a tab that is going through a close animation. |
| 704 if ([closingControllers_ containsObject:tab]) | 725 if ([closingControllers_ containsObject:tab]) |
| 705 continue; | 726 continue; |
| 706 | 727 |
| 728 ++openTabCount; |
| 707 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; | 729 BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; |
| 708 NSRect tabFrame = [[tab view] frame]; | 730 NSRect tabFrame = [[tab view] frame]; |
| 709 tabFrame.size.height = [[self class] defaultTabHeight] + 1; | 731 tabFrame.size.height = [[self class] defaultTabHeight] + 1; |
| 710 tabFrame.origin.y = 0; | 732 tabFrame.origin.y = 0; |
| 711 tabFrame.origin.x = offset; | 733 tabFrame.origin.x = offset; |
| 712 | 734 |
| 713 // If the tab is hidden, we consider it a new tab. We make it visible | 735 // If the tab is hidden, we consider it a new tab. We make it visible |
| 714 // and animate it in. | 736 // and animate it in. |
| 715 BOOL newTab = [[tab view] isHidden]; | 737 BOOL newTab = [[tab view] isHidden]; |
| 716 if (newTab) { | 738 if (newTab) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 743 offset -= kTabOverlap; | 765 offset -= kTabOverlap; |
| 744 tabFrame.origin.x = offset; | 766 tabFrame.origin.x = offset; |
| 745 } | 767 } |
| 746 | 768 |
| 747 // Set the width. Selected tabs are slightly wider when things get really | 769 // Set the width. Selected tabs are slightly wider when things get really |
| 748 // small and thus we enforce a different minimum width. | 770 // small and thus we enforce a different minimum width. |
| 749 tabFrame.size.width = [tab pinned] ? kPinnedTabWidth : unpinnedTabWidth; | 771 tabFrame.size.width = [tab pinned] ? kPinnedTabWidth : unpinnedTabWidth; |
| 750 if ([tab selected]) | 772 if ([tab selected]) |
| 751 tabFrame.size.width = MAX(tabFrame.size.width, kMinSelectedTabWidth); | 773 tabFrame.size.width = MAX(tabFrame.size.width, kMinSelectedTabWidth); |
| 752 | 774 |
| 775 // When in rapid-closure mode, treat the width of the last tab in a way |
| 776 // that its close button ends up below the cursor. |
| 777 BOOL isLastOpenTab = tabStripModel_->count() == openTabCount; |
| 778 if (isLastOpenTab && lastClosedTabWidth_.get()) |
| 779 tabFrame.size.width = *lastClosedTabWidth_.get(); |
| 780 |
| 753 // Animate a new tab in by putting it below the horizon unless told to put | 781 // Animate a new tab in by putting it below the horizon unless told to put |
| 754 // it in a specific location (i.e., from a drop). | 782 // it in a specific location (i.e., from a drop). |
| 755 if (newTab && visible && animate) { | 783 if (newTab && visible && animate) { |
| 756 if (NSEqualRects(droppedTabFrame_, NSZeroRect)) { | 784 if (NSEqualRects(droppedTabFrame_, NSZeroRect)) { |
| 757 [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; | 785 [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))]; |
| 758 } else { | 786 } else { |
| 759 [[tab view] setFrame:droppedTabFrame_]; | 787 [[tab view] setFrame:droppedTabFrame_]; |
| 760 droppedTabFrame_ = NSZeroRect; | 788 droppedTabFrame_ = NSZeroRect; |
| 761 } | 789 } |
| 762 } | 790 } |
| 763 | 791 |
| 764 // Check the frame by identifier to avoid redundant calls to animator. | 792 // Check the frame by identifier to avoid redundant calls to animator. |
| 765 id frameTarget = visible && animate ? [[tab view] animator] : [tab view]; | 793 id frameTarget = visible && animate ? [[tab view] animator] : [tab view]; |
| 766 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; | 794 NSValue* identifier = [NSValue valueWithPointer:[tab view]]; |
| 767 NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier]; | 795 NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier]; |
| 768 if (!oldTargetValue || | 796 if (!oldTargetValue || |
| 769 !NSEqualRects([oldTargetValue rectValue], tabFrame)) { | 797 !NSEqualRects([oldTargetValue rectValue], tabFrame)) { |
| 770 [frameTarget setFrame:tabFrame]; | 798 [frameTarget setFrame:tabFrame]; |
| 771 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] | 799 [targetFrames_ setObject:[NSValue valueWithRect:tabFrame] |
| 772 forKey:identifier]; | 800 forKey:identifier]; |
| 773 } | 801 } |
| 774 | 802 |
| 775 enclosingRect = NSUnionRect(tabFrame, enclosingRect); | 803 enclosingRect = NSUnionRect(tabFrame, enclosingRect); |
| 776 | 804 |
| 777 offset += NSWidth(tabFrame); | 805 offset += NSWidth(tabFrame); |
| 778 offset -= kTabOverlap; | 806 offset -= kTabOverlap; |
| 779 i++; | |
| 780 } | 807 } |
| 781 | 808 |
| 782 // Hide the new tab button if we're explicitly told to. It may already | 809 // Hide the new tab button if we're explicitly told to. It may already |
| 783 // be hidden, doing it again doesn't hurt. Otherwise position it | 810 // be hidden, doing it again doesn't hurt. Otherwise position it |
| 784 // appropriately, showing it if necessary. | 811 // appropriately, showing it if necessary. |
| 785 if (forceNewTabButtonHidden_) { | 812 if (forceNewTabButtonHidden_) { |
| 786 [newTabButton_ setHidden:YES]; | 813 [newTabButton_ setHidden:YES]; |
| 787 } else { | 814 } else { |
| 788 NSRect newTabNewFrame = [newTabButton_ frame]; | 815 NSRect newTabNewFrame = [newTabButton_ frame]; |
| 789 // We've already ensured there's enough space for the new tab button | 816 // We've already ensured there's enough space for the new tab button |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 884 // upwards as it's being initially layed out. Oddly, this works while doing | 911 // upwards as it's being initially layed out. Oddly, this works while doing |
| 885 // something similar in |-layoutTabs| confuses the window server. | 912 // something similar in |-layoutTabs| confuses the window server. |
| 886 [newView setFrame:NSOffsetRect([newView frame], | 913 [newView setFrame:NSOffsetRect([newView frame], |
| 887 0, -[[self class] defaultTabHeight])]; | 914 0, -[[self class] defaultTabHeight])]; |
| 888 | 915 |
| 889 [self setTabTitle:newController withContents:contents]; | 916 [self setTabTitle:newController withContents:contents]; |
| 890 | 917 |
| 891 // If a tab is being inserted, we can again use the entire tab strip width | 918 // If a tab is being inserted, we can again use the entire tab strip width |
| 892 // for layout. | 919 // for layout. |
| 893 availableResizeWidth_ = kUseFullAvailableWidth; | 920 availableResizeWidth_ = kUseFullAvailableWidth; |
| 921 lastClosedTabWidth_.reset(); |
| 894 | 922 |
| 895 // We don't need to call |-layoutTabs| if the tab will be in the foreground | 923 // We don't need to call |-layoutTabs| if the tab will be in the foreground |
| 896 // because it will get called when the new tab is selected by the tab model. | 924 // because it will get called when the new tab is selected by the tab model. |
| 897 // Whenever |-layoutTabs| is called, it'll also add the new subview. | 925 // Whenever |-layoutTabs| is called, it'll also add the new subview. |
| 898 if (!inForeground) { | 926 if (!inForeground) { |
| 899 [self layoutTabs]; | 927 [self layoutTabs]; |
| 900 } | 928 } |
| 901 | 929 |
| 902 // During normal loading, we won't yet have a favicon and we'll get | 930 // During normal loading, we won't yet have a favicon and we'll get |
| 903 // subsequent state change notifications to show the throbber, but when we're | 931 // subsequent state change notifications to show the throbber, but when we're |
| (...skipping 498 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1402 | 1430 |
| 1403 // Called when the tracking area is in effect which means we're tracking to | 1431 // Called when the tracking area is in effect which means we're tracking to |
| 1404 // see if the user leaves the tab strip with their mouse. When they do, | 1432 // see if the user leaves the tab strip with their mouse. When they do, |
| 1405 // reset layout to use all available width. | 1433 // reset layout to use all available width. |
| 1406 - (void)mouseExited:(NSEvent*)event { | 1434 - (void)mouseExited:(NSEvent*)event { |
| 1407 NSTrackingArea* area = [event trackingArea]; | 1435 NSTrackingArea* area = [event trackingArea]; |
| 1408 if ([area isEqual:trackingArea_]) { | 1436 if ([area isEqual:trackingArea_]) { |
| 1409 mouseInside_ = NO; | 1437 mouseInside_ = NO; |
| 1410 [self setTabTrackingAreasEnabled:NO]; | 1438 [self setTabTrackingAreasEnabled:NO]; |
| 1411 availableResizeWidth_ = kUseFullAvailableWidth; | 1439 availableResizeWidth_ = kUseFullAvailableWidth; |
| 1440 lastClosedTabWidth_.reset(); |
| 1412 [hoveredTab_ mouseExited:event]; | 1441 [hoveredTab_ mouseExited:event]; |
| 1413 hoveredTab_ = nil; | 1442 hoveredTab_ = nil; |
| 1414 [self layoutTabs]; | 1443 [self layoutTabs]; |
| 1415 } else if ([area isEqual:newTabTrackingArea_]) { | 1444 } else if ([area isEqual:newTabTrackingArea_]) { |
| 1416 [newTabButton_ setImage:nsimage_cache::ImageNamed(kNewTabImage)]; | 1445 [newTabButton_ setImage:nsimage_cache::ImageNamed(kNewTabImage)]; |
| 1417 } | 1446 } |
| 1418 } | 1447 } |
| 1419 | 1448 |
| 1420 // Enable/Disable the tracking areas for the tabs. They are only enabled | 1449 // Enable/Disable the tracking areas for the tabs. They are only enabled |
| 1421 // when the mouse is in the tabstrip. | 1450 // when the mouse is in the tabstrip. |
| (...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1723 return; | 1752 return; |
| 1724 | 1753 |
| 1725 TabContentsController* tabController = | 1754 TabContentsController* tabController = |
| 1726 [tabContentsArray_ objectAtIndex:index]; | 1755 [tabContentsArray_ objectAtIndex:index]; |
| 1727 TabContents* devtoolsContents = contents ? | 1756 TabContents* devtoolsContents = contents ? |
| 1728 DevToolsWindow::GetDevToolsContents(contents) : NULL; | 1757 DevToolsWindow::GetDevToolsContents(contents) : NULL; |
| 1729 [tabController showDevToolsContents:devtoolsContents]; | 1758 [tabController showDevToolsContents:devtoolsContents]; |
| 1730 } | 1759 } |
| 1731 | 1760 |
| 1732 @end | 1761 @end |
| OLD | NEW |