summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/tabs/tab_strip.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/tabs/tab_strip.cc')
-rw-r--r--chrome/browser/views/tabs/tab_strip.cc1369
1 files changed, 834 insertions, 535 deletions
diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc
index a28ff52b..1a9edb3 100644
--- a/chrome/browser/views/tabs/tab_strip.cc
+++ b/chrome/browser/views/tabs/tab_strip.cc
@@ -4,14 +4,12 @@
#include "chrome/browser/views/tabs/tab_strip.h"
-#include "app/animation_container.h"
#include "app/drag_drop_types.h"
#include "app/l10n_util.h"
#include "app/os_exchange_data.h"
#include "app/resource_bundle.h"
#include "app/slide_animation.h"
#include "base/command_line.h"
-#include "base/compiler_specific.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_theme_provider.h"
@@ -27,7 +25,6 @@
#include "chrome/browser/views/tabs/tab.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
#include "gfx/canvas.h"
#include "gfx/path.h"
#include "gfx/size.h"
@@ -57,21 +54,10 @@
using views::DropTargetEvent;
-// Duration of the first step in a new tab animation.
-static const int kNewTabDurationMs = 50;
-
-// Duration of the last step in the new tab animation.
-static const int kNewTab3DurationMs = 100;
-
-// Amount in pixels newly inserted tabs go past target bounds before animating
-// to final position. This is used for ANIMATION_NEW_TAB_2.
-static const int kNewTabOvershoot = 9;
-
-// Amount in pixels the newly inserted tab is clipped against the previous
-// tab while animating. This is used to make sure the user doesn't see the
-// newly inserted tab behind other tabs and so that its shadow isn't visible
-// until the user can actually see the tab.
-static const int kNetTabSelectedOffset = -13;
+static const int kDefaultAnimationDurationMs = 200;
+static const int kResizeLayoutAnimationDurationMs = 200;
+static const int kReorderAnimationDurationMs = 200;
+static const int kMiniTabAnimationDurationMs = 200;
static const int kNewTabButtonHOffset = -5;
static const int kNewTabButtonVOffset = 5;
@@ -95,27 +81,6 @@ static inline int Round(double x) {
return static_cast<int>(floor(x + 0.5));
}
-namespace {
-
-// Animation delegate used during new tab animation step 2 to vary the alpha of
-// the tab.
-class NewTabAlphaDelegate
- : public views::BoundsAnimator::OwnedAnimationDelegate {
- public:
- explicit NewTabAlphaDelegate(Tab* tab) : tab_(tab) {
- }
-
- virtual void AnimationProgressed(const Animation* animation) {
- if (tab_->render_unselected())
- tab_->set_alpha(animation->GetCurrentValue());
- }
-
- private:
- Tab* tab_;
-
- DISALLOW_COPY_AND_ASSIGN(NewTabAlphaDelegate);
-};
-
///////////////////////////////////////////////////////////////////////////////
// NewTabButton
//
@@ -159,44 +124,292 @@ class NewTabButton : public views::ImageButton {
DISALLOW_COPY_AND_ASSIGN(NewTabButton);
};
-} // namespace
-
-// AnimationDelegate used when removing a tab. Does the necessary cleanup when
-// done.
-class TabStrip::RemoveTabDelegate
- : public views::BoundsAnimator::OwnedAnimationDelegate {
+///////////////////////////////////////////////////////////////////////////////
+//
+// TabAnimation
+//
+// A base class for all TabStrip animations.
+//
+class TabStrip::TabAnimation : public AnimationDelegate {
public:
- RemoveTabDelegate(TabStrip* tab_strip, Tab* tab)
- : tabstrip_(tab_strip),
- tab_(tab) {
+ friend class TabStrip;
+
+ // Possible types of animation.
+ enum Type {
+ INSERT,
+ REMOVE,
+ MOVE,
+ RESIZE,
+ MINI,
+ MINI_MOVE
+ };
+
+ TabAnimation(TabStrip* tabstrip, Type type)
+ : tabstrip_(tabstrip),
+ animation_(this),
+ start_selected_width_(0),
+ start_unselected_width_(0),
+ end_selected_width_(0),
+ end_unselected_width_(0),
+ layout_on_completion_(false),
+ type_(type) {
+ }
+ virtual ~TabAnimation() {}
+
+ Type type() const { return type_; }
+
+ void Start() {
+ animation_.SetSlideDuration(GetDuration());
+ animation_.SetTweenType(SlideAnimation::EASE_OUT);
+ if (!animation_.IsShowing()) {
+ animation_.Reset();
+ animation_.Show();
+ }
+ }
+
+ void Stop() {
+ animation_.Stop();
+ }
+
+ void set_layout_on_completion(bool layout_on_completion) {
+ layout_on_completion_ = layout_on_completion;
+ }
+
+ // Retrieves the width for the Tab at the specified index if an animation is
+ // active.
+ static double GetCurrentTabWidth(TabStrip* tabstrip,
+ TabStrip::TabAnimation* animation,
+ int index) {
+ Tab* tab = tabstrip->GetTabAt(index);
+ double tab_width;
+ if (tab->mini()) {
+ tab_width = Tab::GetMiniWidth();
+ } else {
+ double unselected, selected;
+ tabstrip->GetCurrentTabWidths(&unselected, &selected);
+ tab_width = tab->IsSelected() ? selected : unselected;
+ }
+ if (animation) {
+ double specified_tab_width = animation->GetWidthForTab(index);
+ if (specified_tab_width != -1)
+ tab_width = specified_tab_width;
+ }
+ return tab_width;
+ }
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation) {
+ tabstrip_->AnimationLayout(end_unselected_width_);
}
virtual void AnimationEnded(const Animation* animation) {
- CompleteRemove();
+ tabstrip_->FinishAnimation(this, layout_on_completion_);
+ // This object is destroyed now, so we can't do anything else after this.
}
virtual void AnimationCanceled(const Animation* animation) {
- // We can be canceled for two interesting reasons:
- // . The tab we reference was dragged back into the tab strip. In this case
- // we don't want to remove the tab (closing is false).
- // . The drag was completed before the animation completed
- // (DestroyDraggedSourceTab). In this case we need to remove the tab
- // (closing is true).
- if (tab_->closing())
- CompleteRemove();
+ AnimationEnded(animation);
+ }
+
+ // Returns the gap before the tab at the specified index. Subclass if during
+ // an animation you need to insert a gap before a tab.
+ virtual double GetGapWidth(int index) {
+ return 0;
+ }
+
+ protected:
+ // Returns the duration of the animation.
+ virtual int GetDuration() const {
+ return kDefaultAnimationDurationMs;
+ }
+
+ // Subclasses override to return the width of the Tab at the specified index
+ // at the current animation frame. -1 indicates the default width should be
+ // used for the Tab.
+ virtual double GetWidthForTab(int index) const {
+ return -1; // Use default.
+ }
+
+ // Figure out the desired start and end widths for the specified pre- and
+ // post- animation tab counts.
+ void GenerateStartAndEndWidths(int start_tab_count, int end_tab_count,
+ int start_mini_count,
+ int end_mini_count) {
+ tabstrip_->GetDesiredTabWidths(start_tab_count, start_mini_count,
+ &start_unselected_width_,
+ &start_selected_width_);
+ double standard_tab_width =
+ static_cast<double>(TabRenderer::GetStandardSize().width());
+ if (start_tab_count < end_tab_count &&
+ start_unselected_width_ < standard_tab_width) {
+ double minimum_tab_width =
+ static_cast<double>(TabRenderer::GetMinimumUnselectedSize().width());
+ start_unselected_width_ -= minimum_tab_width / start_tab_count;
+ }
+ tabstrip_->GenerateIdealBounds();
+ tabstrip_->GetDesiredTabWidths(end_tab_count, end_mini_count,
+ &end_unselected_width_,
+ &end_selected_width_);
}
+ TabStrip* tabstrip_;
+ SlideAnimation animation_;
+
+ double start_selected_width_;
+ double start_unselected_width_;
+ double end_selected_width_;
+ double end_unselected_width_;
+
private:
- void CompleteRemove() {
- if (!tab_->closing()) {
- // The tab was added back yet we weren't canceled. This shouldn't happen.
- NOTREACHED();
- return;
+ // True if a complete re-layout is required upon completion of the animation.
+ // Subclasses set this if they don't perform a complete layout
+ // themselves and canceling the animation may leave the strip in an
+ // inconsistent state.
+ bool layout_on_completion_;
+
+ const Type type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabAnimation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Handles insertion of a Tab at |index|.
+class TabStrip::InsertTabAnimation : public TabStrip::TabAnimation {
+ public:
+ explicit InsertTabAnimation(TabStrip* tabstrip, int index)
+ : TabAnimation(tabstrip, INSERT),
+ index_(index) {
+ int tab_count = tabstrip->GetTabCount();
+ int end_mini_count = tabstrip->GetMiniTabCount();
+ int start_mini_count = end_mini_count;
+ if (index < end_mini_count)
+ start_mini_count--;
+ GenerateStartAndEndWidths(tab_count - 1, tab_count, start_mini_count,
+ end_mini_count);
+ }
+ virtual ~InsertTabAnimation() {}
+
+ protected:
+ // Overridden from TabStrip::TabAnimation:
+ virtual double GetWidthForTab(int index) const {
+ if (index == index_) {
+ bool is_selected = tabstrip_->model()->selected_index() == index;
+ double start_width, target_width;
+ if (index < tabstrip_->GetMiniTabCount()) {
+ start_width = Tab::GetMinimumSelectedSize().width();
+ target_width = Tab::GetMiniWidth();
+ } else {
+ target_width =
+ is_selected ? end_unselected_width_ : end_selected_width_;
+ start_width =
+ is_selected ? Tab::GetMinimumSelectedSize().width() :
+ Tab::GetMinimumUnselectedSize().width();
+ }
+ double delta = target_width - start_width;
+ if (delta > 0)
+ return start_width + (delta * animation_.GetCurrentValue());
+ return start_width;
+ }
+
+ if (tabstrip_->GetTabAt(index)->mini())
+ return Tab::GetMiniWidth();
+
+ if (tabstrip_->GetTabAt(index)->IsSelected()) {
+ double delta = end_selected_width_ - start_selected_width_;
+ return start_selected_width_ + (delta * animation_.GetCurrentValue());
+ }
+
+ double delta = end_unselected_width_ - start_unselected_width_;
+ return start_unselected_width_ + (delta * animation_.GetCurrentValue());
+ }
+
+ private:
+ int index_;
+
+ DISALLOW_COPY_AND_ASSIGN(InsertTabAnimation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Handles removal of a Tab from |index|
+class TabStrip::RemoveTabAnimation : public TabStrip::TabAnimation {
+ public:
+ RemoveTabAnimation(TabStrip* tabstrip, int index, TabContents* contents)
+ : TabAnimation(tabstrip, REMOVE),
+ index_(index) {
+ int tab_count = tabstrip->GetTabCount();
+ int start_mini_count = tabstrip->GetMiniTabCount();
+ int end_mini_count = start_mini_count;
+ if (index < start_mini_count)
+ end_mini_count--;
+ GenerateStartAndEndWidths(tab_count, tab_count - 1, start_mini_count,
+ end_mini_count);
+ // If the last non-mini-tab is being removed we force a layout on
+ // completion. This is necessary as the value returned by GetTabHOffset
+ // changes once the tab is actually removed (which happens at the end of
+ // the animation), and unless we layout GetTabHOffset won't be called after
+ // the removal.
+ // We do the same when the last mini-tab is being removed for the same
+ // reason.
+ set_layout_on_completion(start_mini_count > 0 &&
+ (end_mini_count == 0 ||
+ (start_mini_count == end_mini_count &&
+ tab_count == start_mini_count + 1)));
+ }
+
+ // Returns the index of the tab being removed.
+ int index() const { return index_; }
+
+ virtual ~RemoveTabAnimation() {
+ }
+
+ protected:
+ // Overridden from TabStrip::TabAnimation:
+ virtual double GetWidthForTab(int index) const {
+ Tab* tab = tabstrip_->GetTabAt(index);
+ if (index == index_) {
+ // The tab(s) being removed are gradually shrunken depending on the state
+ // of the animation.
+ // Removed animated Tabs are never selected.
+ if (tab->mini()) {
+ return animation_.CurrentValueBetween(Tab::GetMiniWidth(),
+ -kTabHOffset);
+ }
+
+ double start_width = start_unselected_width_;
+ // Make sure target_width is at least abs(kTabHOffset), otherwise if
+ // less than kTabHOffset during layout tabs get negatively offset.
+ double target_width =
+ std::max(abs(kTabHOffset),
+ Tab::GetMinimumUnselectedSize().width() + kTabHOffset);
+ return animation_.CurrentValueBetween(start_width, target_width);
+ }
+
+ if (tab->mini())
+ return Tab::GetMiniWidth();
+
+ if (tabstrip_->available_width_for_tabs_ != -1 &&
+ index_ != tabstrip_->GetTabCount() - 1) {
+ return TabStrip::TabAnimation::GetWidthForTab(index);
+ }
+ // All other tabs are sized according to the start/end widths specified at
+ // the start of the animation.
+ if (tab->IsSelected()) {
+ double delta = end_selected_width_ - start_selected_width_;
+ return start_selected_width_ + (delta * animation_.GetCurrentValue());
}
- tabstrip_->RemoveTab(tab_);
+ double delta = end_unselected_width_ - start_unselected_width_;
+ return start_unselected_width_ + (delta * animation_.GetCurrentValue());
+ }
+
+ virtual void AnimationEnded(const Animation* animation) {
+ tabstrip_->RemoveTabAt(index_);
HighlightCloseButton();
+ TabStrip::TabAnimation::AnimationEnded(animation);
}
+ private:
// When the animation completes, we send the Container a message to simulate
// a mouse moved event at the current mouse position. This tickles the Tab
// the mouse is currently over to show the "hot" state of the close button.
@@ -226,10 +439,293 @@ class TabStrip::RemoveTabDelegate
#endif
}
- TabStrip* tabstrip_;
+ int index_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveTabAnimation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Handles the movement of a Tab from one position to another.
+class TabStrip::MoveTabAnimation : public TabStrip::TabAnimation {
+ public:
+ MoveTabAnimation(TabStrip* tabstrip, int tab_a_index, int tab_b_index)
+ : TabAnimation(tabstrip, MOVE),
+ start_tab_a_bounds_(tabstrip_->GetIdealBounds(tab_b_index)),
+ start_tab_b_bounds_(tabstrip_->GetIdealBounds(tab_a_index)) {
+ tab_a_ = tabstrip_->GetTabAt(tab_a_index);
+ tab_b_ = tabstrip_->GetTabAt(tab_b_index);
+
+ // Since we don't do a full TabStrip re-layout, we need to force a full
+ // layout upon completion since we're not guaranteed to be in a good state
+ // if for example the animation is canceled.
+ set_layout_on_completion(true);
+ }
+ virtual ~MoveTabAnimation() {}
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation) {
+ // Position Tab A
+ double distance = start_tab_b_bounds_.x() - start_tab_a_bounds_.x();
+ double delta = distance * animation_.GetCurrentValue();
+ double new_x = start_tab_a_bounds_.x() + delta;
+ tab_a_->SetBounds(Round(new_x), tab_a_->y(), tab_a_->width(),
+ tab_a_->height());
+
+ // Position Tab B
+ distance = start_tab_a_bounds_.x() - start_tab_b_bounds_.x();
+ delta = distance * animation_.GetCurrentValue();
+ new_x = start_tab_b_bounds_.x() + delta;
+ tab_b_->SetBounds(Round(new_x), tab_b_->y(), tab_b_->width(),
+ tab_b_->height());
+
+ tabstrip_->SchedulePaint();
+ }
+
+ protected:
+ // Overridden from TabStrip::TabAnimation:
+ virtual int GetDuration() const { return kReorderAnimationDurationMs; }
+
+ private:
+ // The two tabs being exchanged.
+ Tab* tab_a_;
+ Tab* tab_b_;
+
+ // ...and their bounds.
+ gfx::Rect start_tab_a_bounds_;
+ gfx::Rect start_tab_b_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(MoveTabAnimation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Handles the animated resize layout of the entire TabStrip from one width
+// to another.
+class TabStrip::ResizeLayoutAnimation : public TabStrip::TabAnimation {
+ public:
+ explicit ResizeLayoutAnimation(TabStrip* tabstrip)
+ : TabAnimation(tabstrip, RESIZE) {
+ int tab_count = tabstrip->GetTabCount();
+ int mini_tab_count = tabstrip->GetMiniTabCount();
+ GenerateStartAndEndWidths(tab_count, tab_count, mini_tab_count,
+ mini_tab_count);
+ InitStartState();
+ }
+ virtual ~ResizeLayoutAnimation() {
+ }
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationEnded(const Animation* animation) {
+ tabstrip_->needs_resize_layout_ = false;
+ TabStrip::TabAnimation::AnimationEnded(animation);
+ }
+
+ protected:
+ // Overridden from TabStrip::TabAnimation:
+ virtual int GetDuration() const {
+ return kResizeLayoutAnimationDurationMs;
+ }
+
+ virtual double GetWidthForTab(int index) const {
+ Tab* tab = tabstrip_->GetTabAt(index);
+ if (tab->mini())
+ return Tab::GetMiniWidth();
+
+ if (tab->IsSelected()) {
+ return animation_.CurrentValueBetween(start_selected_width_,
+ end_selected_width_);
+ }
+
+ return animation_.CurrentValueBetween(start_unselected_width_,
+ end_unselected_width_);
+ }
+
+ private:
+ // We need to start from the current widths of the Tabs as they were last
+ // laid out, _not_ the last known good state, which is what'll be done if we
+ // don't measure the Tab sizes here and just go with the default TabAnimation
+ // behavior...
+ void InitStartState() {
+ for (int i = 0; i < tabstrip_->GetTabCount(); ++i) {
+ Tab* current_tab = tabstrip_->GetTabAt(i);
+ if (!current_tab->mini()) {
+ if (current_tab->IsSelected()) {
+ start_selected_width_ = current_tab->width();
+ } else {
+ start_unselected_width_ = current_tab->width();
+ }
+ }
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ResizeLayoutAnimation);
+};
+
+// Handles a tabs mini-state changing while the tab does not change position
+// in the model.
+class TabStrip::MiniTabAnimation : public TabStrip::TabAnimation {
+ public:
+ explicit MiniTabAnimation(TabStrip* tabstrip, int index)
+ : TabAnimation(tabstrip, MINI),
+ index_(index) {
+ int tab_count = tabstrip->GetTabCount();
+ int start_mini_count = tabstrip->GetMiniTabCount();
+ int end_mini_count = start_mini_count;
+ if (tabstrip->GetTabAt(index)->mini())
+ start_mini_count--;
+ else
+ start_mini_count++;
+ tabstrip_->GetTabAt(index)->set_animating_mini_change(true);
+ GenerateStartAndEndWidths(tab_count, tab_count, start_mini_count,
+ end_mini_count);
+ }
+
+ protected:
+ // Overridden from TabStrip::TabAnimation:
+ virtual int GetDuration() const {
+ return kMiniTabAnimationDurationMs;
+ }
+
+ virtual double GetWidthForTab(int index) const {
+ Tab* tab = tabstrip_->GetTabAt(index);
+
+ if (index == index_) {
+ if (tab->mini()) {
+ return animation_.CurrentValueBetween(
+ start_selected_width_,
+ static_cast<double>(Tab::GetMiniWidth()));
+ } else {
+ return animation_.CurrentValueBetween(
+ static_cast<double>(Tab::GetMiniWidth()),
+ end_selected_width_);
+ }
+ } else if (tab->mini()) {
+ return Tab::GetMiniWidth();
+ }
+
+ if (tab->IsSelected()) {
+ return animation_.CurrentValueBetween(start_selected_width_,
+ end_selected_width_);
+ }
+
+ return animation_.CurrentValueBetween(start_unselected_width_,
+ end_unselected_width_);
+ }
+
+ private:
+ // Index of the tab whose mini state changed.
+ int index_;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniTabAnimation);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Handles the animation when a tabs mini state changes and the tab moves as a
+// result.
+class TabStrip::MiniMoveAnimation : public TabStrip::TabAnimation {
+ public:
+ explicit MiniMoveAnimation(TabStrip* tabstrip,
+ int from_index,
+ int to_index,
+ const gfx::Rect& start_bounds)
+ : TabAnimation(tabstrip, MINI_MOVE),
+ tab_(tabstrip->GetTabAt(to_index)),
+ start_bounds_(start_bounds),
+ from_index_(from_index),
+ to_index_(to_index) {
+ int tab_count = tabstrip->GetTabCount();
+ int start_mini_count = tabstrip->GetMiniTabCount();
+ int end_mini_count = start_mini_count;
+ if (tabstrip->GetTabAt(to_index)->mini())
+ start_mini_count--;
+ else
+ start_mini_count++;
+ GenerateStartAndEndWidths(tab_count, tab_count, start_mini_count,
+ end_mini_count);
+ target_bounds_ = tabstrip->GetIdealBounds(to_index);
+ tab_->set_animating_mini_change(true);
+ }
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation) {
+ // Do the normal layout.
+ TabAnimation::AnimationProgressed(animation);
+
+ // Then special case the position of the tab being moved.
+ int x = animation_.CurrentValueBetween(start_bounds_.x(),
+ target_bounds_.x());
+ int width = animation_.CurrentValueBetween(start_bounds_.width(),
+ target_bounds_.width());
+ gfx::Rect tab_bounds(x, start_bounds_.y(), width,
+ start_bounds_.height());
+ tab_->SetBounds(tab_bounds);
+ }
+
+ virtual void AnimationEnded(const Animation* animation) {
+ tabstrip_->needs_resize_layout_ = false;
+ TabStrip::TabAnimation::AnimationEnded(animation);
+ }
+
+ virtual double GetGapWidth(int index) {
+ if (to_index_ < from_index_) {
+ // The tab was mini.
+ if (index == to_index_) {
+ double current_size =
+ animation_.CurrentValueBetween(0, target_bounds_.width());
+ if (current_size < -kTabHOffset)
+ return -(current_size + kTabHOffset);
+ } else if (index == from_index_ + 1) {
+ return animation_.CurrentValueBetween(start_bounds_.width(), 0);
+ }
+ } else {
+ // The tab was made a normal tab.
+ if (index == from_index_) {
+ return animation_.CurrentValueBetween(Tab::GetMiniWidth() +
+ kTabHOffset, 0);
+ }
+ }
+ return 0;
+ }
+
+ protected:
+ // Overridden from TabStrip::TabAnimation:
+ virtual int GetDuration() const { return kReorderAnimationDurationMs; }
+
+ virtual double GetWidthForTab(int index) const {
+ Tab* tab = tabstrip_->GetTabAt(index);
+
+ if (index == to_index_)
+ return animation_.CurrentValueBetween(0, target_bounds_.width());
+
+ if (tab->mini())
+ return Tab::GetMiniWidth();
+
+ if (tab->IsSelected()) {
+ return animation_.CurrentValueBetween(start_selected_width_,
+ end_selected_width_);
+ }
+
+ return animation_.CurrentValueBetween(start_unselected_width_,
+ end_unselected_width_);
+ }
+
+ private:
+ // The tab being moved.
Tab* tab_;
- DISALLOW_COPY_AND_ASSIGN(RemoveTabDelegate);
+ // Initial bounds of tab_.
+ gfx::Rect start_bounds_;
+
+ // Target bounds.
+ gfx::Rect target_bounds_;
+
+ // Start and end indices of the tab.
+ int from_index_;
+ int to_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniMoveAnimation);
};
///////////////////////////////////////////////////////////////////////////////
@@ -245,14 +741,13 @@ TabStrip::TabStrip(TabStripModel* model)
needs_resize_layout_(false),
current_unselected_width_(Tab::GetStandardSize().width()),
current_selected_width_(Tab::GetStandardSize().width()),
- available_width_for_tabs_(-1),
- animation_container_(new AnimationContainer()),
- ALLOW_THIS_IN_INITIALIZER_LIST(bounds_animator_(this)),
- animation_type_(ANIMATION_DEFAULT) {
+ available_width_for_tabs_(-1) {
Init();
}
TabStrip::~TabStrip() {
+ active_animation_.reset(NULL);
+
// TODO(beng): (1031854) Restore this line once XPFrame/VistaFrame are dead.
// model_->RemoveObserver(this);
@@ -280,34 +775,36 @@ void TabStrip::DestroyDragController() {
void TabStrip::DestroyDraggedSourceTab(Tab* tab) {
// We could be running an animation that references this Tab.
- StopAnimating(true);
-
+ if (active_animation_.get())
+ active_animation_->Stop();
// Make sure we leave the tab_data_ vector in a consistent state, otherwise
// we'll be pointing to tabs that have been deleted and removed from the
// child view list.
- int tab_data_index = TabDataIndexOfTab(tab);
- if (tab_data_index != -1) {
- if (!model_->closing_all())
- NOTREACHED() << "Leaving in an inconsistent state!";
- tab_data_.erase(tab_data_.begin() + tab_data_index);
+ std::vector<TabData>::iterator it = tab_data_.begin();
+ for (; it != tab_data_.end(); ++it) {
+ if (it->tab == tab) {
+ if (!model_->closing_all())
+ NOTREACHED() << "Leaving in an inconsistent state!";
+ tab_data_.erase(it);
+ break;
+ }
}
-
+ tab->GetParent()->RemoveChildView(tab);
delete tab;
-
// Force a layout here, because if we've just quickly drag detached a Tab,
// the stopping of the active animation above may have left the TabStrip in a
// bad (visual) state.
Layout();
}
-gfx::Rect TabStrip::GetIdealBounds(int tab_data_index) {
- DCHECK_GE(tab_data_index, 0);
- DCHECK_LT(tab_data_index, GetTabCount());
- return tab_data_[tab_data_index].ideal_bounds;
+gfx::Rect TabStrip::GetIdealBounds(int index) {
+ DCHECK_GE(index, 0);
+ DCHECK_LT(index, GetTabCount());
+ return tab_data_.at(index).ideal_bounds;
}
Tab* TabStrip::GetSelectedTab() const {
- return GetTabAtModelIndex(model()->selected_index());
+ return GetTabAtAdjustForAnimation(model()->selected_index());
}
void TabStrip::InitTabStripButtons() {
@@ -339,7 +836,7 @@ int TabStrip::GetPreferredHeight() {
void TabStrip::SetBackgroundOffset(const gfx::Point& offset) {
int tab_count = GetTabCount();
for (int i = 0; i < tab_count; ++i)
- GetTabAtTabDataIndex(i)->SetBackgroundOffset(offset);
+ GetTabAt(i)->SetBackgroundOffset(offset);
}
bool TabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
@@ -374,10 +871,12 @@ bool TabStrip::IsDragSessionActive() const {
}
void TabStrip::UpdateLoadingAnimations() {
- for (int i = 0, model_index = 0; i < GetTabCount(); ++i) {
- Tab* current_tab = GetTabAtTabDataIndex(i);
- if (!current_tab->closing()) {
- TabContents* contents = model_->GetTabContentsAt(model_index);
+ for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
+ Tab* current_tab = GetTabAt(i);
+ if (current_tab->closing()) {
+ --index;
+ } else {
+ TabContents* contents = model_->GetTabContentsAt(index);
if (!contents || !contents->is_loading()) {
current_tab->ValidateLoadingAnimation(Tab::ANIMATION_NONE);
} else if (contents->waiting_for_response()) {
@@ -385,13 +884,12 @@ void TabStrip::UpdateLoadingAnimations() {
} else {
current_tab->ValidateLoadingAnimation(Tab::ANIMATION_LOADING);
}
- model_index++;
}
}
}
bool TabStrip::IsAnimating() const {
- return bounds_animator_.IsAnimating() || new_tab_timer_.IsRunning();
+ return active_animation_.get() != NULL;
}
TabStrip* TabStrip::AsTabStrip() {
@@ -403,7 +901,6 @@ TabStrip* TabStrip::AsTabStrip() {
void TabStrip::PaintChildren(gfx::Canvas* canvas) {
// Tabs are painted in reverse order, so they stack to the left.
- int tab_count = GetTabCount();
// Phantom tabs appear behind all other tabs and are rendered first. To make
// them slightly transparent we render them to a different layer.
@@ -413,8 +910,8 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) {
canvas->saveLayerAlpha(&bounds, kPhantomTabAlpha,
SkCanvas::kARGB_ClipLayer_SaveFlag);
canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
- for (int i = tab_count - 1; i >= 0; --i) {
- Tab* tab = GetTabAtTabDataIndex(i);
+ for (int i = GetTabCount() - 1; i >= 0; --i) {
+ Tab* tab = GetTabAt(i);
if (tab->phantom())
tab->ProcessPaint(canvas);
}
@@ -423,8 +920,8 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) {
canvas->saveLayerAlpha(&bounds, kPhantomTabIconAlpha,
SkCanvas::kARGB_ClipLayer_SaveFlag);
canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
- for (int i = tab_count - 1; i >= 0; --i) {
- Tab* tab = GetTabAtTabDataIndex(i);
+ for (int i = GetTabCount() - 1; i >= 0; --i) {
+ Tab* tab = GetTabAt(i);
if (tab->phantom()) {
canvas->save();
canvas->ClipRectInt(tab->MirroredX(), tab->y(), tab->width(),
@@ -439,27 +936,14 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) {
Tab* selected_tab = NULL;
- for (int i = tab_count - 1; i >= 0; --i) {
- Tab* tab = GetTabAtTabDataIndex(i);
+ for (int i = GetTabCount() - 1; i >= 0; --i) {
+ Tab* tab = GetTabAt(i);
// We must ask the _Tab's_ model, not ourselves, because in some situations
// the model will be different to this object, e.g. when a Tab is being
// removed after its TabContents has been destroyed.
if (!tab->phantom()) {
if (!tab->IsSelected()) {
- if (tab->render_unselected() && model_->count() > 1) {
- // See comment above kNetTabAnimationSelectedOffset as to why we do
- // this.
- Tab* last_tab = GetTabAtModelIndex(model_->count() - 2);
- canvas->save();
- int clip_x = last_tab->bounds().right() + kNetTabSelectedOffset;
- int clip_width = width() - clip_x;
- clip_x = MirroredXWithWidthInsideView(clip_x, clip_width);
- canvas->ClipRectInt(clip_x, 0, clip_width, height());
- tab->ProcessPaint(canvas);
- canvas->restore();
- } else {
- tab->ProcessPaint(canvas);
- }
+ tab->ProcessPaint(canvas);
} else {
selected_tab = tab;
}
@@ -478,30 +962,23 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) {
paint);
}
- if (animation_type_ == ANIMATION_NEW_TAB_3)
- newtab_button_->ProcessPaint(canvas);
-
// Paint the selected tab last, so it overlaps all the others.
if (selected_tab)
selected_tab->ProcessPaint(canvas);
// Paint the New Tab button.
- if (animation_type_ != ANIMATION_NEW_TAB_1 &&
- animation_type_ != ANIMATION_NEW_TAB_2 &&
- animation_type_ != ANIMATION_NEW_TAB_3) {
- newtab_button_->ProcessPaint(canvas);
- }
+ newtab_button_->ProcessPaint(canvas);
}
// Overridden to support automation. See automation_proxy_uitest.cc.
views::View* TabStrip::GetViewByID(int view_id) const {
if (GetTabCount() > 0) {
if (view_id == VIEW_ID_TAB_LAST) {
- return GetTabAtTabDataIndex(GetTabCount() - 1);
+ return GetTabAt(GetTabCount() - 1);
} else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
int index = view_id - VIEW_ID_TAB_0;
if (index >= 0 && index < GetTabCount()) {
- return GetTabAtTabDataIndex(index);
+ return GetTabAt(index);
} else {
return NULL;
}
@@ -514,15 +991,22 @@ views::View* TabStrip::GetViewByID(int view_id) const {
void TabStrip::Layout() {
// Called from:
// - window resize
- StopAnimating(false);
-
+ // - animation completion
+ if (active_animation_.get())
+ active_animation_->Stop();
GenerateIdealBounds();
+ int tab_count = GetTabCount();
+ int tab_right = 0;
- for (int i = 0, tab_count = GetTabCount(); i < tab_count; ++i)
- tab_data_[i].tab->SetBounds(tab_data_[i].ideal_bounds);
-
- newtab_button_->SetBounds(newtab_button_bounds_);
-
+ for (int i = 0; i < tab_count; ++i) {
+ const gfx::Rect& bounds = tab_data_.at(i).ideal_bounds;
+ Tab* tab = GetTabAt(i);
+ tab->set_animating_mini_change(false);
+ tab->SetBounds(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ tab_right = bounds.right();
+ tab_right += GetTabHOffset(i + 1);
+ }
+ LayoutNewTabButton(static_cast<double>(tab_right), current_unselected_width_);
SchedulePaint();
}
@@ -531,9 +1015,6 @@ gfx::Size TabStrip::GetPreferredSize() {
}
void TabStrip::OnDragEntered(const DropTargetEvent& event) {
- // Force animations to stop, otherwise it makes the index calculation tricky.
- StopAnimating(true);
-
UpdateDropIndex(event);
}
@@ -604,10 +1085,10 @@ views::View* TabStrip::GetViewForPoint(const gfx::Point& point) {
// left-adjacent Tab, so we look ahead for it as we walk.
int tab_count = GetTabCount();
for (int i = 0; i < tab_count; ++i) {
- Tab* next_tab = i < (tab_count - 1) ? GetTabAtTabDataIndex(i + 1) : NULL;
+ Tab* next_tab = i < (tab_count - 1) ? GetTabAt(i + 1) : NULL;
if (next_tab && next_tab->IsSelected() && IsPointInTab(next_tab, point))
return next_tab;
- Tab* tab = GetTabAtTabDataIndex(i);
+ Tab* tab = GetTabAt(i);
if (IsPointInTab(tab, point))
return tab;
}
@@ -636,15 +1117,16 @@ void TabStrip::ViewHierarchyChanged(bool is_add,
// TabStrip, TabStripModelObserver implementation:
void TabStrip::TabInsertedAt(TabContents* contents,
- int model_index,
+ int index,
bool foreground) {
DCHECK(contents);
- DCHECK(model_index == TabStripModel::kNoTab ||
- model_->ContainsIndex(model_index));
+ DCHECK(index == TabStripModel::kNoTab || model_->ContainsIndex(index));
// This tab may be attached to another browser window, we should notify
// renderer.
contents->render_view_host()->UpdateBrowserWindowId(
contents->controller().window_id().id());
+ if (active_animation_.get())
+ active_animation_->Stop();
bool contains_tab = false;
Tab* tab = NULL;
@@ -663,23 +1145,10 @@ void TabStrip::TabInsertedAt(TabContents* contents,
}
// See if we're already in the list. We don't want to add ourselves twice.
- int tab_data_index = TabDataIndexOfTab(tab);
-
- if (tab_data_index != -1) {
- contains_tab = true;
-
- // Make sure we stop animating the view. This is necessary otherwise when
- // the animation is done it'll try to remove the tab.
- bounds_animator_.StopAnimatingView(tab);
-
- // We have the tab, but it might not be at the right index. Reset the data
- // to ensure it's at the right index.
- TabData tab_data = tab_data_[tab_data_index];
- DCHECK(tab_data.tab == tab);
- tab_data_.erase(tab_data_.begin() + tab_data_index);
- tab_data_.insert(
- tab_data_.begin() + ModelIndexToTabDataIndex(model_index),
- tab_data);
+ std::vector<TabData>::const_iterator iter = tab_data_.begin();
+ for (; iter != tab_data_.end() && !contains_tab; ++iter) {
+ if (iter->tab == tab)
+ contains_tab = true;
}
}
@@ -690,43 +1159,40 @@ void TabStrip::TabInsertedAt(TabContents* contents,
// Only insert if we're not already in the list.
if (!contains_tab) {
TabData d = { tab, gfx::Rect() };
- tab_data_.insert(tab_data_.begin() +
- ModelIndexToTabDataIndex(model_index), d);
- tab->UpdateData(contents, model_->IsPhantomTab(model_index), false);
+ tab_data_.insert(tab_data_.begin() + index, d);
+ tab->UpdateData(contents, model_->IsPhantomTab(index), false);
}
- tab->set_mini(model_->IsMiniTab(model_index));
- tab->set_app(model_->IsAppTab(model_index));
- tab->SetBlocked(model_->IsTabBlocked(model_index));
+ tab->set_mini(model_->IsMiniTab(index));
+ tab->SetBlocked(model_->IsTabBlocked(index));
// We only add the tab to the child list if it's not already - an invisible
// tab maintained by the DraggedTabController will already be parented.
- if (!tab->GetParent()) {
+ if (!tab->GetParent())
AddChildView(tab);
- tab->SetAnimationContainer(animation_container_.get());
- }
// Don't animate the first tab, it looks weird, and don't animate anything
// if the containing window isn't visible yet.
if (GetTabCount() > 1 && GetWindow() && GetWindow()->IsVisible()) {
- if (!IsDragSessionActive() &&
- ShouldStartIntertTabAnimationAtEnd(model_index, foreground)) {
- StartInsertTabAnimationAtEnd();
- } else {
- StartInsertTabAnimation(model_index);
- }
+ StartInsertTabAnimation(index);
} else {
Layout();
}
}
-void TabStrip::TabDetachedAt(TabContents* contents, int model_index) {
- StartRemoveTabAnimation(model_index);
+void TabStrip::TabDetachedAt(TabContents* contents, int index) {
+ GenerateIdealBounds();
+ StartRemoveTabAnimation(index, contents);
+ // Have to do this _after_ calling StartRemoveTabAnimation, so that any
+ // previous remove is completed fully and index is valid in sync with the
+ // model index.
+ GetTabAt(index)->set_closing(true);
}
void TabStrip::TabSelectedAt(TabContents* old_contents,
TabContents* new_contents,
- int model_index,
+ int index,
bool user_gesture) {
+ DCHECK(index >= 0 && index < GetTabCount());
// We have "tiny tabs" if the tabs are so tiny that the unselected ones are
// a different size to the selected ones.
bool tiny_tabs = current_unselected_width_ != current_selected_width_;
@@ -736,133 +1202,117 @@ void TabStrip::TabSelectedAt(TabContents* old_contents,
SchedulePaint();
}
- int old_model_index = model_->GetIndexOfTabContents(old_contents);
- if (old_model_index >= 0) {
- GetTabAtTabDataIndex(ModelIndexToTabDataIndex(old_model_index))->
- StopMiniTabTitleAnimation();
- }
+ int old_index = model_->GetIndexOfTabContents(old_contents);
+ if (old_index >= 0)
+ GetTabAt(old_index)->StopMiniTabTitleAnimation();
}
-void TabStrip::TabMoved(TabContents* contents,
- int from_model_index,
- int to_model_index) {
- StartMoveTabAnimation(from_model_index, to_model_index);
+void TabStrip::TabMoved(TabContents* contents, int from_index, int to_index) {
+ gfx::Rect start_bounds = GetIdealBounds(from_index);
+ Tab* tab = GetTabAt(from_index);
+ tab_data_.erase(tab_data_.begin() + from_index);
+ TabData data = {tab, gfx::Rect()};
+ tab->set_mini(model_->IsMiniTab(to_index));
+ tab->SetBlocked(model_->IsTabBlocked(to_index));
+ tab_data_.insert(tab_data_.begin() + to_index, data);
+ if (tab->phantom() != model_->IsPhantomTab(to_index))
+ tab->set_phantom(!tab->phantom());
+ GenerateIdealBounds();
+ StartMoveTabAnimation(from_index, to_index);
}
-void TabStrip::TabChangedAt(TabContents* contents,
- int model_index,
+void TabStrip::TabChangedAt(TabContents* contents, int index,
TabChangeType change_type) {
// Index is in terms of the model. Need to make sure we adjust that index in
// case we have an animation going.
- Tab* tab = GetTabAtModelIndex(model_index);
+ Tab* tab = GetTabAtAdjustForAnimation(index);
if (change_type == TITLE_NOT_LOADING) {
if (tab->mini() && !tab->IsSelected())
tab->StartMiniTabTitleAnimation();
// We'll receive another notification of the change asynchronously.
return;
}
- tab->UpdateData(contents, model_->IsPhantomTab(model_index),
+ tab->UpdateData(contents, model_->IsPhantomTab(index),
change_type == LOADING_ONLY);
tab->UpdateFromModel();
}
void TabStrip::TabReplacedAt(TabContents* old_contents,
TabContents* new_contents,
- int model_index) {
- TabChangedAt(new_contents, model_index, ALL);
+ int index) {
+ TabChangedAt(new_contents, index, ALL);
}
-void TabStrip::TabMiniStateChanged(TabContents* contents, int model_index) {
- GetTabAtModelIndex(model_index)->set_mini(
- model_->IsMiniTab(model_index));
+void TabStrip::TabMiniStateChanged(TabContents* contents, int index) {
+ GetTabAt(index)->set_mini(model_->IsMiniTab(index));
// Don't animate if the window isn't visible yet. The window won't be visible
// when dragging a mini-tab to a new window.
if (GetWindow() && GetWindow()->IsVisible())
- StartMiniTabAnimation();
+ StartMiniTabAnimation(index);
else
Layout();
}
-void TabStrip::TabBlockedStateChanged(TabContents* contents, int model_index) {
- GetTabAtModelIndex(model_index)->SetBlocked(
- model_->IsTabBlocked(model_index));
+void TabStrip::TabBlockedStateChanged(TabContents* contents, int index) {
+ GetTabAt(index)->SetBlocked(model_->IsTabBlocked(index));
}
///////////////////////////////////////////////////////////////////////////////
// TabStrip, Tab::Delegate implementation:
bool TabStrip::IsTabSelected(const Tab* tab) const {
- if (tab->closing() || tab->render_unselected())
+ if (tab->closing())
return false;
- return GetModelIndexOfTab(tab) == model_->selected_index();
+ return GetIndexOfTab(tab) == model_->selected_index();
}
bool TabStrip::IsTabPinned(const Tab* tab) const {
if (tab->closing())
return false;
- return model_->IsTabPinned(GetModelIndexOfTab(tab));
+ return model_->IsTabPinned(GetIndexOfTab(tab));
}
void TabStrip::SelectTab(Tab* tab) {
- int model_index = GetModelIndexOfTab(tab);
- if (model_->ContainsIndex(model_index))
- model_->SelectTabContentsAt(model_index, true);
+ int index = GetIndexOfTab(tab);
+ if (model_->ContainsIndex(index))
+ model_->SelectTabContentsAt(index, true);
}
void TabStrip::CloseTab(Tab* tab) {
- int model_index = GetModelIndexOfTab(tab);
- if (model_->ContainsIndex(model_index)) {
- TabContents* contents = model_->GetTabContentsAt(model_index);
+ int tab_index = GetIndexOfTab(tab);
+ if (model_->ContainsIndex(tab_index)) {
+ TabContents* contents = model_->GetTabContentsAt(tab_index);
if (contents)
UserMetrics::RecordAction(UserMetricsAction("CloseTab_Mouse"),
contents->profile());
- if (model_index + 1 != model_->count() && model_->count() > 1) {
- Tab* last_tab = GetTabAtModelIndex(model_->count() - 2);
- // Limit the width available to the TabStrip for laying out Tabs, so that
- // Tabs are not resized until a later time (when the mouse pointer leaves
- // the TabStrip).
- available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab);
- needs_resize_layout_ = true;
- AddMessageLoopObserver();
- } else if (model_->count() > 1) {
- Tab* last_tab = GetTabAtModelIndex(model_->count() - 1);
- // Limit the width available to the TabStrip for laying out Tabs, so that
- // Tabs are not resized until a later time (when the mouse pointer leaves
- // the TabStrip).
- available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab);
- needs_resize_layout_ = true;
- AddMessageLoopObserver();
- }
+ Tab* last_tab = GetTabAt(GetTabCount() - 1);
+ // Limit the width available to the TabStrip for laying out Tabs, so that
+ // Tabs are not resized until a later time (when the mouse pointer leaves
+ // the TabStrip).
+ available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab);
+ needs_resize_layout_ = true;
+ AddMessageLoopObserver();
// Note that the next call might not close the tab (because of unload
// hanlders or if the delegate veto the close).
- model_->CloseTabContentsAt(model_index);
+ model_->CloseTabContentsAt(tab_index);
}
}
bool TabStrip::IsCommandEnabledForTab(
TabStripModel::ContextMenuCommand command_id, const Tab* tab) const {
- int model_index = GetModelIndexOfTab(tab);
- if (model_->ContainsIndex(model_index))
- return model_->IsContextMenuCommandEnabled(model_index, command_id);
+ int index = GetIndexOfTab(tab);
+ if (model_->ContainsIndex(index))
+ return model_->IsContextMenuCommandEnabled(index, command_id);
return false;
}
void TabStrip::ExecuteCommandForTab(
TabStripModel::ContextMenuCommand command_id, Tab* tab) {
- int model_index = GetModelIndexOfTab(tab);
- if (model_->ContainsIndex(model_index))
- model_->ExecuteContextMenuCommand(model_index, command_id);
-}
-
-void TabStrip::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
- AnimationType last_type = animation_type_;
-
- ResetAnimationState(false);
-
- if (last_type == ANIMATION_NEW_TAB_2)
- NewTabAnimation2Done();
+ int index = GetIndexOfTab(tab);
+ if (model_->ContainsIndex(index))
+ model_->ExecuteContextMenuCommand(index, command_id);
}
void TabStrip::StartHighlightTabsForCommand(
@@ -870,13 +1320,13 @@ void TabStrip::StartHighlightTabsForCommand(
if (command_id == TabStripModel::CommandCloseTabsOpenedBy ||
command_id == TabStripModel::CommandCloseOtherTabs ||
command_id == TabStripModel::CommandCloseTabsToRight) {
- int model_index = GetModelIndexOfTab(tab);
- if (model_->ContainsIndex(model_index)) {
+ int index = GetIndexOfTab(tab);
+ if (model_->ContainsIndex(index)) {
std::vector<int> indices =
- model_->GetIndicesClosedByCommand(model_index, command_id);
+ model_->GetIndicesClosedByCommand(index, command_id);
for (std::vector<int>::const_iterator i = indices.begin();
i != indices.end(); ++i) {
- GetTabAtModelIndex(*i)->StartPulse();
+ GetTabAtAdjustForAnimation(*i)->StartPulse();
}
}
}
@@ -894,7 +1344,7 @@ void TabStrip::StopHighlightTabsForCommand(
void TabStrip::StopAllHighlighting() {
for (int i = 0; i < GetTabCount(); ++i)
- GetTabAtTabDataIndex(i)->StopPulse();
+ GetTabAt(i)->StopPulse();
}
void TabStrip::MaybeStartDrag(Tab* tab, const views::MouseEvent& event) {
@@ -904,8 +1354,8 @@ void TabStrip::MaybeStartDrag(Tab* tab, const views::MouseEvent& event) {
// the user is dragging.
if (IsAnimating() || tab->closing() || !HasAvailableDragActions())
return;
- int model_index = GetModelIndexOfTab(tab);
- if (!model_->ContainsIndex(model_index)) {
+ int index = GetIndexOfTab(tab);
+ if (!model_->ContainsIndex(index)) {
CHECK(false);
return;
}
@@ -1013,18 +1463,15 @@ void TabStrip::DidProcessEvent(GdkEvent* event) {
void TabStrip::Init() {
SetID(VIEW_ID_TAB_STRIP);
model_->AddObserver(this);
- newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight);
- if (browser_defaults::kSizeTabButtonToTopOfTabStrip) {
- newtab_button_bounds_.set_height(
- kNewTabButtonHeight + kNewTabButtonVOffset);
- }
+ newtab_button_size_.SetSize(kNewTabButtonWidth, kNewTabButtonHeight);
+ if (browser_defaults::kSizeTabButtonToTopOfTabStrip)
+ newtab_button_size_.set_height(kNewTabButtonHeight + kNewTabButtonVOffset);
if (drop_indicator_width == 0) {
// Direction doesn't matter, both images are the same size.
SkBitmap* drop_image = GetDropArrowImage(true);
drop_indicator_width = drop_image->width();
drop_indicator_height = drop_image->height();
}
- bounds_animator_.set_observer(this);
}
void TabStrip::LoadNewTabButtonImage() {
@@ -1054,14 +1501,20 @@ void TabStrip::LoadNewTabButtonImage() {
delete tp;
}
-Tab* TabStrip::GetTabAtTabDataIndex(int tab_data_index) const {
- DCHECK_GE(tab_data_index, 0);
- DCHECK_LT(tab_data_index, GetTabCount());
- return tab_data_[tab_data_index].tab;
+Tab* TabStrip::GetTabAt(int index) const {
+ DCHECK_GE(index, 0);
+ DCHECK_LT(index, GetTabCount());
+ return tab_data_.at(index).tab;
}
-Tab* TabStrip::GetTabAtModelIndex(int model_index) const {
- return GetTabAtTabDataIndex(ModelIndexToTabDataIndex(model_index));
+Tab* TabStrip::GetTabAtAdjustForAnimation(int index) const {
+ if (active_animation_.get() &&
+ active_animation_->type() == TabAnimation::REMOVE &&
+ index >=
+ static_cast<RemoveTabAnimation*>(active_animation_.get())->index()) {
+ index++;
+ }
+ return GetTabAt(index);
}
int TabStrip::GetTabCount() const {
@@ -1094,7 +1547,7 @@ void TabStrip::GetDesiredTabWidths(int tab_count,
int available_width;
if (available_width_for_tabs_ < 0) {
available_width = width();
- available_width -= (kNewTabButtonHOffset + newtab_button_bounds_.width());
+ available_width -= (kNewTabButtonHOffset + newtab_button_size_.width());
} else {
// Interesting corner case: if |available_width_for_tabs_| > the result
// of the calculation in the conditional arm above, the strip is in
@@ -1154,6 +1607,14 @@ void TabStrip::GetDesiredTabWidths(int tab_count,
}
}
+int TabStrip::GetTabHOffset(int tab_index) {
+ if (tab_index < GetTabCount() && GetTabAt(tab_index - 1)->mini() &&
+ !GetTabAt(tab_index)->mini()) {
+ return mini_to_non_mini_gap_ + kTabHOffset;
+ }
+ return kTabHOffset;
+}
+
void TabStrip::ResizeLayoutTabs() {
// We've been called back after the TabStrip has been emptied out (probably
// just prior to the window being destroyed). We need to do nothing here or
@@ -1174,7 +1635,7 @@ void TabStrip::ResizeLayoutTabs() {
// mini-tabs have the same width), so there is nothing to do.
return;
}
- Tab* first_tab = GetTabAtTabDataIndex(mini_tab_count);
+ Tab* first_tab = GetTabAt(mini_tab_count);
double unselected, selected;
GetDesiredTabWidths(GetTabCount(), mini_tab_count, &unselected, &selected);
int w = Round(first_tab->IsSelected() ? selected : selected);
@@ -1196,7 +1657,7 @@ bool TabStrip::IsCursorInTabStripZone() const {
DWORD pos = GetMessagePos();
gfx::Point cursor_point(pos);
#elif defined(OS_LINUX)
- // TODO(sky): make sure this is right with multiple monitors.
+ // TODO: make sure this is right with multiple monitors.
GdkScreen* screen = gdk_screen_get_default();
GdkDisplay* display = gdk_screen_get_display(screen);
gint x, y;
@@ -1227,13 +1688,14 @@ gfx::Rect TabStrip::GetDropBounds(int drop_index,
DCHECK(drop_index != -1);
int center_x;
if (drop_index < GetTabCount()) {
- Tab* tab = GetTabAtTabDataIndex(drop_index);
+ Tab* tab = GetTabAt(drop_index);
+ // TODO(sky): update these for mini-tabs.
if (drop_before)
center_x = tab->x() - (kTabHOffset / 2);
else
center_x = tab->x() + (tab->width() / 2);
} else {
- Tab* last_tab = GetTabAtTabDataIndex(drop_index - 1);
+ Tab* last_tab = GetTabAt(drop_index - 1);
center_x = last_tab->x() + last_tab->width() + (kTabHOffset / 2);
}
@@ -1269,7 +1731,7 @@ void TabStrip::UpdateDropIndex(const DropTargetEvent& event) {
const int x = MirroredXCoordinateInsideView(event.x());
// We don't allow replacing the urls of mini-tabs.
for (int i = GetMiniTabCount(); i < GetTabCount(); ++i) {
- Tab* tab = GetTabAtTabDataIndex(i);
+ Tab* tab = GetTabAt(i);
const int tab_max_x = tab->x() + tab->width();
const int hot_width = tab->width() / 3;
if (x < tab_max_x) {
@@ -1287,26 +1749,25 @@ void TabStrip::UpdateDropIndex(const DropTargetEvent& event) {
SetDropIndex(GetTabCount(), true);
}
-void TabStrip::SetDropIndex(int tab_data_index, bool drop_before) {
- if (tab_data_index == -1) {
+void TabStrip::SetDropIndex(int index, bool drop_before) {
+ if (index == -1) {
if (drop_info_.get())
drop_info_.reset(NULL);
return;
}
- if (drop_info_.get() && drop_info_->drop_index == tab_data_index &&
+ if (drop_info_.get() && drop_info_->drop_index == index &&
drop_info_->drop_before == drop_before) {
return;
}
bool is_beneath;
- gfx::Rect drop_bounds = GetDropBounds(tab_data_index, drop_before,
- &is_beneath);
+ gfx::Rect drop_bounds = GetDropBounds(index, drop_before, &is_beneath);
if (!drop_info_.get()) {
- drop_info_.reset(new DropInfo(tab_data_index, drop_before, !is_beneath));
+ drop_info_.reset(new DropInfo(index, drop_before, !is_beneath));
} else {
- drop_info_->drop_index = tab_data_index;
+ drop_info_->drop_index = index;
drop_info_->drop_before = drop_before;
if (is_beneath == drop_info_->point_down) {
drop_info_->point_down = !is_beneath;
@@ -1383,19 +1844,8 @@ TabStrip::DropInfo::~DropInfo() {
// - Tab reorder
void TabStrip::GenerateIdealBounds() {
int tab_count = GetTabCount();
- int non_closing_tab_count = 0;
- int mini_tab_count = 0;
- for (int i = 0; i < tab_count; ++i) {
- if (!tab_data_[i].tab->closing()) {
- ++non_closing_tab_count;
- if (tab_data_[i].tab->mini())
- mini_tab_count++;
- }
- }
-
double unselected, selected;
- GetDesiredTabWidths(non_closing_tab_count, mini_tab_count, &unselected,
- &selected);
+ GetDesiredTabWidths(tab_count, GetMiniTabCount(), &unselected, &selected);
current_unselected_width_ = unselected;
current_selected_width_ = selected;
@@ -1404,259 +1854,139 @@ void TabStrip::GenerateIdealBounds() {
// selected state or the number of tabs in the strip!
int tab_height = Tab::GetStandardSize().height();
double tab_x = 0;
- bool last_was_mini = false;
for (int i = 0; i < tab_count; ++i) {
- if (!tab_data_[i].tab->closing()) {
- Tab* tab = GetTabAtTabDataIndex(i);
- double tab_width = unselected;
- if (tab->mini()) {
- tab_width = Tab::GetMiniWidth();
- } else {
- if (last_was_mini) {
- // Give a bigger gap between mini and non-mini tabs.
- tab_x += mini_to_non_mini_gap_;
- }
- if (tab->IsSelected())
- tab_width = selected;
- }
- double end_of_tab = tab_x + tab_width;
- int rounded_tab_x = Round(tab_x);
- tab_data_[i].ideal_bounds =
- gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
+ Tab* tab = GetTabAt(i);
+ double tab_width = unselected;
+ if (tab->mini())
+ tab_width = Tab::GetMiniWidth();
+ else if (tab->IsSelected())
+ tab_width = selected;
+ double end_of_tab = tab_x + tab_width;
+ int rounded_tab_x = Round(tab_x);
+ gfx::Rect state(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
tab_height);
- tab_x = end_of_tab + kTabHOffset;
- last_was_mini = tab->mini();
- }
+ tab_data_.at(i).ideal_bounds = state;
+ tab_x = end_of_tab + GetTabHOffset(i + 1);
}
+}
- // Update bounds of new tab button.
- int new_tab_x;
- int new_tab_y = browser_defaults::kSizeTabButtonToTopOfTabStrip ?
+void TabStrip::LayoutNewTabButton(double last_tab_right,
+ double unselected_width) {
+ int delta = abs(Round(unselected_width) - Tab::GetStandardSize().width());
+ int v_offset = browser_defaults::kSizeTabButtonToTopOfTabStrip ?
0 : kNewTabButtonVOffset;
- if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 &&
- available_width_for_tabs_ == -1) {
+ if (delta > 1 && !needs_resize_layout_) {
// We're shrinking tabs, so we need to anchor the New Tab button to the
// right edge of the TabStrip's bounds, rather than the right edge of the
// right-most Tab, otherwise it'll bounce when animating.
- new_tab_x = width() - newtab_button_bounds_.width();
+ newtab_button_->SetBounds(width() - newtab_button_size_.width(),
+ v_offset,
+ newtab_button_size_.width(),
+ newtab_button_size_.height());
} else {
- new_tab_x = Round(tab_x - kTabHOffset) + kNewTabButtonHOffset;
- }
- newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
-}
-
-void TabStrip::NewTabAnimation1Done() {
- int tab_data_index = static_cast<int>(tab_data_.size() - 1);
- Tab* tab = GetTabAtTabDataIndex(tab_data_index);
-
- gfx::Rect old_tab_bounds = tab->bounds();
-
- GenerateIdealBounds();
-
- gfx::Rect& end_bounds = tab_data_[tab_data_index].ideal_bounds;
- end_bounds.Offset(kNewTabOvershoot, 0);
-
- int x = old_tab_bounds.right() - end_bounds.width();
- int w = end_bounds.width();
- if (x < 0) {
- w += x;
- x = 0;
- }
- tab->SetBounds(x, old_tab_bounds.y(), w, end_bounds.height());
-
- AnimateToIdealBounds();
-
- animation_type_ = ANIMATION_NEW_TAB_2;
- tab->set_render_as_new_tab(false);
- tab->set_render_unselected(true);
- tab->set_alpha(0);
-
- // BoundsAnimator takes ownership of NewTabAlphaDelegate.
- bounds_animator_.SetAnimationDelegate(tab, new NewTabAlphaDelegate(tab),
- true);
-}
-
-void TabStrip::NewTabAnimation2Done() {
- animation_type_ = ANIMATION_NEW_TAB_3;
-
- GenerateIdealBounds();
-
- AnimateToIdealBounds();
-
- SlideAnimation* animation = new SlideAnimation(NULL);
- animation->SetSlideDuration(kNewTab3DurationMs);
- animation->SetTweenType(SlideAnimation::EASE_IN_OUT);
-
- // BoundsAnimator takes ownership of animation.
- bounds_animator_.SetAnimationForView(tab_data_.back().tab, animation);
-}
-
-void TabStrip::AnimateToIdealBounds() {
- for (size_t i = 0; i < tab_data_.size(); ++i) {
- if (!tab_data_[i].tab->closing()) {
- bounds_animator_.AnimateViewTo(tab_data_[i].tab,
- tab_data_[i].ideal_bounds,
- false);
- }
- }
-
- if (animation_type_ != ANIMATION_NEW_TAB_3) {
- bounds_animator_.AnimateViewTo(newtab_button_,
- newtab_button_bounds_,
- false);
+ newtab_button_->SetBounds(
+ Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset,
+ v_offset, newtab_button_size_.width(), newtab_button_size_.height());
}
}
-bool TabStrip::ShouldStartIntertTabAnimationAtEnd(int model_index,
- bool foreground) {
- return foreground && (model_index + 1 == model_->count()) &&
- (model_->GetTabContentsAt(model_index)->GetURL() ==
- GURL(chrome::kChromeUINewTabURL));
+// Called from:
+// - animation tick
+void TabStrip::AnimationLayout(double unselected_width) {
+ int tab_height = Tab::GetStandardSize().height();
+ double tab_x = 0;
+ for (int i = 0; i < GetTabCount(); ++i) {
+ TabAnimation* animation = active_animation_.get();
+ if (animation)
+ tab_x += animation->GetGapWidth(i);
+ double tab_width = TabAnimation::GetCurrentTabWidth(this, animation, i);
+ double end_of_tab = tab_x + tab_width;
+ int rounded_tab_x = Round(tab_x);
+ Tab* tab = GetTabAt(i);
+ tab->SetBounds(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
+ tab_height);
+ tab_x = end_of_tab + GetTabHOffset(i + 1);
+ }
+ LayoutNewTabButton(tab_x, unselected_width);
+ SchedulePaint();
}
void TabStrip::StartResizeLayoutAnimation() {
- ResetAnimationState(true);
- GenerateIdealBounds();
- AnimateToIdealBounds();
+ if (active_animation_.get())
+ active_animation_->Stop();
+ active_animation_.reset(new ResizeLayoutAnimation(this));
+ active_animation_->Start();
}
-void TabStrip::StartInsertTabAnimationAtEnd() {
- ResetAnimationState(true);
-
+void TabStrip::StartInsertTabAnimation(int index) {
// The TabStrip can now use its entire width to lay out Tabs.
available_width_for_tabs_ = -1;
-
- animation_type_ = ANIMATION_NEW_TAB_1;
-
- GenerateIdealBounds();
-
- int tab_data_index = ModelIndexToTabDataIndex(model_->count() - 1);
- Tab* tab = tab_data_[tab_data_index].tab;
- tab->SizeToNewTabButtonImages();
- tab->SetBounds(newtab_button_->x() +
- (newtab_button_->width() - tab->width()) / 2,
- tab_data_[tab_data_index].ideal_bounds.y(),
- tab->width(), tab->height());
- tab->set_render_as_new_tab(true);
-
- new_tab_timer_.Start(base::TimeDelta::FromMilliseconds(kNewTabDurationMs),
- this, &TabStrip::NewTabAnimation1Done);
+ if (active_animation_.get())
+ active_animation_->Stop();
+ active_animation_.reset(new InsertTabAnimation(this, index));
+ active_animation_->Start();
}
-void TabStrip::StartInsertTabAnimation(int model_index) {
- ResetAnimationState(true);
-
- // The TabStrip can now use its entire width to lay out Tabs.
- available_width_for_tabs_ = -1;
-
- GenerateIdealBounds();
-
- int tab_data_index = ModelIndexToTabDataIndex(model_index);
- Tab* tab = tab_data_[tab_data_index].tab;
- if (model_index == 0) {
- tab->SetBounds(0, tab_data_[tab_data_index].ideal_bounds.y(), 0,
- tab_data_[tab_data_index].ideal_bounds.height());
- } else {
- Tab* last_tab = tab_data_[tab_data_index - 1].tab;
- tab->SetBounds(last_tab->bounds().right() + kTabHOffset,
- tab_data_[tab_data_index].ideal_bounds.y(), 0,
- tab_data_[tab_data_index].ideal_bounds.height());
+void TabStrip::StartRemoveTabAnimation(int index, TabContents* contents) {
+ if (active_animation_.get()) {
+ // Some animations (e.g. MoveTabAnimation) cause there to be a Layout when
+ // they're completed (which includes canceled). Since |tab_data_| is now
+ // inconsistent with TabStripModel, doing this Layout will crash now, so
+ // we ask the MoveTabAnimation to skip its Layout (the state will be
+ // corrected by the RemoveTabAnimation we're about to initiate).
+ active_animation_->set_layout_on_completion(false);
+ active_animation_->Stop();
}
-
- AnimateToIdealBounds();
+ active_animation_.reset(new RemoveTabAnimation(this, index, contents));
+ active_animation_->Start();
}
-void TabStrip::StartRemoveTabAnimation(int model_index) {
- ResetAnimationState(true);
-
- // Mark the tab as closing.
- int tab_data_index = ModelIndexToTabDataIndex(model_index);
- Tab* tab = tab_data_[tab_data_index].tab;
- tab->set_closing(true);
-
- // Start an animation for the tabs.
- GenerateIdealBounds();
- AnimateToIdealBounds();
-
- // Animate the tab being closed to 0x0.
- gfx::Rect tab_bounds = tab->bounds();
- tab_bounds.set_width(0);
- bounds_animator_.AnimateViewTo(tab, tab_bounds, false);
-
- // Register delegate to do cleanup when done, BoundsAnimator takes
- // ownership of RemoveTabDelegate.
- bounds_animator_.SetAnimationDelegate(tab, new RemoveTabDelegate(this, tab),
- true);
+void TabStrip::StartMoveTabAnimation(int from_index, int to_index) {
+ if (active_animation_.get())
+ active_animation_->Stop();
+ active_animation_.reset(new MoveTabAnimation(this, from_index, to_index));
+ active_animation_->Start();
}
-void TabStrip::StartMoveTabAnimation(int from_model_index,
- int to_model_index) {
- ResetAnimationState(true);
-
- int from_tab_data_index = ModelIndexToTabDataIndex(from_model_index);
-
- Tab* tab = tab_data_[from_tab_data_index].tab;
- tab_data_.erase(tab_data_.begin() + from_tab_data_index);
-
- TabData data = {tab, gfx::Rect()};
- tab->set_mini(model_->IsMiniTab(to_model_index));
- //tab->set_app(model_->IsAppTab(to_model_index));
- tab->SetBlocked(model_->IsTabBlocked(to_model_index));
-
- int to_tab_data_index = ModelIndexToTabDataIndex(to_model_index);
-
- tab_data_.insert(tab_data_.begin() + to_tab_data_index, data);
- if (tab->phantom() != model_->IsPhantomTab(to_model_index))
- tab->set_phantom(!tab->phantom());
-
- GenerateIdealBounds();
- AnimateToIdealBounds();
+void TabStrip::StartMiniTabAnimation(int index) {
+ if (active_animation_.get())
+ active_animation_->Stop();
+ active_animation_.reset(new MiniTabAnimation(this, index));
+ active_animation_->Start();
}
-void TabStrip::StartMiniTabAnimation() {
- ResetAnimationState(true);
-
- GenerateIdealBounds();
- AnimateToIdealBounds();
+void TabStrip::StartMiniMoveTabAnimation(int from_index,
+ int to_index,
+ const gfx::Rect& start_bounds) {
+ if (active_animation_.get())
+ active_animation_->Stop();
+ active_animation_.reset(
+ new MiniMoveAnimation(this, from_index, to_index, start_bounds));
+ active_animation_->Start();
}
-void TabStrip::StopAnimating(bool layout) {
- if (!IsAnimating())
- return;
-
- new_tab_timer_.Stop();
+void TabStrip::FinishAnimation(TabStrip::TabAnimation* animation,
+ bool layout) {
+ active_animation_.reset(NULL);
- if (bounds_animator_.IsAnimating()) {
- // Cancelling the animation triggers OnBoundsAnimatorDone, which invokes
- // ResetAnimationState.
- bounds_animator_.Cancel();
- } else {
- ResetAnimationState(false);
- }
-
- DCHECK(!IsAnimating());
+ // Reset the animation state of each tab.
+ for (int i = 0, count = GetTabCount(); i < count; ++i)
+ GetTabAt(i)->set_animating_mini_change(false);
if (layout)
Layout();
}
-void TabStrip::ResetAnimationState(bool stop_new_tab_timer) {
- if (animation_type_ == ANIMATION_NEW_TAB_2)
- newtab_button_->SchedulePaint();
-
- if (stop_new_tab_timer)
- new_tab_timer_.Stop();
-
- animation_type_ = ANIMATION_DEFAULT;
-
- // Reset the animation state of each tab.
- for (int i = 0, count = GetTabCount(); i < count; ++i) {
- Tab* tab = GetTabAtTabDataIndex(i);
- tab->set_animating_mini_change(false);
- tab->set_render_as_new_tab(false);
- tab->set_render_unselected(false);
- tab->set_alpha(1);
+int TabStrip::GetIndexOfTab(const Tab* tab) const {
+ for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
+ Tab* current_tab = GetTabAt(i);
+ if (current_tab->closing()) {
+ --index;
+ } else if (current_tab == tab) {
+ return index;
+ }
}
+ return -1;
}
int TabStrip::GetMiniTabCount() const {
@@ -1681,21 +2011,22 @@ bool TabStrip::IsPointInTab(Tab* tab,
return tab->HitTest(point_in_tab_coords);
}
-void TabStrip::RemoveTab(Tab* tab) {
- int tab_data_index = TabDataIndexOfTab(tab);
-
- DCHECK(tab_data_index != -1);
+void TabStrip::RemoveTabAt(int index) {
+ Tab* removed = tab_data_.at(index).tab;
// Remove the Tab from the TabStrip's list...
- tab_data_.erase(tab_data_.begin() + tab_data_index);
+ tab_data_.erase(tab_data_.begin() + index);
// If the TabContents being detached was removed as a result of a drag
// gesture from its corresponding Tab, we don't want to remove the Tab from
// the child list, because if we do so it'll stop receiving events and the
// drag will stall. So we only remove if a drag isn't active, or the Tab
// was for some other TabContents.
- if (!IsDragSessionActive() || !drag_controller_->IsDragSourceTab(tab))
- delete tab;
+ if (!IsDragSessionActive() || !drag_controller_->IsDragSourceTab(removed)) {
+ removed->GetParent()->RemoveChildView(removed);
+ delete removed;
+ }
+ GenerateIdealBounds();
}
void TabStrip::HandleGlobalMouseMoveEvent() {
@@ -1718,40 +2049,8 @@ void TabStrip::HandleGlobalMouseMoveEvent() {
bool TabStrip::HasPhantomTabs() const {
for (int i = 0; i < GetTabCount(); ++i) {
- if (GetTabAtTabDataIndex(i)->phantom())
+ if (GetTabAt(i)->phantom())
return true;
}
return false;
}
-
-int TabStrip::GetModelIndexOfTab(const Tab* tab) const {
- for (int i = 0, model_index = 0; i < GetTabCount(); ++i) {
- Tab* current_tab = GetTabAtTabDataIndex(i);
- if (!current_tab->closing()) {
- if (current_tab == tab)
- return model_index;
- model_index++;
- }
- }
- return -1;
-}
-
-int TabStrip::ModelIndexToTabDataIndex(int model_index) const {
- int current_model_index = 0;
- for (size_t i = 0; i < tab_data_.size(); ++i) {
- if (!tab_data_[i].tab->closing()) {
- if (current_model_index == model_index)
- return i;
- current_model_index++;
- }
- }
- return tab_data_.size();
-}
-
-int TabStrip::TabDataIndexOfTab(Tab* tab) const {
- for (size_t i = 0; i < tab_data_.size(); ++i) {
- if (tab_data_[i].tab == tab)
- return i;
- }
- return -1;
-}