diff options
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.cc | 144 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.h | 8 |
2 files changed, 138 insertions, 14 deletions
diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc index 302d43b..07a4a4a 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc @@ -192,7 +192,7 @@ class InsertTabAnimation : public TabStripGtk::TabAnimation { virtual ~InsertTabAnimation() {} protected: - // Overridden from TabStrip::TabAnimation: + // Overridden from TabStripGtk::TabAnimation: virtual double GetWidthForTab(int index) const { if (index == index_) { bool is_selected = tabstrip_->model()->selected_index() == index; @@ -224,6 +224,92 @@ class InsertTabAnimation : public TabStripGtk::TabAnimation { DISALLOW_EVIL_CONSTRUCTORS(InsertTabAnimation); }; +/////////////////////////////////////////////////////////////////////////////// + +// Handles removal of a Tab from |index| +class RemoveTabAnimation : public TabStripGtk::TabAnimation { + public: + RemoveTabAnimation(TabStripGtk* tabstrip, int index, TabContents* contents) + : TabAnimation(tabstrip, REMOVE), + index_(index) { + int tab_count = tabstrip->GetTabCount(); + GenerateStartAndEndWidths(tab_count, tab_count - 1); + } + + virtual ~RemoveTabAnimation() { } + + // Returns the index of the tab being removed. + int index() const { return index_; } + + protected: + // Overridden from TabStripGtk::TabAnimation: + virtual double GetWidthForTab(int index) const { + TabGtk* 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. + 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), + TabGtk::GetMinimumUnselectedSize().width() + kTabHOffset); + double delta = start_width - target_width; + return start_width - (delta * animation_.GetCurrentValue()); + } + + if (tabstrip_->available_width_for_tabs_ != -1 && + index_ != tabstrip_->GetTabCount() - 1) { + return TabStripGtk::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()); + } + + 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(); + TabStripGtk::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. + void HighlightCloseButton() { + if (tabstrip_->available_width_for_tabs_ == -1) { + // This function is not required (and indeed may crash!) for removes + // spawned by non-mouse closes and drag-detaches. + return; + } + + /* get default display and screen */ + GdkDisplay* display = gdk_display_get_default(); + GdkScreen* screen = gdk_display_get_default_screen(display); + + /* get cursor position */ + int x, y; + gdk_display_get_pointer(display, NULL, &x, &y, NULL); + + /* reset cusor position */ + gdk_display_warp_pointer(display, screen, x, y); + } + + int index_; + + DISALLOW_EVIL_CONSTRUCTORS(RemoveTabAnimation); +}; + //////////////////////////////////////////////////////////////////////////////// // TabStripGtk, public: @@ -352,12 +438,14 @@ void TabStripGtk::TabInsertedAt(TabContents* contents, } void TabStripGtk::TabDetachedAt(TabContents* contents, int index) { - GetTabAt(index)->set_closing(true); - // TODO(jhawkins): Remove erase call when animations are hooked up. - tab_data_.erase(tab_data_.begin() + index); - GenerateIdealBounds(); - // TODO(jhawkins): Remove layout call when animations are hooked up. - Layout(); + if (CanUpdateDisplay()) { + 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 TabStripGtk::TabSelectedAt(TabContents* old_contents, @@ -366,13 +454,15 @@ void TabStripGtk::TabSelectedAt(TabContents* old_contents, bool user_gesture) { DCHECK(index >= 0 && index < static_cast<int>(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_; - if (!IsAnimating() && (!resize_layout_scheduled_ || tiny_tabs)) { - Layout(); - } else { - gtk_widget_queue_draw(tabstrip_.get()); + if (CanUpdateDisplay()) { + // 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 (!IsAnimating() && (!resize_layout_scheduled_ || tiny_tabs)) { + Layout(); + } else { + gtk_widget_queue_draw(tabstrip_.get()); + } } } @@ -638,6 +728,32 @@ void TabStripGtk::StartInsertTabAnimation(int index) { active_animation_->Start(); } +void TabStripGtk::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(); + } + + active_animation_.reset(new RemoveTabAnimation(this, index, contents)); + active_animation_->Start(); +} + +bool TabStripGtk::CanUpdateDisplay() { + // Don't bother laying out/painting when we're closing all tabs. + if (model_->closing_all()) { + // Make sure any active animation is ended, too. + if (active_animation_.get()) + active_animation_->Stop(); + return false; + } + return true; +} + void TabStripGtk::FinishAnimation(TabStripGtk::TabAnimation* animation, bool layout) { active_animation_.reset(NULL); diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.h b/chrome/browser/gtk/tabs/tab_strip_gtk.h index eec7d46..9504963 100644 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.h +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.h @@ -74,6 +74,7 @@ class TabStripGtk : public TabStripModelObserver, private: friend class InsertTabAnimation; + friend class RemoveTabAnimation; friend class TabAnimation; struct TabData { @@ -148,6 +149,13 @@ class TabStripGtk : public TabStripModelObserver, // Starts various types of TabStrip animations. void StartInsertTabAnimation(int index); + void StartRemoveTabAnimation(int index, TabContents* contents); + + // Returns true if detach or select changes in the model should be reflected + // in the TabStrip. This returns false if we're closing all tabs in the + // TabStrip and so we should prevent updating. This is not const because we + // use this as a signal to cancel any active animations. + bool CanUpdateDisplay(); // Notifies the TabStrip that the specified TabAnimation has completed. // Optionally a full Layout will be performed, specified by |layout|. |