diff options
author | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-09 01:15:31 +0000 |
---|---|---|
committer | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-09 01:15:31 +0000 |
commit | 192f61a33b393713132edeb99e42cb4ecb67d194 (patch) | |
tree | 81376d1f21a0e416a58f55ee6e68577f2d873038 | |
parent | a2ac1f5ab862f38704bd03813eaac001169f9aa0 (diff) | |
download | chromium_src-192f61a33b393713132edeb99e42cb4ecb67d194.zip chromium_src-192f61a33b393713132edeb99e42cb4ecb67d194.tar.gz chromium_src-192f61a33b393713132edeb99e42cb4ecb67d194.tar.bz2 |
Add an insert tab animation for the Linux tabstrip.
Review URL: http://codereview.chromium.org/62170
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13402 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.cc | 241 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.h | 25 |
2 files changed, 264 insertions, 2 deletions
diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc index 0704de0..302d43b 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc @@ -11,11 +11,14 @@ #include "chrome/common/gfx/chrome_canvas.h" #include "chrome/common/l10n_util.h" #include "chrome/common/resource_bundle.h" +#include "chrome/common/slide_animation.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" namespace { +const int kDefaultAnimationDurationMs = 100; + const int kNewTabButtonHOffset = -5; const int kNewTabButtonVOffset = 5; @@ -40,6 +43,188 @@ gfx::Rect GetInitialWidgetBounds(GtkWidget* widget) { } // namespace //////////////////////////////////////////////////////////////////////////////// +// +// TabAnimation +// +// A base class for all TabStrip animations. +// +class TabStripGtk::TabAnimation : public AnimationDelegate { + public: + friend class TabStripGtk; + + // Possible types of animation. + enum Type { + INSERT, + REMOVE, + MOVE, + RESIZE + }; + + TabAnimation(TabStripGtk* 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(TabStripGtk* tabstrip, + TabStripGtk::TabAnimation* animation, + int index) { + double unselected, selected; + tabstrip->GetCurrentTabWidths(&unselected, &selected); + TabGtk* tab = tabstrip->GetTabAt(index); + double 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) { + 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) { + AnimationEnded(animation); + } + + 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) { + tabstrip_->GetDesiredTabWidths(start_tab_count, &start_unselected_width_, + &start_selected_width_); + double standard_tab_width = + static_cast<double>(TabRendererGtk::GetStandardSize().width()); + + if (start_tab_count < end_tab_count && + start_unselected_width_ < standard_tab_width) { + double minimum_tab_width = static_cast<double>( + TabRendererGtk::GetMinimumUnselectedSize().width()); + start_unselected_width_ -= minimum_tab_width / start_tab_count; + } + + tabstrip_->GenerateIdealBounds(); + tabstrip_->GetDesiredTabWidths(end_tab_count, + &end_unselected_width_, + &end_selected_width_); + } + + TabStripGtk* tabstrip_; + SlideAnimation animation_; + + double start_selected_width_; + double start_unselected_width_; + double end_selected_width_; + double end_unselected_width_; + + private: + // 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_EVIL_CONSTRUCTORS(TabAnimation); +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Handles insertion of a Tab at |index|. +class InsertTabAnimation : public TabStripGtk::TabAnimation { + public: + explicit InsertTabAnimation(TabStripGtk* tabstrip, int index) + : TabAnimation(tabstrip, INSERT), + index_(index) { + int tab_count = tabstrip->GetTabCount(); + GenerateStartAndEndWidths(tab_count - 1, tab_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 target_width = + is_selected ? end_unselected_width_ : end_selected_width_; + double start_width = + is_selected ? TabGtk::GetMinimumSelectedSize().width() : + TabGtk::GetMinimumUnselectedSize().width(); + + double delta = target_width - start_width; + if (delta > 0) + return start_width + (delta * animation_.GetCurrentValue()); + + return start_width; + } + + 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_EVIL_CONSTRUCTORS(InsertTabAnimation); +}; + +//////////////////////////////////////////////////////////////////////////////// // TabStripGtk, public: TabStripGtk::TabStripGtk(TabStripModel* model) @@ -95,6 +280,10 @@ void TabStripGtk::AddTabStripToBox(GtkWidget* box) { void TabStripGtk::Layout() { // Called from: // - window resize + // - animation completion + if (active_animation_.get()) + active_animation_->Stop(); + GenerateIdealBounds(); int tab_count = GetTabCount(); for (int i = 0; i < tab_count; ++i) { @@ -125,6 +314,10 @@ void TabStripGtk::UpdateLoadingAnimations() { gtk_widget_queue_draw(tabstrip_.get()); } +bool TabStripGtk::IsAnimating() const { + return active_animation_.get() != NULL; +} + //////////////////////////////////////////////////////////////////////////////// // TabStripGtk, TabStripModelObserver implementation: @@ -134,6 +327,9 @@ void TabStripGtk::TabInsertedAt(TabContents* contents, DCHECK(contents); DCHECK(index == TabStripModel::kNoTab || model_->ContainsIndex(index)); + if (active_animation_.get()) + active_animation_->Stop(); + TabGtk* tab = new TabGtk(this); // Only insert if we're not already in the list. @@ -147,7 +343,12 @@ void TabStripGtk::TabInsertedAt(TabContents* contents, tab->UpdateData(contents, false); } - Layout(); + // Don't animate the first tab; it looks weird. + if (GetTabCount() > 1) { + StartInsertTabAnimation(index); + } else { + Layout(); + } } void TabStripGtk::TabDetachedAt(TabContents* contents, int index) { @@ -168,7 +369,7 @@ void TabStripGtk::TabSelectedAt(TabContents* old_contents, // 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_; - if (!resize_layout_scheduled_ || tiny_tabs) { + if (!IsAnimating() && (!resize_layout_scheduled_ || tiny_tabs)) { Layout(); } else { gtk_widget_queue_draw(tabstrip_.get()); @@ -408,6 +609,42 @@ void TabStripGtk::GetDesiredTabWidths(int tab_count, } } +// Called from: +// - animation tick +void TabStripGtk::AnimationLayout(double unselected_width) { + int tab_height = TabGtk::GetStandardSize().height(); + double tab_x = 0; + for (int i = 0; i < GetTabCount(); ++i) { + TabAnimation* animation = active_animation_.get(); + double tab_width = TabAnimation::GetCurrentTabWidth(this, animation, i); + double end_of_tab = tab_x + tab_width; + int rounded_tab_x = Round(tab_x); + TabGtk* tab = GetTabAt(i); + gfx::Rect bounds(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x, + tab_height); + tab->SetBounds(bounds); + tab_x = end_of_tab + kTabHOffset; + } + // TODO(jhawkins): Layout new tab button. + gtk_widget_queue_draw(tabstrip_.get()); +} + +void TabStripGtk::StartInsertTabAnimation(int index) { + // The TabStrip can now use its entire width to lay out Tabs. + available_width_for_tabs_ = -1; + if (active_animation_.get()) + active_animation_->Stop(); + active_animation_.reset(new InsertTabAnimation(this, index)); + active_animation_->Start(); +} + +void TabStripGtk::FinishAnimation(TabStripGtk::TabAnimation* animation, + bool layout) { + active_animation_.reset(NULL); + if (layout) + Layout(); +} + // static gboolean TabStripGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event, TabStripGtk* tabstrip) { diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.h b/chrome/browser/gtk/tabs/tab_strip_gtk.h index fbff765..eec7d46 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.h +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.h @@ -18,6 +18,8 @@ class TabStripGtk : public TabStripModelObserver, public TabGtk::TabDelegate { public: + class TabAnimation; + explicit TabStripGtk(TabStripModel* model); virtual ~TabStripGtk(); @@ -36,6 +38,10 @@ class TabStripGtk : public TabStripModelObserver, // Updates loading animations for the TabStrip. void UpdateLoadingAnimations(); + // Returns true if Tabs in this TabStrip are currently changing size or + // position. + bool IsAnimating() const; + protected: // TabStripModelObserver implementation: virtual void TabInsertedAt(TabContents* contents, @@ -67,6 +73,9 @@ class TabStripGtk : public TabStripModelObserver, virtual bool HasAvailableDragActions() const; private: + friend class InsertTabAnimation; + friend class TabAnimation; + struct TabData { TabGtk* tab; gfx::Rect ideal_bounds; @@ -131,6 +140,19 @@ class TabStripGtk : public TabStripModelObserver, // stable representations of Tab positions. void GenerateIdealBounds(); + // -- Animations ------------------------------------------------------------- + + // A generic Layout method for various classes of TabStrip animations, + // including Insert, Remove and Resize Layout cases. + void AnimationLayout(double unselected_width); + + // Starts various types of TabStrip animations. + void StartInsertTabAnimation(int index); + + // Notifies the TabStrip that the specified TabAnimation has completed. + // Optionally a full Layout will be performed, specified by |layout|. + void FinishAnimation(TabAnimation* animation, bool layout); + // The Tabs we contain, and their last generated "good" bounds. std::vector<TabData> tab_data_; @@ -167,6 +189,9 @@ class TabStripGtk : public TabStripModelObserver, // The index of the tab the mouse is currently over. -1 if not over a tab. int hover_index_; + // The currently running animation. + scoped_ptr<TabAnimation> active_animation_; + DISALLOW_COPY_AND_ASSIGN(TabStripGtk); }; |