diff options
Diffstat (limited to 'chrome/browser/views/tabs/tab_strip.cc')
-rw-r--r-- | chrome/browser/views/tabs/tab_strip.cc | 1369 |
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; -} |