Commit b56e9fc5 authored by Bret Sepulveda's avatar Bret Sepulveda Committed by Commit Bot

Fix slot index being ambiguous depending on the group of the tab.

If a tab wants to move to model index to the left of the leftmost tab
in a group, where it should end up in TabStripLayoutHelper::slots_ is
ambiguous. If it is in the same group as the grouped tab, it should go
to the right of the header so the group remains contiguous. But if not,
it should go to the left. To disambiguate this case,
GetSlotIndexForTabModelIndex now takes the group of the tab at
|model_index| as a parameter. It cannot look up the Tab* in
TabStrip::tabs_ in the general case, since it might be called in the
middle of processing a MoveTab or RemoveTab, where the model indices
have already been updated in that data structure.

Bug: 989964
Change-Id: I2f35218d01c8398e6e024096bd35244ca67ca330
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1764181
Commit-Queue: Bret Sepulveda <bsep@chromium.org>
Reviewed-by: default avatarTaylor Bergquist <tbergquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#690053}
parent 58311c27
...@@ -2170,7 +2170,7 @@ void TabStrip::StartRemoveTabAnimation(int model_index, bool was_active) { ...@@ -2170,7 +2170,7 @@ void TabStrip::StartRemoveTabAnimation(int model_index, bool was_active) {
UpdateIdealBoundsForPinnedTabs(nullptr), old_x); UpdateIdealBoundsForPinnedTabs(nullptr), old_x);
} }
layout_helper_->RemoveTab(model_index); layout_helper_->RemoveTab(model_index, tab);
} }
void TabStrip::StartFallbackRemoveTabAnimation(int model_index, void TabStrip::StartFallbackRemoveTabAnimation(int model_index,
...@@ -2224,7 +2224,7 @@ void TabStrip::StartFallbackRemoveTabAnimation(int model_index, ...@@ -2224,7 +2224,7 @@ void TabStrip::StartFallbackRemoveTabAnimation(int model_index,
UpdateIdealBoundsForPinnedTabs(nullptr), old_x); UpdateIdealBoundsForPinnedTabs(nullptr), old_x);
} }
layout_helper_->RemoveTabNoAnimation(model_index); layout_helper_->RemoveTabNoAnimation(model_index, tab);
UpdateIdealBounds(); UpdateIdealBounds();
AnimateToIdealBounds(); AnimateToIdealBounds();
......
...@@ -124,7 +124,8 @@ void TabStripLayoutHelper::InsertTabAtNoAnimation( ...@@ -124,7 +124,8 @@ void TabStripLayoutHelper::InsertTabAtNoAnimation(
Tab* tab, Tab* tab,
base::OnceClosure tab_removed_callback, base::OnceClosure tab_removed_callback,
TabAnimationState::TabPinnedness pinned) { TabAnimationState::TabPinnedness pinned) {
const int slot_index = GetSlotIndexForTabModelIndex(model_index); const int slot_index =
GetSlotIndexForTabModelIndex(model_index, tab->group());
slots_.insert( slots_.insert(
slots_.begin() + slot_index, slots_.begin() + slot_index,
TabSlot::CreateForTab(tab, TabAnimationState::TabOpenness::kOpen, pinned, TabSlot::CreateForTab(tab, TabAnimationState::TabOpenness::kOpen, pinned,
...@@ -136,7 +137,8 @@ void TabStripLayoutHelper::InsertTabAt( ...@@ -136,7 +137,8 @@ void TabStripLayoutHelper::InsertTabAt(
Tab* tab, Tab* tab,
base::OnceClosure tab_removed_callback, base::OnceClosure tab_removed_callback,
TabAnimationState::TabPinnedness pinned) { TabAnimationState::TabPinnedness pinned) {
const int slot_index = GetSlotIndexForTabModelIndex(model_index); const int slot_index =
GetSlotIndexForTabModelIndex(model_index, tab->group());
slots_.insert( slots_.insert(
slots_.begin() + slot_index, slots_.begin() + slot_index,
TabSlot::CreateForTab(tab, TabAnimationState::TabOpenness::kClosed, TabSlot::CreateForTab(tab, TabAnimationState::TabOpenness::kClosed,
...@@ -146,16 +148,17 @@ void TabStripLayoutHelper::InsertTabAt( ...@@ -146,16 +148,17 @@ void TabStripLayoutHelper::InsertTabAt(
TabAnimationState::TabOpenness::kOpen)); TabAnimationState::TabOpenness::kOpen));
} }
void TabStripLayoutHelper::RemoveTabNoAnimation(int model_index) { void TabStripLayoutHelper::RemoveTabNoAnimation(int model_index, Tab* tab) {
TabAnimation* animation = TabAnimation* animation =
slots_[GetSlotIndexForTabModelIndex(model_index)].animation.get(); slots_[GetSlotIndexForTabModelIndex(model_index, tab->group())]
.animation.get();
animation->AnimateTo(animation->target_state().WithOpenness( animation->AnimateTo(animation->target_state().WithOpenness(
TabAnimationState::TabOpenness::kClosed)); TabAnimationState::TabOpenness::kClosed));
animation->CompleteAnimation(); animation->CompleteAnimation();
} }
void TabStripLayoutHelper::RemoveTab(int model_index) { void TabStripLayoutHelper::RemoveTab(int model_index, Tab* tab) {
int slot_index = GetSlotIndexForTabModelIndex(model_index); int slot_index = GetSlotIndexForTabModelIndex(model_index, tab->group());
AnimateSlot(slot_index, AnimateSlot(slot_index,
slots_[slot_index].animation->target_state().WithOpenness( slots_[slot_index].animation->target_state().WithOpenness(
TabAnimationState::TabOpenness::kClosed)); TabAnimationState::TabOpenness::kClosed));
...@@ -172,27 +175,31 @@ void TabStripLayoutHelper::OnTabDestroyed(Tab* tab) { ...@@ -172,27 +175,31 @@ void TabStripLayoutHelper::OnTabDestroyed(Tab* tab) {
slots_.erase(it); slots_.erase(it);
} }
void TabStripLayoutHelper::MoveTab( void TabStripLayoutHelper::MoveTab(base::Optional<TabGroupId> moving_tab_group,
base::Optional<TabGroupId> group_at_prev_index, int prev_index,
int prev_index, int new_index) {
int new_index) { const int prev_slot_index =
const int prev_slot_index = GetSlotIndexForTabModelIndex(prev_index); GetSlotIndexForTabModelIndex(prev_index, moving_tab_group);
TabSlot moving_tab = std::move(slots_[prev_slot_index]); TabSlot moving_tab = std::move(slots_[prev_slot_index]);
slots_.erase(slots_.begin() + prev_slot_index); slots_.erase(slots_.begin() + prev_slot_index);
const int new_slot_index = GetSlotIndexForTabModelIndex(new_index); const int new_slot_index =
GetSlotIndexForTabModelIndex(new_index, moving_tab_group);
slots_.insert(slots_.begin() + new_slot_index, std::move(moving_tab)); slots_.insert(slots_.begin() + new_slot_index, std::move(moving_tab));
if (group_at_prev_index.has_value()) if (moving_tab_group.has_value())
UpdateGroupHeaderIndex(group_at_prev_index.value()); UpdateGroupHeaderIndex(moving_tab_group.value());
} }
void TabStripLayoutHelper::SetTabPinnedness( void TabStripLayoutHelper::SetTabPinnedness(
int model_index, int model_index,
TabAnimationState::TabPinnedness pinnedness) { TabAnimationState::TabPinnedness pinnedness) {
// TODO(958173): Animate state change. // TODO(958173): Animate state change.
views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
TabAnimation* animation = TabAnimation* animation =
slots_[GetSlotIndexForTabModelIndex(model_index)].animation.get(); slots_[GetSlotIndexForTabModelIndex(model_index,
tabs->view_at(model_index)->group())]
.animation.get();
animation->AnimateTo(animation->target_state().WithPinnedness(pinnedness)); animation->AnimateTo(animation->target_state().WithPinnedness(pinnedness));
animation->CompleteAnimation(); animation->CompleteAnimation();
} }
...@@ -203,7 +210,8 @@ void TabStripLayoutHelper::InsertGroupHeader( ...@@ -203,7 +210,8 @@ void TabStripLayoutHelper::InsertGroupHeader(
base::OnceClosure header_removed_callback) { base::OnceClosure header_removed_callback) {
// TODO(958173): Animate open. // TODO(958173): Animate open.
std::vector<int> tabs_in_group = controller_->ListTabsInGroup(group); std::vector<int> tabs_in_group = controller_->ListTabsInGroup(group);
const int header_slot_index = GetSlotIndexForTabModelIndex(tabs_in_group[0]); const int header_slot_index =
GetSlotIndexForTabModelIndex(tabs_in_group[0], group);
slots_.insert(slots_.begin() + header_slot_index, slots_.insert(slots_.begin() + header_slot_index,
TabSlot::CreateForGroupHeader( TabSlot::CreateForGroupHeader(
group, header, TabAnimationState::TabPinnedness::kUnpinned, group, header, TabAnimationState::TabPinnedness::kUnpinned,
...@@ -224,21 +232,24 @@ void TabStripLayoutHelper::UpdateGroupHeaderIndex(TabGroupId group) { ...@@ -224,21 +232,24 @@ void TabStripLayoutHelper::UpdateGroupHeaderIndex(TabGroupId group) {
slots_.erase(slots_.begin() + slot_index); slots_.erase(slots_.begin() + slot_index);
std::vector<int> tabs_in_group = controller_->ListTabsInGroup(group); std::vector<int> tabs_in_group = controller_->ListTabsInGroup(group);
const int first_tab_slot_index = const int first_tab_slot_index =
GetSlotIndexForTabModelIndex(tabs_in_group[0]); GetSlotIndexForTabModelIndex(tabs_in_group[0], group);
slots_.insert(slots_.begin() + first_tab_slot_index, std::move(header_slot)); slots_.insert(slots_.begin() + first_tab_slot_index, std::move(header_slot));
} }
void TabStripLayoutHelper::SetActiveTab(int prev_active_index, void TabStripLayoutHelper::SetActiveTab(int prev_active_index,
int new_active_index) { int new_active_index) {
views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
// Set activeness without animating by retargeting the existing animation. // Set activeness without animating by retargeting the existing animation.
if (prev_active_index >= 0) { if (prev_active_index >= 0) {
const int prev_slot_index = GetSlotIndexForTabModelIndex(prev_active_index); const int prev_slot_index = GetSlotIndexForTabModelIndex(
prev_active_index, tabs->view_at(prev_active_index)->group());
TabAnimation* animation = slots_[prev_slot_index].animation.get(); TabAnimation* animation = slots_[prev_slot_index].animation.get();
animation->RetargetTo(animation->target_state().WithActiveness( animation->RetargetTo(animation->target_state().WithActiveness(
TabAnimationState::TabActiveness::kInactive)); TabAnimationState::TabActiveness::kInactive));
} }
if (new_active_index >= 0) { if (new_active_index >= 0) {
const int new_slot_index = GetSlotIndexForTabModelIndex(new_active_index); const int new_slot_index = GetSlotIndexForTabModelIndex(
new_active_index, tabs->view_at(new_active_index)->group());
TabAnimation* animation = slots_[new_slot_index].animation.get(); TabAnimation* animation = slots_[new_slot_index].animation.get();
animation->RetargetTo(animation->target_state().WithActiveness( animation->RetargetTo(animation->target_state().WithActiveness(
TabAnimationState::TabActiveness::kActive)); TabAnimationState::TabActiveness::kActive));
...@@ -257,14 +268,23 @@ void TabStripLayoutHelper::CompleteAnimationsWithoutDestroyingTabs() { ...@@ -257,14 +268,23 @@ void TabStripLayoutHelper::CompleteAnimationsWithoutDestroyingTabs() {
} }
void TabStripLayoutHelper::UpdateIdealBounds(int available_width) { void TabStripLayoutHelper::UpdateIdealBounds(int available_width) {
views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
std::map<TabGroupId, TabGroupHeader*> group_headers =
get_group_headers_callback_.Run();
const int active_tab_model_index = controller_->GetActiveIndex(); const int active_tab_model_index = controller_->GetActiveIndex();
const int active_tab_slot_index = const int active_tab_slot_index =
controller_->IsValidIndex(active_tab_model_index) controller_->IsValidIndex(active_tab_model_index)
? GetSlotIndexForTabModelIndex(active_tab_model_index) ? GetSlotIndexForTabModelIndex(
active_tab_model_index,
tabs->view_at(active_tab_model_index)->group())
: TabStripModel::kNoTab; : TabStripModel::kNoTab;
const int pinned_tab_count = GetPinnedTabCount(); const int pinned_tab_count = GetPinnedTabCount();
const int last_pinned_tab_index = pinned_tab_count - 1;
const int last_pinned_tab_slot_index = const int last_pinned_tab_slot_index =
pinned_tab_count > 0 ? GetSlotIndexForTabModelIndex(pinned_tab_count - 1) pinned_tab_count > 0 ? GetSlotIndexForTabModelIndex(
last_pinned_tab_index,
tabs->view_at(last_pinned_tab_index)->group())
: TabStripModel::kNoTab; : TabStripModel::kNoTab;
TabLayoutConstants layout_constants = GetTabLayoutConstants(); TabLayoutConstants layout_constants = GetTabLayoutConstants();
...@@ -290,10 +310,6 @@ void TabStripLayoutHelper::UpdateIdealBounds(int available_width) { ...@@ -290,10 +310,6 @@ void TabStripLayoutHelper::UpdateIdealBounds(int available_width) {
CalculateTabBounds(layout_constants, tab_widths, available_width); CalculateTabBounds(layout_constants, tab_widths, available_width);
DCHECK_EQ(slots_.size(), bounds.size()); DCHECK_EQ(slots_.size(), bounds.size());
views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
std::map<TabGroupId, TabGroupHeader*> group_headers =
get_group_headers_callback_.Run();
int current_tab_model_index = 0; int current_tab_model_index = 0;
for (int i = 0; i < int{bounds.size()}; ++i) { for (int i = 0; i < int{bounds.size()}; ++i) {
const TabSlot& slot = slots_[i]; const TabSlot& slot = slots_[i];
...@@ -395,14 +411,26 @@ int TabStripLayoutHelper::LayoutTabs(int available_width) { ...@@ -395,14 +411,26 @@ int TabStripLayoutHelper::LayoutTabs(int available_width) {
return trailing_x; return trailing_x;
} }
int TabStripLayoutHelper::GetSlotIndexForTabModelIndex(int model_index) const { int TabStripLayoutHelper::GetSlotIndexForTabModelIndex(
int model_index,
base::Optional<TabGroupId> group) const {
int current_model_index = 0; int current_model_index = 0;
for (size_t i = 0; i < slots_.size(); i++) { for (size_t i = 0; i < slots_.size(); i++) {
if (slots_[i].type == ViewType::kTab && !slots_[i].animation->IsClosing()) { const bool model_space_index =
if (current_model_index == model_index) slots_[i].type == ViewType::kTab && !slots_[i].animation->IsClosing();
if (current_model_index == model_index) {
if (model_space_index) {
return i; return i;
++current_model_index; } else if (slots_[i].type == ViewType::kGroupHeader) {
// If the tab is in the header's group, then it should be to its right,
// so as to be contiguous with the group. If not, it goes to its left.
return static_cast<TabGroupHeader*>(slots_[i].view)->group() == group
? i + 1
: i;
}
} }
if (model_space_index)
++current_model_index;
} }
DCHECK_EQ(model_index, current_model_index); DCHECK_EQ(model_index, current_model_index);
return slots_.size(); return slots_.size();
......
...@@ -70,18 +70,18 @@ class TabStripLayoutHelper { ...@@ -70,18 +70,18 @@ class TabStripLayoutHelper {
// the tab has been removed from the model but the old animation style owns // the tab has been removed from the model but the old animation style owns
// animating it. // animating it.
// TODO(958173): Remove this when the old animation style is removed. // TODO(958173): Remove this when the old animation style is removed.
void RemoveTabNoAnimation(int model_index); void RemoveTabNoAnimation(int model_index, Tab* tab);
// Marks the tab at |model_index| as closing and animates it closed. // Marks the tab at |model_index| as closing and animates it closed.
void RemoveTab(int model_index); void RemoveTab(int model_index, Tab* tab);
// Invoked when |tab| has been destroyed by TabStrip (i.e. the remove // Invoked when |tab| has been destroyed by TabStrip (i.e. the remove
// animation has completed). // animation has completed).
void OnTabDestroyed(Tab* tab); void OnTabDestroyed(Tab* tab);
// Moves the tab at |prev_index| with group |group_at_prev_index| to // Moves the tab at |prev_index| with group |moving_tab_group| to |new_index|.
// |new_index|. Also updates the group header's location if necessary. // Also updates the group header's location if necessary.
void MoveTab(base::Optional<TabGroupId> group_at_prev_index, void MoveTab(base::Optional<TabGroupId> moving_tab_group,
int prev_index, int prev_index,
int new_index); int new_index);
...@@ -135,9 +135,10 @@ class TabStripLayoutHelper { ...@@ -135,9 +135,10 @@ class TabStripLayoutHelper {
private: private:
struct TabSlot; struct TabSlot;
// Given a tab's |model_index|, returns the index of its corresponding TabSlot // Given a tab's |model_index| and |group|, returns the index of its
// in |slots_|. // corresponding TabSlot in |slots_|.
int GetSlotIndexForTabModelIndex(int model_index) const; int GetSlotIndexForTabModelIndex(int model_index,
base::Optional<TabGroupId> group) const;
// Given a group ID, returns the index of its header's corresponding TabSlot // Given a group ID, returns the index of its header's corresponding TabSlot
// in |slots_|. // in |slots_|.
......
...@@ -1121,6 +1121,23 @@ TEST_P(TabStripTest, GroupHeaderMovesOnRegrouping) { ...@@ -1121,6 +1121,23 @@ TEST_P(TabStripTest, GroupHeaderMovesOnRegrouping) {
EXPECT_LT(header1->x(), tab_strip_->tab_at(2)->x()); EXPECT_LT(header1->x(), tab_strip_->tab_at(2)->x());
} }
TEST_P(TabStripTest, UngroupedTabMovesLeftOfHeader) {
tab_strip_->SetBounds(0, 0, 2000, 100);
for (int i = 0; i < 2; i++)
tab_strip_->AddTabAt(i, TabRendererData(), false);
TabGroupId group = TabGroupId::GenerateNew();
controller_->MoveTabIntoGroup(0, group);
CompleteAnimationAndLayout();
controller_->MoveTab(1, 0);
CompleteAnimationAndLayout();
// Header is right of tab 0.
TabGroupHeader* header = ListGroupHeaders()[0];
EXPECT_LT(tab_strip_->tab_at(0)->x(), header->x());
EXPECT_LT(header->x(), tab_strip_->tab_at(1)->x());
}
// This can happen when a tab in the middle of a group starts to close. // This can happen when a tab in the middle of a group starts to close.
TEST_P(TabStripTest, DiscontinuousGroup) { TEST_P(TabStripTest, DiscontinuousGroup) {
tab_strip_->SetBounds(0, 0, 1000, 100); tab_strip_->SetBounds(0, 0, 1000, 100);
......
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