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 |