Commit e8225fad authored by pkasting@chromium.org's avatar pkasting@chromium.org

Clip tabs in overflow mode.

The tabstrip now tries to ensure that if tabs would encroach on the New Tab
button area or be clipped by the edge of the strip, they're made invisible
instead.  This prevents glitchy-looking overflow, modulo some existing bugs (on
file) where the strip doesn't recalculate widths correctly.

This also hides the tab next to the New Tab button if it can be shown when not
selected, but might be hidden when it (or a prior tab) is selected.  This
prevents having this tab toggle in and out as the active tab changes.

BUG=62510
TEST=Spawn lots of tabs and shrink the window to a narrow width.  Once the tabs hit their min size they should start hiding at the right edge instead of drawing atop the New Tab button.

Review URL: https://codereview.chromium.org/339923005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278028 0039d316-1c4b-4281-b951-d872f2087c98
parent e53c441b
...@@ -1317,15 +1317,18 @@ int TabDragController::GetInsertionIndexForDraggedBounds( ...@@ -1317,15 +1317,18 @@ int TabDragController::GetInsertionIndexForDraggedBounds(
index = (dragged_bounds.right() > last_tab_right) ? tab_count : 0; index = (dragged_bounds.right() > last_tab_right) ? tab_count : 0;
} }
if (!drag_data_[0].attached_tab) { const Tab* last_visible_tab = attached_tabstrip_->GetLastVisibleTab();
// If 'attached_tab' is NULL, it means we're in the process of attaching and int last_insertion_point = last_visible_tab ?
// don't need to constrain the index. (attached_tabstrip_->GetModelIndexOfTab(last_visible_tab) + 1) : 0;
return index; if (drag_data_[0].attached_tab) {
// We're not in the process of attaching, so clamp the insertion point to
// keep it within the visible region.
last_insertion_point = std::max(
0, last_insertion_point - static_cast<int>(drag_data_.size()));
} }
int max_index = GetModel(attached_tabstrip_)->count() - // Ensure the first dragged tab always stays in the visible index range.
static_cast<int>(drag_data_.size()); return std::min(index, last_insertion_point);
return std::max(0, std::min(max_index, index));
} }
bool TabDragController::ShouldDragToNextStackedTab( bool TabDragController::ShouldDragToNextStackedTab(
......
This diff is collapsed.
...@@ -113,6 +113,13 @@ class TabStrip : public views::View, ...@@ -113,6 +113,13 @@ class TabStrip : public views::View,
// Sets the tab data at the specified model index. // Sets the tab data at the specified model index.
void SetTabData(int model_index, const TabRendererData& data); void SetTabData(int model_index, const TabRendererData& data);
// Returns true if the tab is not partly or fully clipped (due to overflow),
// and the tab couldn't become partly clipped due to changing the selected tab
// (for example, if currently the strip has the last tab selected, and
// changing that to the first tab would cause |tab| to be pushed over enough
// to clip).
bool ShouldTabBeVisible(const Tab* tab) const;
// Invoked from the controller when the close initiates from the TabController // Invoked from the controller when the close initiates from the TabController
// (the user clicked the tab close button or middle clicked the tab). This is // (the user clicked the tab close button or middle clicked the tab). This is
// invoked from Close. Because of unload handlers Close is not always // invoked from Close. Because of unload handlers Close is not always
...@@ -337,6 +344,9 @@ class TabStrip : public views::View, ...@@ -337,6 +344,9 @@ class TabStrip : public views::View,
// Invoked from Layout if the size changes or layout is really needed. // Invoked from Layout if the size changes or layout is really needed.
void DoLayout(); void DoLayout();
// Sets the visibility state of all tabs based on ShouldTabBeVisible().
void SetTabVisibility();
// Drags the active tab by |delta|. |initial_positions| is the x-coordinates // Drags the active tab by |delta|. |initial_positions| is the x-coordinates
// of the tabs when the drag started. // of the tabs when the drag started.
void DragActiveTab(const std::vector<int>& initial_positions, int delta); void DragActiveTab(const std::vector<int>& initial_positions, int delta);
...@@ -372,7 +382,8 @@ class TabStrip : public views::View, ...@@ -372,7 +382,8 @@ class TabStrip : public views::View,
// Returns the number of mini-tabs. // Returns the number of mini-tabs.
int GetMiniTabCount() const; int GetMiniTabCount() const;
// Returns the last tab in the strip. // Returns the last tab in the strip that's actually visible. This will be
// the actual last tab unless the strip is in the overflow state.
const Tab* GetLastVisibleTab() const; const Tab* GetLastVisibleTab() const;
// Adds the tab at |index| to |tabs_closing_map_| and removes the tab from // Adds the tab at |index| to |tabs_closing_map_| and removes the tab from
......
...@@ -205,6 +205,62 @@ TEST_F(TabStripTest, RemoveTab) { ...@@ -205,6 +205,62 @@ TEST_F(TabStripTest, RemoveTab) {
EXPECT_EQ(0, observer.last_tab_removed()); EXPECT_EQ(0, observer.last_tab_removed());
} }
TEST_F(TabStripTest, VisibilityInOverflow) {
tab_strip_->SetBounds(0, 0, 200, 20);
// The first tab added to a reasonable-width strip should be visible. If we
// add enough additional tabs, eventually one should be invisible due to
// overflow.
int invisible_tab_index = 0;
for (; invisible_tab_index < 100; ++invisible_tab_index) {
controller_->AddTab(invisible_tab_index, false);
if (!tab_strip_->tab_at(invisible_tab_index)->visible())
break;
}
EXPECT_GT(invisible_tab_index, 0);
EXPECT_LT(invisible_tab_index, 100);
// The tabs before the invisible tab should still be visible.
for (int i = 0; i < invisible_tab_index; ++i)
EXPECT_TRUE(tab_strip_->tab_at(i)->visible());
// Enlarging the strip should result in the last tab becoming visible.
tab_strip_->SetBounds(0, 0, 400, 20);
EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index)->visible());
// Shrinking it again should re-hide the last tab.
tab_strip_->SetBounds(0, 0, 200, 20);
EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
// Shrinking it still more should make more tabs invisible, though not all.
// All the invisible tabs should be at the end of the strip.
tab_strip_->SetBounds(0, 0, 100, 20);
int i = 0;
for (; i < invisible_tab_index; ++i) {
if (!tab_strip_->tab_at(i)->visible())
break;
}
ASSERT_GT(i, 0);
EXPECT_LT(i, invisible_tab_index);
invisible_tab_index = i;
for (int i = invisible_tab_index + 1; i < tab_strip_->tab_count(); ++i)
EXPECT_FALSE(tab_strip_->tab_at(i)->visible());
// When we're already in overflow, adding tabs at the beginning or end of
// the strip should not change how many tabs are visible.
controller_->AddTab(tab_strip_->tab_count(), false);
EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index - 1)->visible());
EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
controller_->AddTab(0, false);
EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index - 1)->visible());
EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
// If we remove enough tabs, all the tabs should be visible.
for (int i = tab_strip_->tab_count() - 1; i >= invisible_tab_index; --i)
controller_->RemoveTab(i);
EXPECT_TRUE(tab_strip_->tab_at(tab_strip_->tab_count() - 1)->visible());
}
TEST_F(TabStripTest, ImmersiveMode) { TEST_F(TabStripTest, ImmersiveMode) {
// Immersive mode defaults to off. // Immersive mode defaults to off.
EXPECT_FALSE(tab_strip_->IsImmersiveStyle()); EXPECT_FALSE(tab_strip_->IsImmersiveStyle());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment