OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |