Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: chrome/browser/ui/views/tabs/tab_strip.cc

Issue 339923005: Clip tabs in overflow mode. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Unit tests Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #include "chrome/browser/ui/views/tabs/tab_strip.h" 5 #include "chrome/browser/ui/views/tabs/tab_strip.h"
6 6
7 #if defined(OS_WIN) 7 #if defined(OS_WIN)
8 #include <windowsx.h> 8 #include <windowsx.h>
9 #endif 9 #endif
10 10
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 base::string16 GetClipboardText() { 120 base::string16 GetClipboardText() {
121 if (!ui::Clipboard::IsSupportedClipboardType(ui::CLIPBOARD_TYPE_SELECTION)) 121 if (!ui::Clipboard::IsSupportedClipboardType(ui::CLIPBOARD_TYPE_SELECTION))
122 return base::string16(); 122 return base::string16();
123 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 123 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
124 CHECK(clipboard); 124 CHECK(clipboard);
125 base::string16 clipboard_text; 125 base::string16 clipboard_text;
126 clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &clipboard_text); 126 clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &clipboard_text);
127 return clipboard_text; 127 return clipboard_text;
128 } 128 }
129 129
130 // Animation delegate used for any automatic tab movement. Hides the tab if it
131 // is not fully visible within the tabstrip area, to prevent overflow clipping.
132 class TabAnimationDelegate : public gfx::AnimationDelegate {
133 public:
134 TabAnimationDelegate(TabStrip* tab_strip, Tab* tab);
135 virtual ~TabAnimationDelegate();
136
137 virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
138
139 protected:
140 TabStrip* tab_strip() { return tab_strip_; }
141 Tab* tab() { return tab_; }
142
143 private:
144 TabStrip* const tab_strip_;
145 Tab* const tab_;
146
147 DISALLOW_COPY_AND_ASSIGN(TabAnimationDelegate);
148 };
149
150 TabAnimationDelegate::TabAnimationDelegate(TabStrip* tab_strip, Tab* tab)
151 : tab_strip_(tab_strip),
152 tab_(tab) {
153 }
154
155 TabAnimationDelegate::~TabAnimationDelegate() {
156 }
157
158 void TabAnimationDelegate::AnimationProgressed(
159 const gfx::Animation* animation) {
160 tab_->SetVisible(tab_strip_->ShouldTabBeVisible(tab_));
161 }
162
130 // Animation delegate used when a dragged tab is released. When done sets the 163 // Animation delegate used when a dragged tab is released. When done sets the
131 // dragging state to false. 164 // dragging state to false.
132 class ResetDraggingStateDelegate : public gfx::AnimationDelegate { 165 class ResetDraggingStateDelegate : public TabAnimationDelegate {
133 public: 166 public:
134 explicit ResetDraggingStateDelegate(Tab* tab); 167 ResetDraggingStateDelegate(TabStrip* tab_strip, Tab* tab);
135 virtual ~ResetDraggingStateDelegate(); 168 virtual ~ResetDraggingStateDelegate();
136 169
137 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE; 170 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
138 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE; 171 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE;
139 172
140 private: 173 private:
141 Tab* tab_;
142
143 DISALLOW_COPY_AND_ASSIGN(ResetDraggingStateDelegate); 174 DISALLOW_COPY_AND_ASSIGN(ResetDraggingStateDelegate);
144 }; 175 };
145 176
146 ResetDraggingStateDelegate::ResetDraggingStateDelegate(Tab* tab) : tab_(tab) { 177 ResetDraggingStateDelegate::ResetDraggingStateDelegate(TabStrip* tab_strip,
178 Tab* tab)
179 : TabAnimationDelegate(tab_strip, tab) {
147 } 180 }
148 181
149 ResetDraggingStateDelegate::~ResetDraggingStateDelegate() { 182 ResetDraggingStateDelegate::~ResetDraggingStateDelegate() {
150 } 183 }
151 184
152 void ResetDraggingStateDelegate::AnimationEnded( 185 void ResetDraggingStateDelegate::AnimationEnded(
153 const gfx::Animation* animation) { 186 const gfx::Animation* animation) {
154 tab_->set_dragging(false); 187 tab()->set_dragging(false);
188 AnimationProgressed(animation); // Forces tab visibility to update.
155 } 189 }
156 190
157 void ResetDraggingStateDelegate::AnimationCanceled( 191 void ResetDraggingStateDelegate::AnimationCanceled(
158 const gfx::Animation* animation) { 192 const gfx::Animation* animation) {
159 AnimationEnded(animation); 193 AnimationEnded(animation);
160 } 194 }
161 195
162 // If |dest| contains the point |point_in_source| the event handler from |dest| 196 // If |dest| contains the point |point_in_source| the event handler from |dest|
163 // is returned. Otherwise NULL is returned. 197 // is returned. Otherwise NULL is returned.
164 views::View* ConvertPointToViewAndGetEventHandler( 198 views::View* ConvertPointToViewAndGetEventHandler(
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 } 483 }
450 484
451 DISALLOW_COPY_AND_ASSIGN(NewTabButtonTargeter); 485 DISALLOW_COPY_AND_ASSIGN(NewTabButtonTargeter);
452 }; 486 };
453 487
454 /////////////////////////////////////////////////////////////////////////////// 488 ///////////////////////////////////////////////////////////////////////////////
455 // TabStrip::RemoveTabDelegate 489 // TabStrip::RemoveTabDelegate
456 // 490 //
457 // AnimationDelegate used when removing a tab. Does the necessary cleanup when 491 // AnimationDelegate used when removing a tab. Does the necessary cleanup when
458 // done. 492 // done.
459 class TabStrip::RemoveTabDelegate : public gfx::AnimationDelegate { 493 class TabStrip::RemoveTabDelegate : public TabAnimationDelegate {
460 public: 494 public:
461 RemoveTabDelegate(TabStrip* tab_strip, Tab* tab); 495 RemoveTabDelegate(TabStrip* tab_strip, Tab* tab);
462 496
463 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE; 497 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
464 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE; 498 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE;
465 499
466 private: 500 private:
467 void CompleteRemove();
468
469 // When the animation completes, we send the Container a message to simulate
470 // a mouse moved event at the current mouse position. This tickles the Tab
471 // the mouse is currently over to show the "hot" state of the close button.
472 void HighlightCloseButton();
473
474 TabStrip* tabstrip_;
475 Tab* tab_;
476
477 DISALLOW_COPY_AND_ASSIGN(RemoveTabDelegate); 501 DISALLOW_COPY_AND_ASSIGN(RemoveTabDelegate);
478 }; 502 };
479 503
480 TabStrip::RemoveTabDelegate::RemoveTabDelegate(TabStrip* tab_strip, 504 TabStrip::RemoveTabDelegate::RemoveTabDelegate(TabStrip* tab_strip,
481 Tab* tab) 505 Tab* tab)
482 : tabstrip_(tab_strip), 506 : TabAnimationDelegate(tab_strip, tab) {
483 tab_(tab) {
484 } 507 }
485 508
486 void TabStrip::RemoveTabDelegate::AnimationEnded( 509 void TabStrip::RemoveTabDelegate::AnimationEnded(
487 const gfx::Animation* animation) { 510 const gfx::Animation* animation) {
488 CompleteRemove(); 511 DCHECK(tab()->closing());
512 tab_strip()->RemoveAndDeleteTab(tab());
513
514 // Send the Container a message to simulate a mouse moved event at the current
515 // mouse position. This tickles the Tab the mouse is currently over to show
516 // the "hot" state of the close button. Note that this is not required (and
517 // indeed may crash!) for removes spawned by non-mouse closes and
518 // drag-detaches.
519 if (!tab_strip()->IsDragSessionActive() &&
520 tab_strip()->ShouldHighlightCloseButtonAfterRemove()) {
521 // The widget can apparently be null during shutdown.
522 views::Widget* widget = tab_strip()->GetWidget();
523 if (widget)
524 widget->SynthesizeMouseMoveEvent();
525 }
489 } 526 }
490 527
491 void TabStrip::RemoveTabDelegate::AnimationCanceled( 528 void TabStrip::RemoveTabDelegate::AnimationCanceled(
492 const gfx::Animation* animation) { 529 const gfx::Animation* animation) {
493 AnimationEnded(animation); 530 AnimationEnded(animation);
494 } 531 }
495 532
496 void TabStrip::RemoveTabDelegate::CompleteRemove() {
497 DCHECK(tab_->closing());
498 tabstrip_->RemoveAndDeleteTab(tab_);
499 HighlightCloseButton();
500 }
501
502 void TabStrip::RemoveTabDelegate::HighlightCloseButton() {
503 if (tabstrip_->IsDragSessionActive() ||
504 !tabstrip_->ShouldHighlightCloseButtonAfterRemove()) {
505 // This function is not required (and indeed may crash!) for removes
506 // spawned by non-mouse closes and drag-detaches.
507 return;
508 }
509
510 views::Widget* widget = tabstrip_->GetWidget();
511 // This can be null during shutdown. See http://crbug.com/42737.
512 if (!widget)
513 return;
514
515 widget->SynthesizeMouseMoveEvent();
516 }
517
518 /////////////////////////////////////////////////////////////////////////////// 533 ///////////////////////////////////////////////////////////////////////////////
519 // TabStrip, public: 534 // TabStrip, public:
520 535
521 // static 536 // static
522 const char TabStrip::kViewClassName[] = "TabStrip"; 537 const char TabStrip::kViewClassName[] = "TabStrip";
523 const int TabStrip::kNewTabButtonHorizontalOffset = -11; 538 const int TabStrip::kNewTabButtonHorizontalOffset = -11;
524 const int TabStrip::kNewTabButtonVerticalOffset = 7; 539 const int TabStrip::kNewTabButtonVerticalOffset = 7;
525 const int TabStrip::kMiniToNonMiniGap = 3; 540 const int TabStrip::kMiniToNonMiniGap = 3;
526 const int TabStrip::kNewTabButtonAssetWidth = 34; 541 const int TabStrip::kNewTabButtonAssetWidth = 34;
527 const int TabStrip::kNewTabButtonAssetHeight = 18; 542 const int TabStrip::kNewTabButtonAssetHeight = 18;
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
713 touch_layout_->SetXAndMiniCount(start_x, mini_tab_count); 728 touch_layout_->SetXAndMiniCount(start_x, mini_tab_count);
714 } 729 }
715 if (GetWidget() && GetWidget()->IsVisible()) 730 if (GetWidget() && GetWidget()->IsVisible())
716 StartMiniTabAnimation(); 731 StartMiniTabAnimation();
717 else 732 else
718 DoLayout(); 733 DoLayout();
719 } 734 }
720 SwapLayoutIfNecessary(); 735 SwapLayoutIfNecessary();
721 } 736 }
722 737
738 bool TabStrip::ShouldTabBeVisible(const Tab* tab) const {
739 // When stacking tabs, all tabs should always be visible.
740 if (stacked_layout_)
741 return true;
742
743 // If the tab is currently clipped, it shouldn't be visible. Note that we
744 // allow dragged tabs to draw over the "New Tab button" region as well,
745 // because either the New Tab button will be hidden, or the dragged tabs will
746 // be animating back to their normal positions and we don't want to hide them
747 // in the New Tab button region in case they re-appear after leaving it.
748 // (This prevents flickeriness.) We never draw non-dragged tabs in New Tab
749 // button area, even when the button is invisible, so that they don't appear
750 // to "pop in" when the button disappears.
751 // TODO: Probably doesn't work for RTL
752 int right_edge = tab->bounds().right();
753 const int visible_width = tab->dragging() ? width() : tab_area_width();
754 if (right_edge > visible_width)
755 return false;
756
757 // Non-clipped dragging tabs should always be visible.
758 if (tab->dragging())
759 return true;
760
761 // Let all non-clipped closing tabs be visible. These will probably finish
762 // closing before the user changes the active tab, so there's little reason to
763 // try and make the more complex logic below apply.
764 if (tab->closing())
765 return true;
766
767 // Now we need to check whether the tab isn't currently clipped, but could
768 // become clipped if we changed the active tab, widening either this tab or
769 // the tabstrip portion before it.
770
771 // Mini tabs don't change size when activated, so any tab in the mini tab
772 // region is safe.
773 if (tab->data().mini)
774 return true;
775
776 // If the active tab is on or before this tab, we're safe.
777 if (controller_->GetActiveIndex() <= GetModelIndexOfTab(tab))
778 return true;
779
780 // We need to check what would happen if the active tab were to move to this
781 // tab or before.
782 return (right_edge + current_selected_width_ - current_unselected_width_) <=
783 tab_area_width();
784 }
785
723 void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) { 786 void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) {
724 if (!in_tab_close_ && IsAnimating()) { 787 if (!in_tab_close_ && IsAnimating()) {
725 // Cancel any current animations. We do this as remove uses the current 788 // Cancel any current animations. We do this as remove uses the current
726 // ideal bounds and we need to know ideal bounds is in a good state. 789 // ideal bounds and we need to know ideal bounds is in a good state.
727 StopAnimating(true); 790 StopAnimating(true);
728 } 791 }
729 792
730 if (!GetWidget()) 793 if (!GetWidget())
731 return; 794 return;
732 795
(...skipping 752 matching lines...) Expand 10 before | Expand all | Expand 10 after
1485 // strip. 1548 // strip.
1486 if (TabDragController::IsAttachedTo(this)) { 1549 if (TabDragController::IsAttachedTo(this)) {
1487 bounds_animator_.StopAnimatingView(newtab_button_); 1550 bounds_animator_.StopAnimatingView(newtab_button_);
1488 newtab_button_->SetBoundsRect(newtab_button_bounds_); 1551 newtab_button_->SetBoundsRect(newtab_button_bounds_);
1489 } 1552 }
1490 } 1553 }
1491 1554
1492 void TabStrip::AnimateToIdealBounds() { 1555 void TabStrip::AnimateToIdealBounds() {
1493 for (int i = 0; i < tab_count(); ++i) { 1556 for (int i = 0; i < tab_count(); ++i) {
1494 Tab* tab = tab_at(i); 1557 Tab* tab = tab_at(i);
1495 if (!tab->dragging()) 1558 if (!tab->dragging()) {
1496 bounds_animator_.AnimateViewTo(tab, ideal_bounds(i)); 1559 bounds_animator_.AnimateViewTo(tab, ideal_bounds(i));
1560 bounds_animator_.SetAnimationDelegate(
1561 tab,
1562 scoped_ptr<gfx::AnimationDelegate>(
1563 new TabAnimationDelegate(this, tab)));
1564 }
1497 } 1565 }
1498 1566
1499 bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_); 1567 bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_);
1500 } 1568 }
1501 1569
1502 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() { 1570 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
1503 return in_tab_close_; 1571 return in_tab_close_;
1504 } 1572 }
1505 1573
1506 void TabStrip::DoLayout() { 1574 void TabStrip::DoLayout() {
1507 last_layout_size_ = size(); 1575 last_layout_size_ = size();
1508 1576
1509 StopAnimating(false); 1577 StopAnimating(false);
1510 1578
1511 SwapLayoutIfNecessary(); 1579 SwapLayoutIfNecessary();
1512 1580
1513 if (touch_layout_) 1581 if (touch_layout_)
1514 touch_layout_->SetWidth(tab_area_width()); 1582 touch_layout_->SetWidth(tab_area_width());
1515 1583
1516 GenerateIdealBounds(); 1584 GenerateIdealBounds();
1517 1585
1518 views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_); 1586 views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_);
1587 SetTabVisibility();
1519 1588
1520 SchedulePaint(); 1589 SchedulePaint();
1521 1590
1522 bounds_animator_.StopAnimatingView(newtab_button_); 1591 bounds_animator_.StopAnimatingView(newtab_button_);
1523 newtab_button_->SetBoundsRect(newtab_button_bounds_); 1592 newtab_button_->SetBoundsRect(newtab_button_bounds_);
1524 } 1593 }
1525 1594
1595 void TabStrip::SetTabVisibility() {
1596 // We could probably be more efficient here by making use of the fact that the
1597 // tabstrip will always have any visible tabs, and then any invisible tabs, so
1598 // we could e.g. binary-search for the changeover point. But since we have to
1599 // iterate through all the tabs to call SetVisible() anyway, it doesn't seem
1600 // worth it.
1601 for (int i = 0; i < tab_count(); ++i) {
1602 Tab* tab = tab_at(i);
1603 tab->SetVisible(ShouldTabBeVisible(tab));
1604 }
1605 for (TabsClosingMap::const_iterator i(tabs_closing_map_.begin());
1606 i != tabs_closing_map_.end(); ++i) {
1607 for (Tabs::const_iterator j(i->second.begin()); j != i->second.end(); ++j) {
1608 Tab* tab = *j;
1609 tab->SetVisible(ShouldTabBeVisible(tab));
1610 }
1611 }
1612 }
1613
1526 void TabStrip::DragActiveTab(const std::vector<int>& initial_positions, 1614 void TabStrip::DragActiveTab(const std::vector<int>& initial_positions,
1527 int delta) { 1615 int delta) {
1528 DCHECK_EQ(tab_count(), static_cast<int>(initial_positions.size())); 1616 DCHECK_EQ(tab_count(), static_cast<int>(initial_positions.size()));
1529 if (!touch_layout_) { 1617 if (!touch_layout_) {
1530 StackDraggedTabs(delta); 1618 StackDraggedTabs(delta);
1531 return; 1619 return;
1532 } 1620 }
1533 SetIdealBoundsFromPositions(initial_positions); 1621 SetIdealBoundsFromPositions(initial_positions);
1534 touch_layout_->DragActiveTab(delta); 1622 touch_layout_->DragActiveTab(delta);
1535 DoLayout(); 1623 DoLayout();
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
1621 return drag_controller_.get() && drag_controller_->started_drag() && 1709 return drag_controller_.get() && drag_controller_->started_drag() &&
1622 (drag_controller_->move_behavior() == 1710 (drag_controller_->move_behavior() ==
1623 TabDragController::MOVE_VISIBILE_TABS); 1711 TabDragController::MOVE_VISIBILE_TABS);
1624 } 1712 }
1625 1713
1626 void TabStrip::LayoutDraggedTabsAt(const Tabs& tabs, 1714 void TabStrip::LayoutDraggedTabsAt(const Tabs& tabs,
1627 Tab* active_tab, 1715 Tab* active_tab,
1628 const gfx::Point& location, 1716 const gfx::Point& location,
1629 bool initial_drag) { 1717 bool initial_drag) {
1630 // Immediately hide the new tab button if the last tab is being dragged. 1718 // Immediately hide the new tab button if the last tab is being dragged.
1631 if (GetLastVisibleTab()->dragging()) 1719 const Tab* last_visible_tab = GetLastVisibleTab();
1720 if (last_visible_tab && last_visible_tab->dragging())
1632 newtab_button_->SetVisible(false); 1721 newtab_button_->SetVisible(false);
1633 std::vector<gfx::Rect> bounds; 1722 std::vector<gfx::Rect> bounds;
1634 CalculateBoundsForDraggedTabs(tabs, &bounds); 1723 CalculateBoundsForDraggedTabs(tabs, &bounds);
1635 DCHECK_EQ(tabs.size(), bounds.size()); 1724 DCHECK_EQ(tabs.size(), bounds.size());
1636 int active_tab_model_index = GetModelIndexOfTab(active_tab); 1725 int active_tab_model_index = GetModelIndexOfTab(active_tab);
1637 int active_tab_index = static_cast<int>( 1726 int active_tab_index = static_cast<int>(
1638 std::find(tabs.begin(), tabs.end(), active_tab) - tabs.begin()); 1727 std::find(tabs.begin(), tabs.end(), active_tab) - tabs.begin());
1639 for (size_t i = 0; i < tabs.size(); ++i) { 1728 for (size_t i = 0; i < tabs.size(); ++i) {
1640 Tab* tab = tabs[i]; 1729 Tab* tab = tabs[i];
1641 gfx::Rect new_bounds = bounds[i]; 1730 gfx::Rect new_bounds = bounds[i];
1642 new_bounds.Offset(location.x(), location.y()); 1731 new_bounds.Offset(location.x(), location.y());
1643 int consecutive_index = 1732 int consecutive_index =
1644 active_tab_model_index - (active_tab_index - static_cast<int>(i)); 1733 active_tab_model_index - (active_tab_index - static_cast<int>(i));
1645 // If this is the initial layout during a drag and the tabs aren't 1734 // If this is the initial layout during a drag and the tabs aren't
1646 // consecutive animate the view into position. Do the same if the tab is 1735 // consecutive animate the view into position. Do the same if the tab is
1647 // already animating (which means we previously caused it to animate). 1736 // already animating (which means we previously caused it to animate).
1648 if ((initial_drag && 1737 if ((initial_drag &&
1649 GetModelIndexOfTab(tabs[i]) != consecutive_index) || 1738 GetModelIndexOfTab(tabs[i]) != consecutive_index) ||
1650 bounds_animator_.IsAnimating(tabs[i])) { 1739 bounds_animator_.IsAnimating(tabs[i])) {
1651 bounds_animator_.SetTargetBounds(tabs[i], new_bounds); 1740 bounds_animator_.SetTargetBounds(tabs[i], new_bounds);
1652 } else { 1741 } else {
1653 tab->SetBoundsRect(new_bounds); 1742 tab->SetBoundsRect(new_bounds);
1654 } 1743 }
1655 } 1744 }
1745 SetTabVisibility();
1656 } 1746 }
1657 1747
1658 void TabStrip::CalculateBoundsForDraggedTabs(const Tabs& tabs, 1748 void TabStrip::CalculateBoundsForDraggedTabs(const Tabs& tabs,
1659 std::vector<gfx::Rect>* bounds) { 1749 std::vector<gfx::Rect>* bounds) {
1660 int x = 0; 1750 int x = 0;
1661 for (size_t i = 0; i < tabs.size(); ++i) { 1751 for (size_t i = 0; i < tabs.size(); ++i) {
1662 Tab* tab = tabs[i]; 1752 Tab* tab = tabs[i];
1663 if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini) 1753 if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
1664 x += kMiniToNonMiniGap; 1754 x += kMiniToNonMiniGap;
1665 gfx::Rect new_bounds = tab->bounds(); 1755 gfx::Rect new_bounds = tab->bounds();
(...skipping 17 matching lines...) Expand all
1683 } 1773 }
1684 1774
1685 int TabStrip::GetMiniTabCount() const { 1775 int TabStrip::GetMiniTabCount() const {
1686 int mini_count = 0; 1776 int mini_count = 0;
1687 while (mini_count < tab_count() && tab_at(mini_count)->data().mini) 1777 while (mini_count < tab_count() && tab_at(mini_count)->data().mini)
1688 mini_count++; 1778 mini_count++;
1689 return mini_count; 1779 return mini_count;
1690 } 1780 }
1691 1781
1692 const Tab* TabStrip::GetLastVisibleTab() const { 1782 const Tab* TabStrip::GetLastVisibleTab() const {
1693 return tab_at(tab_count() - 1); 1783 for (int i = tab_count() - 1; i >= 0; --i) {
1784 const Tab* tab = tab_at(i);
1785 if (tab->visible())
1786 return tab;
1787 }
1788 // While in normal use the tabstrip should always be wide enough to have at
1789 // least one visible tab, it can be zero-width in tests, meaning we get here.
1790 return NULL;
1694 } 1791 }
1695 1792
1696 void TabStrip::RemoveTabFromViewModel(int index) { 1793 void TabStrip::RemoveTabFromViewModel(int index) {
1697 // We still need to paint the tab until we actually remove it. Put it 1794 // We still need to paint the tab until we actually remove it. Put it
1698 // in tabs_closing_map_ so we can find it. 1795 // in tabs_closing_map_ so we can find it.
1699 tabs_closing_map_[index].push_back(tab_at(index)); 1796 tabs_closing_map_[index].push_back(tab_at(index));
1700 UpdateTabsClosingMap(index + 1, -1); 1797 UpdateTabsClosingMap(index + 1, -1);
1701 tabs_.Remove(index); 1798 tabs_.Remove(index);
1702 } 1799 }
1703 1800
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1754 1851
1755 // Move the dragged tabs to their ideal bounds. 1852 // Move the dragged tabs to their ideal bounds.
1756 GenerateIdealBounds(); 1853 GenerateIdealBounds();
1757 1854
1758 // Sets the bounds of the dragged tabs. 1855 // Sets the bounds of the dragged tabs.
1759 for (size_t i = 0; i < tabs.size(); ++i) { 1856 for (size_t i = 0; i < tabs.size(); ++i) {
1760 int tab_data_index = GetModelIndexOfTab(tabs[i]); 1857 int tab_data_index = GetModelIndexOfTab(tabs[i]);
1761 DCHECK_NE(-1, tab_data_index); 1858 DCHECK_NE(-1, tab_data_index);
1762 tabs[i]->SetBoundsRect(ideal_bounds(tab_data_index)); 1859 tabs[i]->SetBoundsRect(ideal_bounds(tab_data_index));
1763 } 1860 }
1861 SetTabVisibility();
1764 SchedulePaint(); 1862 SchedulePaint();
1765 } 1863 }
1766 1864
1767 void TabStrip::DraggedTabsDetached() { 1865 void TabStrip::DraggedTabsDetached() {
1768 // Let the controller know that the user is not dragging this tabstrip's tabs 1866 // Let the controller know that the user is not dragging this tabstrip's tabs
1769 // anymore. 1867 // anymore.
1770 controller()->OnStoppedDraggingTabs(); 1868 controller()->OnStoppedDraggingTabs();
1771 newtab_button_->SetVisible(true); 1869 newtab_button_->SetVisible(true);
1772 } 1870 }
1773 1871
(...skipping 29 matching lines...) Expand all
1803 1901
1804 // Animate the view back to its correct position. 1902 // Animate the view back to its correct position.
1805 GenerateIdealBounds(); 1903 GenerateIdealBounds();
1806 AnimateToIdealBounds(); 1904 AnimateToIdealBounds();
1807 } 1905 }
1808 bounds_animator_.AnimateViewTo(tab, ideal_bounds(tab_data_index)); 1906 bounds_animator_.AnimateViewTo(tab, ideal_bounds(tab_data_index));
1809 // Install a delegate to reset the dragging state when done. We have to leave 1907 // Install a delegate to reset the dragging state when done. We have to leave
1810 // dragging true for the tab otherwise it'll draw beneath the new tab button. 1908 // dragging true for the tab otherwise it'll draw beneath the new tab button.
1811 bounds_animator_.SetAnimationDelegate( 1909 bounds_animator_.SetAnimationDelegate(
1812 tab, 1910 tab,
1813 scoped_ptr<gfx::AnimationDelegate>(new ResetDraggingStateDelegate(tab))); 1911 scoped_ptr<gfx::AnimationDelegate>(
1912 new ResetDraggingStateDelegate(this, tab)));
1814 } 1913 }
1815 1914
1816 void TabStrip::OwnDragController(TabDragController* controller) { 1915 void TabStrip::OwnDragController(TabDragController* controller) {
1817 // Typically, ReleaseDragController() and OwnDragController() calls are paired 1916 // Typically, ReleaseDragController() and OwnDragController() calls are paired
1818 // via corresponding calls to TabDragController::Detach() and 1917 // via corresponding calls to TabDragController::Detach() and
1819 // TabDragController::Attach(). There is one exception to that rule: when a 1918 // TabDragController::Attach(). There is one exception to that rule: when a
1820 // drag might start, we create a TabDragController that is owned by the 1919 // drag might start, we create a TabDragController that is owned by the
1821 // potential source tabstrip in MaybeStartDrag(). If a drag actually starts, 1920 // potential source tabstrip in MaybeStartDrag(). If a drag actually starts,
1822 // we then call Attach() on the source tabstrip, but since the source tabstrip 1921 // we then call Attach() on the source tabstrip, but since the source tabstrip
1823 // already owns the TabDragController, so we don't need to do anything. 1922 // already owns the TabDragController, so we don't need to do anything.
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
1951 2050
1952 *unselected_width = min_unselected_width; 2051 *unselected_width = min_unselected_width;
1953 *selected_width = min_selected_width; 2052 *selected_width = min_selected_width;
1954 2053
1955 if (tab_count == 0) { 2054 if (tab_count == 0) {
1956 // Return immediately to avoid divide-by-zero below. 2055 // Return immediately to avoid divide-by-zero below.
1957 return; 2056 return;
1958 } 2057 }
1959 2058
1960 // Determine how much space we can actually allocate to tabs. 2059 // Determine how much space we can actually allocate to tabs.
1961 int available_width; 2060 int available_width = (available_width_for_tabs_ < 0) ?
1962 if (available_width_for_tabs_ < 0) { 2061 tab_area_width() : available_width_for_tabs_;
1963 available_width = width() - new_tab_button_width();
1964 } else {
1965 // Interesting corner case: if |available_width_for_tabs_| > the result
1966 // of the calculation in the conditional arm above, the strip is in
1967 // overflow. We can either use the specified width or the true available
1968 // width here; the first preserves the consistent "leave the last tab under
1969 // the user's mouse so they can close many tabs" behavior at the cost of
1970 // prolonging the glitchy appearance of the overflow state, while the second
1971 // gets us out of overflow as soon as possible but forces the user to move
1972 // their mouse for a few tabs' worth of closing. We choose visual
1973 // imperfection over behavioral imperfection and select the first option.
1974 available_width = available_width_for_tabs_;
1975 }
1976
1977 if (mini_tab_count > 0) { 2062 if (mini_tab_count > 0) {
1978 available_width -= 2063 available_width -=
1979 mini_tab_count * (Tab::GetMiniWidth() + kTabHorizontalOffset); 2064 mini_tab_count * (Tab::GetMiniWidth() + kTabHorizontalOffset);
1980 tab_count -= mini_tab_count; 2065 tab_count -= mini_tab_count;
1981 if (tab_count == 0) { 2066 if (tab_count == 0) {
1982 *selected_width = *unselected_width = Tab::GetStandardSize().width(); 2067 *selected_width = *unselected_width = Tab::GetStandardSize().width();
1983 return; 2068 return;
1984 } 2069 }
1985 // Account for gap between the last mini-tab and first non-mini-tab. 2070 // Account for gap between the last mini-tab and first non-mini-tab.
1986 available_width -= kMiniToNonMiniGap; 2071 available_width -= kMiniToNonMiniGap;
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after
2453 // many mini-tabs there are). 2538 // many mini-tabs there are).
2454 GenerateIdealBoundsForMiniTabs(NULL); 2539 GenerateIdealBoundsForMiniTabs(NULL);
2455 touch_layout_->SetXAndMiniCount(GetStartXForNormalTabs(), 2540 touch_layout_->SetXAndMiniCount(GetStartXForNormalTabs(),
2456 GetMiniTabCount()); 2541 GetMiniTabCount());
2457 touch_layout_->SetActiveIndex(controller_->GetActiveIndex()); 2542 touch_layout_->SetActiveIndex(controller_->GetActiveIndex());
2458 } else { 2543 } else {
2459 touch_layout_.reset(); 2544 touch_layout_.reset();
2460 } 2545 }
2461 PrepareForAnimation(); 2546 PrepareForAnimation();
2462 GenerateIdealBounds(); 2547 GenerateIdealBounds();
2548 SetTabVisibility();
2463 AnimateToIdealBounds(); 2549 AnimateToIdealBounds();
2464 } 2550 }
2465 2551
2466 bool TabStrip::NeedsTouchLayout() const { 2552 bool TabStrip::NeedsTouchLayout() const {
2467 if (!stacked_layout_) 2553 if (!stacked_layout_)
2468 return false; 2554 return false;
2469 2555
2470 int mini_tab_count = GetMiniTabCount(); 2556 int mini_tab_count = GetMiniTabCount();
2471 int normal_count = tab_count() - mini_tab_count; 2557 int normal_count = tab_count() - mini_tab_count;
2472 if (normal_count <= 1 || normal_count == mini_tab_count) 2558 if (normal_count <= 1 || normal_count == mini_tab_count)
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
2605 action = TouchUMA::GESTURE_TABSWITCH_TAP; 2691 action = TouchUMA::GESTURE_TABSWITCH_TAP;
2606 TouchUMA::RecordGestureAction(action); 2692 TouchUMA::RecordGestureAction(action);
2607 break; 2693 break;
2608 } 2694 }
2609 2695
2610 default: 2696 default:
2611 break; 2697 break;
2612 } 2698 }
2613 event->SetHandled(); 2699 event->SetHandled();
2614 } 2700 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/tabs/tab_strip.h ('k') | chrome/browser/ui/views/tabs/tab_strip_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698