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_) | |
sky
2014/06/17 19:41:17
touch_layout_ is more correct here.
Peter Kasting
2014/06/17 20:20:20
Why? If stacked_layout_ is true, then either we'r
sky
2014/06/17 23:53:13
No, you're right.
| |
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 const int tab_index = GetModelIndexOfTab(tab); | |
774 if (tab_index <= GetMiniTabCount()) | |
sky
2014/06/17 19:41:18
I would check tab->data().mini here. The condition
Peter Kasting
2014/06/17 20:20:20
Done.
| |
775 return true; | |
776 | |
777 // If the active tab is on or before this tab, we're safe. | |
778 if (controller_->GetActiveIndex() <= tab_index) | |
779 return true; | |
780 | |
781 // We need to check what would happen if the active tab were to move to this | |
782 // tab or before. | |
783 return (right_edge + current_selected_width_ - current_unselected_width_) <= | |
784 tab_area_width(); | |
785 } | |
786 | |
723 void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) { | 787 void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) { |
724 if (!in_tab_close_ && IsAnimating()) { | 788 if (!in_tab_close_ && IsAnimating()) { |
725 // Cancel any current animations. We do this as remove uses the current | 789 // 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. | 790 // ideal bounds and we need to know ideal bounds is in a good state. |
727 StopAnimating(true); | 791 StopAnimating(true); |
728 } | 792 } |
729 | 793 |
730 if (!GetWidget()) | 794 if (!GetWidget()) |
731 return; | 795 return; |
732 | 796 |
(...skipping 752 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1485 // strip. | 1549 // strip. |
1486 if (TabDragController::IsAttachedTo(this)) { | 1550 if (TabDragController::IsAttachedTo(this)) { |
1487 bounds_animator_.StopAnimatingView(newtab_button_); | 1551 bounds_animator_.StopAnimatingView(newtab_button_); |
1488 newtab_button_->SetBoundsRect(newtab_button_bounds_); | 1552 newtab_button_->SetBoundsRect(newtab_button_bounds_); |
1489 } | 1553 } |
1490 } | 1554 } |
1491 | 1555 |
1492 void TabStrip::AnimateToIdealBounds() { | 1556 void TabStrip::AnimateToIdealBounds() { |
1493 for (int i = 0; i < tab_count(); ++i) { | 1557 for (int i = 0; i < tab_count(); ++i) { |
1494 Tab* tab = tab_at(i); | 1558 Tab* tab = tab_at(i); |
1495 if (!tab->dragging()) | 1559 if (!tab->dragging()) { |
1496 bounds_animator_.AnimateViewTo(tab, ideal_bounds(i)); | 1560 bounds_animator_.AnimateViewTo(tab, ideal_bounds(i)); |
1561 bounds_animator_.SetAnimationDelegate( | |
1562 tab, make_scoped_ptr(new TabAnimationDelegate(this, tab))); | |
1563 } | |
1497 } | 1564 } |
1498 | 1565 |
1499 bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_); | 1566 bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_); |
1500 } | 1567 } |
1501 | 1568 |
1502 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() { | 1569 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() { |
1503 return in_tab_close_; | 1570 return in_tab_close_; |
1504 } | 1571 } |
1505 | 1572 |
1506 void TabStrip::DoLayout() { | 1573 void TabStrip::DoLayout() { |
1507 last_layout_size_ = size(); | 1574 last_layout_size_ = size(); |
1508 | 1575 |
1509 StopAnimating(false); | 1576 StopAnimating(false); |
1510 | 1577 |
1511 SwapLayoutIfNecessary(); | 1578 SwapLayoutIfNecessary(); |
1512 | 1579 |
1513 if (touch_layout_) | 1580 if (touch_layout_) |
1514 touch_layout_->SetWidth(tab_area_width()); | 1581 touch_layout_->SetWidth(tab_area_width()); |
1515 | 1582 |
1516 GenerateIdealBounds(); | 1583 GenerateIdealBounds(); |
1517 | 1584 |
1518 views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_); | 1585 views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_); |
1586 SetTabVisibility(); | |
1519 | 1587 |
1520 SchedulePaint(); | 1588 SchedulePaint(); |
1521 | 1589 |
1522 bounds_animator_.StopAnimatingView(newtab_button_); | 1590 bounds_animator_.StopAnimatingView(newtab_button_); |
1523 newtab_button_->SetBoundsRect(newtab_button_bounds_); | 1591 newtab_button_->SetBoundsRect(newtab_button_bounds_); |
1524 } | 1592 } |
1525 | 1593 |
1594 void TabStrip::SetTabVisibility() { | |
sky
2014/06/17 19:41:17
Can you still hit the crash you sent to me with yo
Peter Kasting
2014/06/17 20:20:20
I removed the DCHECK in ShouldTabBeVisible() that
| |
1595 // We could probably be more efficient here by making use of the fact that the | |
1596 // tabstrip will always have any visible tabs, and then any invisible tabs, so | |
1597 // we could e.g. binary-search for the changeover point. But since we have to | |
1598 // iterate through all the tabs to call SetVisible() anyway, it doesn't seem | |
1599 // worth it. | |
1600 for (int i = 0; i < tab_count(); ++i) { | |
1601 Tab* tab = tab_at(i); | |
1602 tab->SetVisible(ShouldTabBeVisible(tab)); | |
1603 } | |
1604 for (TabsClosingMap::const_iterator i(tabs_closing_map_.begin()); | |
1605 i != tabs_closing_map_.end(); ++i) { | |
1606 for (Tabs::const_iterator j(i->second.begin()); j != i->second.end(); ++j) { | |
1607 Tab* tab = *j; | |
1608 tab->SetVisible(ShouldTabBeVisible(tab)); | |
1609 } | |
1610 } | |
1611 } | |
1612 | |
1526 void TabStrip::DragActiveTab(const std::vector<int>& initial_positions, | 1613 void TabStrip::DragActiveTab(const std::vector<int>& initial_positions, |
1527 int delta) { | 1614 int delta) { |
1528 DCHECK_EQ(tab_count(), static_cast<int>(initial_positions.size())); | 1615 DCHECK_EQ(tab_count(), static_cast<int>(initial_positions.size())); |
1529 if (!touch_layout_) { | 1616 if (!touch_layout_) { |
1530 StackDraggedTabs(delta); | 1617 StackDraggedTabs(delta); |
1531 return; | 1618 return; |
1532 } | 1619 } |
1533 SetIdealBoundsFromPositions(initial_positions); | 1620 SetIdealBoundsFromPositions(initial_positions); |
1534 touch_layout_->DragActiveTab(delta); | 1621 touch_layout_->DragActiveTab(delta); |
1535 DoLayout(); | 1622 DoLayout(); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1646 // consecutive animate the view into position. Do the same if the tab is | 1733 // consecutive animate the view into position. Do the same if the tab is |
1647 // already animating (which means we previously caused it to animate). | 1734 // already animating (which means we previously caused it to animate). |
1648 if ((initial_drag && | 1735 if ((initial_drag && |
1649 GetModelIndexOfTab(tabs[i]) != consecutive_index) || | 1736 GetModelIndexOfTab(tabs[i]) != consecutive_index) || |
1650 bounds_animator_.IsAnimating(tabs[i])) { | 1737 bounds_animator_.IsAnimating(tabs[i])) { |
1651 bounds_animator_.SetTargetBounds(tabs[i], new_bounds); | 1738 bounds_animator_.SetTargetBounds(tabs[i], new_bounds); |
1652 } else { | 1739 } else { |
1653 tab->SetBoundsRect(new_bounds); | 1740 tab->SetBoundsRect(new_bounds); |
1654 } | 1741 } |
1655 } | 1742 } |
1743 SetTabVisibility(); | |
1656 } | 1744 } |
1657 | 1745 |
1658 void TabStrip::CalculateBoundsForDraggedTabs(const Tabs& tabs, | 1746 void TabStrip::CalculateBoundsForDraggedTabs(const Tabs& tabs, |
1659 std::vector<gfx::Rect>* bounds) { | 1747 std::vector<gfx::Rect>* bounds) { |
1660 int x = 0; | 1748 int x = 0; |
1661 for (size_t i = 0; i < tabs.size(); ++i) { | 1749 for (size_t i = 0; i < tabs.size(); ++i) { |
1662 Tab* tab = tabs[i]; | 1750 Tab* tab = tabs[i]; |
1663 if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini) | 1751 if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini) |
1664 x += kMiniToNonMiniGap; | 1752 x += kMiniToNonMiniGap; |
1665 gfx::Rect new_bounds = tab->bounds(); | 1753 gfx::Rect new_bounds = tab->bounds(); |
(...skipping 17 matching lines...) Expand all Loading... | |
1683 } | 1771 } |
1684 | 1772 |
1685 int TabStrip::GetMiniTabCount() const { | 1773 int TabStrip::GetMiniTabCount() const { |
1686 int mini_count = 0; | 1774 int mini_count = 0; |
1687 while (mini_count < tab_count() && tab_at(mini_count)->data().mini) | 1775 while (mini_count < tab_count() && tab_at(mini_count)->data().mini) |
1688 mini_count++; | 1776 mini_count++; |
1689 return mini_count; | 1777 return mini_count; |
1690 } | 1778 } |
1691 | 1779 |
1692 const Tab* TabStrip::GetLastVisibleTab() const { | 1780 const Tab* TabStrip::GetLastVisibleTab() const { |
1693 return tab_at(tab_count() - 1); | 1781 for (int i = tab_count() - 1; i >= 0; --i) { |
1782 const Tab* tab = tab_at(i); | |
1783 if (tab->visible()) | |
1784 return tab; | |
1785 } | |
1786 NOTREACHED(); | |
1787 return NULL; | |
1694 } | 1788 } |
1695 | 1789 |
1696 void TabStrip::RemoveTabFromViewModel(int index) { | 1790 void TabStrip::RemoveTabFromViewModel(int index) { |
1697 // We still need to paint the tab until we actually remove it. Put it | 1791 // We still need to paint the tab until we actually remove it. Put it |
1698 // in tabs_closing_map_ so we can find it. | 1792 // in tabs_closing_map_ so we can find it. |
1699 tabs_closing_map_[index].push_back(tab_at(index)); | 1793 tabs_closing_map_[index].push_back(tab_at(index)); |
1700 UpdateTabsClosingMap(index + 1, -1); | 1794 UpdateTabsClosingMap(index + 1, -1); |
1701 tabs_.Remove(index); | 1795 tabs_.Remove(index); |
1702 } | 1796 } |
1703 | 1797 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1754 | 1848 |
1755 // Move the dragged tabs to their ideal bounds. | 1849 // Move the dragged tabs to their ideal bounds. |
1756 GenerateIdealBounds(); | 1850 GenerateIdealBounds(); |
1757 | 1851 |
1758 // Sets the bounds of the dragged tabs. | 1852 // Sets the bounds of the dragged tabs. |
1759 for (size_t i = 0; i < tabs.size(); ++i) { | 1853 for (size_t i = 0; i < tabs.size(); ++i) { |
1760 int tab_data_index = GetModelIndexOfTab(tabs[i]); | 1854 int tab_data_index = GetModelIndexOfTab(tabs[i]); |
1761 DCHECK_NE(-1, tab_data_index); | 1855 DCHECK_NE(-1, tab_data_index); |
1762 tabs[i]->SetBoundsRect(ideal_bounds(tab_data_index)); | 1856 tabs[i]->SetBoundsRect(ideal_bounds(tab_data_index)); |
1763 } | 1857 } |
1858 SetTabVisibility(); | |
1764 SchedulePaint(); | 1859 SchedulePaint(); |
1765 } | 1860 } |
1766 | 1861 |
1767 void TabStrip::DraggedTabsDetached() { | 1862 void TabStrip::DraggedTabsDetached() { |
1768 // Let the controller know that the user is not dragging this tabstrip's tabs | 1863 // Let the controller know that the user is not dragging this tabstrip's tabs |
1769 // anymore. | 1864 // anymore. |
1770 controller()->OnStoppedDraggingTabs(); | 1865 controller()->OnStoppedDraggingTabs(); |
1771 newtab_button_->SetVisible(true); | 1866 newtab_button_->SetVisible(true); |
1772 } | 1867 } |
1773 | 1868 |
(...skipping 29 matching lines...) Expand all Loading... | |
1803 | 1898 |
1804 // Animate the view back to its correct position. | 1899 // Animate the view back to its correct position. |
1805 GenerateIdealBounds(); | 1900 GenerateIdealBounds(); |
1806 AnimateToIdealBounds(); | 1901 AnimateToIdealBounds(); |
1807 } | 1902 } |
1808 bounds_animator_.AnimateViewTo(tab, ideal_bounds(tab_data_index)); | 1903 bounds_animator_.AnimateViewTo(tab, ideal_bounds(tab_data_index)); |
1809 // Install a delegate to reset the dragging state when done. We have to leave | 1904 // 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. | 1905 // dragging true for the tab otherwise it'll draw beneath the new tab button. |
1811 bounds_animator_.SetAnimationDelegate( | 1906 bounds_animator_.SetAnimationDelegate( |
1812 tab, | 1907 tab, |
1813 scoped_ptr<gfx::AnimationDelegate>(new ResetDraggingStateDelegate(tab))); | 1908 scoped_ptr<gfx::AnimationDelegate>( |
1909 new ResetDraggingStateDelegate(this, tab))); | |
1814 } | 1910 } |
1815 | 1911 |
1816 void TabStrip::OwnDragController(TabDragController* controller) { | 1912 void TabStrip::OwnDragController(TabDragController* controller) { |
1817 // Typically, ReleaseDragController() and OwnDragController() calls are paired | 1913 // Typically, ReleaseDragController() and OwnDragController() calls are paired |
1818 // via corresponding calls to TabDragController::Detach() and | 1914 // via corresponding calls to TabDragController::Detach() and |
1819 // TabDragController::Attach(). There is one exception to that rule: when a | 1915 // TabDragController::Attach(). There is one exception to that rule: when a |
1820 // drag might start, we create a TabDragController that is owned by the | 1916 // drag might start, we create a TabDragController that is owned by the |
1821 // potential source tabstrip in MaybeStartDrag(). If a drag actually starts, | 1917 // potential source tabstrip in MaybeStartDrag(). If a drag actually starts, |
1822 // we then call Attach() on the source tabstrip, but since the source tabstrip | 1918 // 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. | 1919 // 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 | 2047 |
1952 *unselected_width = min_unselected_width; | 2048 *unselected_width = min_unselected_width; |
1953 *selected_width = min_selected_width; | 2049 *selected_width = min_selected_width; |
1954 | 2050 |
1955 if (tab_count == 0) { | 2051 if (tab_count == 0) { |
1956 // Return immediately to avoid divide-by-zero below. | 2052 // Return immediately to avoid divide-by-zero below. |
1957 return; | 2053 return; |
1958 } | 2054 } |
1959 | 2055 |
1960 // Determine how much space we can actually allocate to tabs. | 2056 // Determine how much space we can actually allocate to tabs. |
1961 int available_width; | 2057 int available_width = (available_width_for_tabs_ < 0) ? |
1962 if (available_width_for_tabs_ < 0) { | 2058 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) { | 2059 if (mini_tab_count > 0) { |
1978 available_width -= | 2060 available_width -= |
1979 mini_tab_count * (Tab::GetMiniWidth() + kTabHorizontalOffset); | 2061 mini_tab_count * (Tab::GetMiniWidth() + kTabHorizontalOffset); |
1980 tab_count -= mini_tab_count; | 2062 tab_count -= mini_tab_count; |
1981 if (tab_count == 0) { | 2063 if (tab_count == 0) { |
1982 *selected_width = *unselected_width = Tab::GetStandardSize().width(); | 2064 *selected_width = *unselected_width = Tab::GetStandardSize().width(); |
1983 return; | 2065 return; |
1984 } | 2066 } |
1985 // Account for gap between the last mini-tab and first non-mini-tab. | 2067 // Account for gap between the last mini-tab and first non-mini-tab. |
1986 available_width -= kMiniToNonMiniGap; | 2068 available_width -= kMiniToNonMiniGap; |
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2453 // many mini-tabs there are). | 2535 // many mini-tabs there are). |
2454 GenerateIdealBoundsForMiniTabs(NULL); | 2536 GenerateIdealBoundsForMiniTabs(NULL); |
2455 touch_layout_->SetXAndMiniCount(GetStartXForNormalTabs(), | 2537 touch_layout_->SetXAndMiniCount(GetStartXForNormalTabs(), |
2456 GetMiniTabCount()); | 2538 GetMiniTabCount()); |
2457 touch_layout_->SetActiveIndex(controller_->GetActiveIndex()); | 2539 touch_layout_->SetActiveIndex(controller_->GetActiveIndex()); |
2458 } else { | 2540 } else { |
2459 touch_layout_.reset(); | 2541 touch_layout_.reset(); |
2460 } | 2542 } |
2461 PrepareForAnimation(); | 2543 PrepareForAnimation(); |
2462 GenerateIdealBounds(); | 2544 GenerateIdealBounds(); |
2545 SetTabVisibility(); | |
2463 AnimateToIdealBounds(); | 2546 AnimateToIdealBounds(); |
2464 } | 2547 } |
2465 | 2548 |
2466 bool TabStrip::NeedsTouchLayout() const { | 2549 bool TabStrip::NeedsTouchLayout() const { |
2467 if (!stacked_layout_) | 2550 if (!stacked_layout_) |
2468 return false; | 2551 return false; |
2469 | 2552 |
2470 int mini_tab_count = GetMiniTabCount(); | 2553 int mini_tab_count = GetMiniTabCount(); |
2471 int normal_count = tab_count() - mini_tab_count; | 2554 int normal_count = tab_count() - mini_tab_count; |
2472 if (normal_count <= 1 || normal_count == mini_tab_count) | 2555 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; | 2688 action = TouchUMA::GESTURE_TABSWITCH_TAP; |
2606 TouchUMA::RecordGestureAction(action); | 2689 TouchUMA::RecordGestureAction(action); |
2607 break; | 2690 break; |
2608 } | 2691 } |
2609 | 2692 |
2610 default: | 2693 default: |
2611 break; | 2694 break; |
2612 } | 2695 } |
2613 event->SetHandled(); | 2696 event->SetHandled(); |
2614 } | 2697 } |
OLD | NEW |