Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(853)

Unified Diff: chrome/browser/ui/gtk/tabs/tab_strip_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/gtk/tabs/tab_strip_gtk.cc
diff --git a/chrome/browser/ui/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/ui/gtk/tabs/tab_strip_gtk.cc
deleted file mode 100644
index 68ec8bd76b2c46bcadfbb52278db398c3b71fdb9..0000000000000000000000000000000000000000
--- a/chrome/browser/ui/gtk/tabs/tab_strip_gtk.cc
+++ /dev/null
@@ -1,2275 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
-
-#include <gtk/gtk.h>
-
-#include <algorithm>
-#include <string>
-
-#include "base/bind.h"
-#include "base/debug/trace_event.h"
-#include "base/i18n/rtl.h"
-#include "base/metrics/histogram.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autocomplete/autocomplete_classifier.h"
-#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
-#include "chrome/browser/autocomplete/autocomplete_input.h"
-#include "chrome/browser/autocomplete/autocomplete_match.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/themes/theme_properties.h"
-#include "chrome/browser/themes/theme_service.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/gtk/browser_window_gtk.h"
-#include "chrome/browser/ui/gtk/custom_button.h"
-#include "chrome/browser/ui/gtk/gtk_theme_service.h"
-#include "chrome/browser/ui/gtk/gtk_util.h"
-#include "chrome/browser/ui/gtk/tabs/dragged_tab_controller_gtk.h"
-#include "chrome/browser/ui/gtk/tabs/tab_strip_menu_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/user_metrics.h"
-#include "content/public/browser/web_contents.h"
-#include "grit/generated_resources.h"
-#include "grit/theme_resources.h"
-#include "grit/ui_resources.h"
-#include "ui/base/dragdrop/gtk_dnd_util.h"
-#include "ui/base/gtk/gtk_screen_util.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/animation/animation_delegate.h"
-#include "ui/gfx/animation/slide_animation.h"
-#include "ui/gfx/gtk_compat.h"
-#include "ui/gfx/gtk_util.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/point.h"
-
-using base::UserMetricsAction;
-using content::WebContents;
-
-namespace {
-
-const int kDefaultAnimationDurationMs = 100;
-const int kResizeLayoutAnimationDurationMs = 166;
-const int kReorderAnimationDurationMs = 166;
-const int kMiniTabAnimationDurationMs = 150;
-
-const int kNewTabButtonHOffset = -5;
-const int kNewTabButtonVOffset = 5;
-
-// The delay between when the mouse leaves the tabstrip and the resize animation
-// is started.
-const int kResizeTabsTimeMs = 300;
-
-// A very short time just to make sure we don't clump up our Layout() calls
-// during slow window resizes.
-const int kLayoutAfterSizeAllocateMs = 10;
-
-// The range outside of the tabstrip where the pointer must enter/leave to
-// start/stop the resize animation.
-const int kTabStripAnimationVSlop = 40;
-
-// The horizontal offset from one tab to the next, which results in overlapping
-// tabs.
-const int kTabHOffset = -16;
-
-// Inverse ratio of the width of a tab edge to the width of the tab. When
-// hovering over the left or right edge of a tab, the drop indicator will
-// point between tabs.
-const int kTabEdgeRatioInverse = 4;
-
-// Size of the drop indicator.
-static int drop_indicator_width;
-static int drop_indicator_height;
-
-inline int Round(double x) {
- return static_cast<int>(x + 0.5);
-}
-
-// widget->allocation is not guaranteed to be set. After window creation,
-// we pick up the normal bounds by connecting to the configure-event signal.
-gfx::Rect GetInitialWidgetBounds(GtkWidget* widget) {
- GtkRequisition request;
- gtk_widget_size_request(widget, &request);
- return gfx::Rect(0, 0, request.width, request.height);
-}
-
-// Sort rectangles based on their x position. We don't care about y position
-// so we don't bother breaking ties.
-int CompareGdkRectangles(const void* p1, const void* p2) {
- int p1_x = static_cast<const GdkRectangle*>(p1)->x;
- int p2_x = static_cast<const GdkRectangle*>(p2)->x;
- if (p1_x < p2_x)
- return -1;
- else if (p1_x == p2_x)
- return 0;
- return 1;
-}
-
-bool GdkRectMatchesTabFaviconBounds(const GdkRectangle& gdk_rect, TabGtk* tab) {
- gfx::Rect favicon_bounds = tab->favicon_bounds();
- return gdk_rect.x == favicon_bounds.x() + tab->x() &&
- gdk_rect.y == favicon_bounds.y() + tab->y() &&
- gdk_rect.width == favicon_bounds.width() &&
- gdk_rect.height == favicon_bounds.height();
-}
-
-} // namespace
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// TabAnimation
-//
-// A base class for all TabStrip animations.
-//
-class TabStripGtk::TabAnimation : public gfx::AnimationDelegate {
- public:
- friend class TabStripGtk;
-
- // Possible types of animation.
- enum Type {
- INSERT,
- REMOVE,
- MOVE,
- RESIZE,
- MINI,
- MINI_MOVE
- };
-
- 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(gfx::Tween::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) {
- TabGtk* tab = tabstrip->GetTabAt(index);
- double tab_width;
- if (tab->mini()) {
- tab_width = TabGtk::GetMiniWidth();
- } else {
- double unselected, selected;
- tabstrip->GetCurrentTabWidths(&unselected, &selected);
- tab_width = tab->IsActive() ? 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 gfx::AnimationDelegate:
- virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
- tabstrip_->AnimationLayout(end_unselected_width_);
- }
-
- virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
- tabstrip_->FinishAnimation(this, layout_on_completion_);
- // This object is destroyed now, so we can't do anything else after this.
- }
-
- virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
- 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>(TabRendererGtk::GetStandardSize().width());
-
- if ((end_tab_count - start_tab_count) > 0 &&
- 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_mini_count,
- &end_unselected_width_,
- &end_selected_width_);
- }
-
- TabStripGtk* tabstrip_;
- gfx::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_COPY_AND_ASSIGN(TabAnimation);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Handles insertion of a Tab at |index|.
-class InsertTabAnimation : public TabStripGtk::TabAnimation {
- public:
- InsertTabAnimation(TabStripGtk* 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 TabStripGtk::TabAnimation:
- virtual double GetWidthForTab(int index) const OVERRIDE {
- if (index == index_) {
- bool is_selected = tabstrip_->model()->active_index() == index;
- double start_width, target_width;
- if (index < tabstrip_->GetMiniTabCount()) {
- start_width = TabGtk::GetMinimumSelectedSize().width();
- target_width = TabGtk::GetMiniWidth();
- } else {
- target_width =
- is_selected ? end_unselected_width_ : end_selected_width_;
- 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)->mini())
- return TabGtk::GetMiniWidth();
-
- if (tabstrip_->GetTabAt(index)->IsActive()) {
- 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 RemoveTabAnimation : public TabStripGtk::TabAnimation {
- public:
- RemoveTabAnimation(TabStripGtk* tabstrip, int index, WebContents* 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)));
- }
-
- 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 OVERRIDE {
- TabGtk* tab = tabstrip_->GetTabAt(index);
-
- if (index == index_) {
- // The tab(s) being removed are gradually shrunken depending on the state
- // of the animation.
- if (tab->mini()) {
- return animation_.CurrentValueBetween(TabGtk::GetMiniWidth(),
- -kTabHOffset);
- }
-
- // 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);
- return animation_.CurrentValueBetween(start_width, target_width);
- }
-
- if (tab->mini())
- return TabGtk::GetMiniWidth();
-
- 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->IsActive()) {
- 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 gfx::Animation* animation) OVERRIDE {
- tabstrip_->RemoveTabAt(index_);
- TabStripGtk::TabAnimation::AnimationEnded(animation);
- }
-
- private:
- int index_;
-
- DISALLOW_COPY_AND_ASSIGN(RemoveTabAnimation);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Handles the movement of a Tab from one position to another.
-class MoveTabAnimation : public TabStripGtk::TabAnimation {
- public:
- MoveTabAnimation(TabStripGtk* 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 gfx::AnimationDelegate:
- virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
- // 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;
- gfx::Rect bounds(Round(new_x), start_tab_a_bounds_.y(), tab_a_->width(),
- tab_a_->height());
- tabstrip_->SetTabBounds(tab_a_, bounds);
-
- // 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;
- bounds = gfx::Rect(Round(new_x), start_tab_b_bounds_.y(), tab_b_->width(),
- tab_b_->height());
- tabstrip_->SetTabBounds(tab_b_, bounds);
- }
-
- protected:
- // Overridden from TabStripGtk::TabAnimation:
- virtual int GetDuration() const OVERRIDE {
- return kReorderAnimationDurationMs;
- }
-
- private:
- // The two tabs being exchanged.
- TabGtk* tab_a_;
- TabGtk* 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 ResizeLayoutAnimation : public TabStripGtk::TabAnimation {
- public:
- explicit ResizeLayoutAnimation(TabStripGtk* 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 gfx::AnimationDelegate:
- virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
- tabstrip_->needs_resize_layout_ = false;
- TabStripGtk::TabAnimation::AnimationEnded(animation);
- }
-
- protected:
- // Overridden from TabStripGtk::TabAnimation:
- virtual int GetDuration() const OVERRIDE {
- return kResizeLayoutAnimationDurationMs;
- }
-
- virtual double GetWidthForTab(int index) const OVERRIDE {
- TabGtk* tab = tabstrip_->GetTabAt(index);
-
- if (tab->mini())
- return TabGtk::GetMiniWidth();
-
- if (tab->IsActive()) {
- 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) {
- TabGtk* current_tab = tabstrip_->GetTabAt(i);
- if (!current_tab->mini()) {
- if (current_tab->IsActive()) {
- 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 MiniTabAnimation : public TabStripGtk::TabAnimation {
- public:
- MiniTabAnimation(TabStripGtk* 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 TabStripGtk::TabAnimation:
- virtual int GetDuration() const OVERRIDE {
- return kMiniTabAnimationDurationMs;
- }
-
- virtual double GetWidthForTab(int index) const OVERRIDE {
- TabGtk* tab = tabstrip_->GetTabAt(index);
-
- if (index == index_) {
- if (tab->mini()) {
- return animation_.CurrentValueBetween(
- start_selected_width_,
- static_cast<double>(TabGtk::GetMiniWidth()));
- } else {
- return animation_.CurrentValueBetween(
- static_cast<double>(TabGtk::GetMiniWidth()),
- end_selected_width_);
- }
- } else if (tab->mini()) {
- return TabGtk::GetMiniWidth();
- }
-
- if (tab->IsActive()) {
- 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 MiniMoveAnimation : public TabStripGtk::TabAnimation {
- public:
- MiniMoveAnimation(TabStripGtk* 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 gfx::AnimationDelegate:
- virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
- // 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());
- tabstrip_->SetTabBounds(tab_, tab_bounds);
- }
-
- virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
- tabstrip_->needs_resize_layout_ = false;
- TabStripGtk::TabAnimation::AnimationEnded(animation);
- }
-
- virtual double GetGapWidth(int index) OVERRIDE {
- if (to_index_ < from_index_) {
- // The tab was made 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 was made a normal tab.
- if (index == from_index_) {
- return animation_.CurrentValueBetween(
- TabGtk::GetMiniWidth() + kTabHOffset, 0);
- }
- }
- return 0;
- }
-
- protected:
- // Overridden from TabStripGtk::TabAnimation:
- virtual int GetDuration() const OVERRIDE {
- return kReorderAnimationDurationMs;
- }
-
- virtual double GetWidthForTab(int index) const OVERRIDE {
- TabGtk* tab = tabstrip_->GetTabAt(index);
-
- if (index == to_index_)
- return animation_.CurrentValueBetween(0, target_bounds_.width());
-
- if (tab->mini())
- return TabGtk::GetMiniWidth();
-
- if (tab->IsActive()) {
- return animation_.CurrentValueBetween(start_selected_width_,
- end_selected_width_);
- }
-
- return animation_.CurrentValueBetween(start_unselected_width_,
- end_unselected_width_);
- }
-
- private:
- // The tab being moved.
- TabGtk* tab_;
-
- // 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);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, public:
-
-// static
-const int TabStripGtk::mini_to_non_mini_gap_ = 3;
-
-TabStripGtk::TabStripGtk(TabStripModel* model, BrowserWindowGtk* window)
- : current_unselected_width_(TabGtk::GetStandardSize().width()),
- current_selected_width_(TabGtk::GetStandardSize().width()),
- available_width_for_tabs_(-1),
- needs_resize_layout_(false),
- tab_vertical_offset_(0),
- model_(model),
- window_(window),
- theme_service_(GtkThemeService::GetFrom(model->profile())),
- added_as_message_loop_observer_(false),
- hover_tab_selector_(model),
- weak_factory_(this),
- layout_factory_(this) {
-}
-
-TabStripGtk::~TabStripGtk() {
- model_->RemoveObserver(this);
- tabstrip_.Destroy();
-
- // Free any remaining tabs. This is needed to free the very last tab,
- // because it is not animated on close. This also happens when all of the
- // tabs are closed at once.
- std::vector<TabData>::iterator iterator = tab_data_.begin();
- for (; iterator < tab_data_.end(); iterator++) {
- delete iterator->tab;
- }
-
- tab_data_.clear();
-
- // Make sure we unhook ourselves as a message loop observer so that we don't
- // crash in the case where the user closes the last tab in a window.
- RemoveMessageLoopObserver();
-}
-
-void TabStripGtk::Init() {
- model_->AddObserver(this);
-
- tabstrip_.Own(gtk_fixed_new());
- ViewIDUtil::SetID(tabstrip_.get(), VIEW_ID_TAB_STRIP);
- // We want the tab strip to be horizontally shrinkable, so that the Chrome
- // window can be resized freely.
- gtk_widget_set_size_request(tabstrip_.get(), 0,
- TabGtk::GetMinimumUnselectedSize().height());
- gtk_widget_set_app_paintable(tabstrip_.get(), TRUE);
- gtk_drag_dest_set(tabstrip_.get(), GTK_DEST_DEFAULT_ALL,
- NULL, 0,
- static_cast<GdkDragAction>(
- GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
- static const int targets[] = { ui::TEXT_URI_LIST,
- ui::NETSCAPE_URL,
- ui::TEXT_PLAIN,
- -1 };
- ui::SetDestTargetList(tabstrip_.get(), targets);
-
- g_signal_connect(tabstrip_.get(), "map",
- G_CALLBACK(OnMapThunk), this);
- g_signal_connect(tabstrip_.get(), "expose-event",
- G_CALLBACK(OnExposeThunk), this);
- g_signal_connect(tabstrip_.get(), "size-allocate",
- G_CALLBACK(OnSizeAllocateThunk), this);
- g_signal_connect(tabstrip_.get(), "drag-motion",
- G_CALLBACK(OnDragMotionThunk), this);
- g_signal_connect(tabstrip_.get(), "drag-drop",
- G_CALLBACK(OnDragDropThunk), this);
- g_signal_connect(tabstrip_.get(), "drag-leave",
- G_CALLBACK(OnDragLeaveThunk), this);
- g_signal_connect(tabstrip_.get(), "drag-data-received",
- G_CALLBACK(OnDragDataReceivedThunk), this);
-
- newtab_button_.reset(MakeNewTabButton());
- newtab_surface_bounds_.SetRect(0, 0, newtab_button_->SurfaceWidth(),
- newtab_button_->SurfaceHeight());
-
- gtk_widget_show_all(tabstrip_.get());
-
- bounds_ = GetInitialWidgetBounds(tabstrip_.get());
-
- if (drop_indicator_width == 0) {
- // Direction doesn't matter, both images are the same size.
- GdkPixbuf* drop_image = GetDropArrowImage(true)->ToGdkPixbuf();
- drop_indicator_width = gdk_pixbuf_get_width(drop_image);
- drop_indicator_height = gdk_pixbuf_get_height(drop_image);
- }
-
- registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
- content::Source<ThemeService>(theme_service_));
- theme_service_->InitThemesFor(this);
-
- ViewIDUtil::SetDelegateForWidget(widget(), this);
-}
-
-void TabStripGtk::Show() {
- gtk_widget_show(tabstrip_.get());
-}
-
-void TabStripGtk::Hide() {
- gtk_widget_hide(tabstrip_.get());
-}
-
-bool TabStripGtk::IsActiveDropTarget() const {
- for (int i = 0; i < GetTabCount(); ++i) {
- TabGtk* tab = GetTabAt(i);
- if (tab->dragging())
- return true;
- }
- return false;
-}
-
-void TabStripGtk::Layout() {
- // Called from:
- // - window resize
- // - animation completion
- StopAnimation();
-
- GenerateIdealBounds();
- int tab_count = GetTabCount();
- int tab_right = 0;
- for (int i = 0; i < tab_count; ++i) {
- const gfx::Rect& bounds = tab_data_.at(i).ideal_bounds;
- TabGtk* tab = GetTabAt(i);
- tab->set_animating_mini_change(false);
- tab->set_vertical_offset(tab_vertical_offset_);
- SetTabBounds(tab, bounds);
- tab_right = bounds.right();
- tab_right += GetTabHOffset(i + 1);
- }
-
- LayoutNewTabButton(static_cast<double>(tab_right), current_unselected_width_);
-}
-
-void TabStripGtk::SchedulePaint() {
- gtk_widget_queue_draw(tabstrip_.get());
-}
-
-void TabStripGtk::SetBounds(const gfx::Rect& bounds) {
- bounds_ = bounds;
-}
-
-void TabStripGtk::UpdateLoadingAnimations() {
- for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
- TabGtk* current_tab = GetTabAt(i);
- if (current_tab->closing()) {
- --index;
- } else {
- TabRendererGtk::AnimationState state;
- content::WebContents* web_contents = model_->GetWebContentsAt(index);
- if (!web_contents|| !web_contents->IsLoading()) {
- state = TabGtk::ANIMATION_NONE;
- } else if (web_contents->IsWaitingForResponse()) {
- state = TabGtk::ANIMATION_WAITING;
- } else {
- state = TabGtk::ANIMATION_LOADING;
- }
- if (current_tab->ValidateLoadingAnimation(state)) {
- // Queue the tab's icon area to be repainted.
- gfx::Rect favicon_bounds = current_tab->favicon_bounds();
- gtk_widget_queue_draw_area(tabstrip_.get(),
- favicon_bounds.x() + current_tab->x(),
- favicon_bounds.y() + current_tab->y(),
- favicon_bounds.width(),
- favicon_bounds.height());
- }
- }
- }
-}
-
-bool TabStripGtk::IsCompatibleWith(TabStripGtk* other) {
- return model_->profile() == other->model()->profile();
-}
-
-bool TabStripGtk::IsAnimating() const {
- return active_animation_.get() != NULL;
-}
-
-void TabStripGtk::DestroyDragController() {
- drag_controller_.reset();
-}
-
-void TabStripGtk::DestroyDraggedTab(TabGtk* tab) {
- // We could be running an animation that references this Tab.
- StopAnimation();
-
- // 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.
- 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;
- }
- }
-
- gtk_container_remove(GTK_CONTAINER(tabstrip_.get()), tab->widget());
- // If we delete the dragged source tab here, the DestroyDragWidget posted
- // task will be run after the tab is deleted, leading to a crash.
- base::MessageLoop::current()->DeleteSoon(FROM_HERE, 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 TabStripGtk::GetIdealBounds(int index) {
- DCHECK(index >= 0 && index < GetTabCount());
- return tab_data_.at(index).ideal_bounds;
-}
-
-void TabStripGtk::SetVerticalOffset(int offset) {
- tab_vertical_offset_ = offset;
- Layout();
-}
-
-gfx::Point TabStripGtk::GetTabStripOriginForWidget(GtkWidget* target) {
- int x, y;
- GtkAllocation widget_allocation;
- gtk_widget_get_allocation(widget(), &widget_allocation);
- if (!gtk_widget_translate_coordinates(widget(), target,
- -widget_allocation.x, 0, &x, &y)) {
- // If the tab strip isn't showing, give the coordinates relative to the
- // toplevel instead.
- if (!gtk_widget_translate_coordinates(
- gtk_widget_get_toplevel(widget()), target, 0, 0, &x, &y)) {
- NOTREACHED();
- }
- }
- if (!gtk_widget_get_has_window(target)) {
- GtkAllocation target_allocation;
- gtk_widget_get_allocation(target, &target_allocation);
- x += target_allocation.x;
- y += target_allocation.y;
- }
- return gfx::Point(x, y);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ViewIDUtil::Delegate implementation
-
-GtkWidget* TabStripGtk::GetWidgetForViewID(ViewID view_id) {
- if (GetTabCount() > 0) {
- if (view_id == VIEW_ID_TAB_LAST) {
- return GetTabAt(GetTabCount() - 1)->widget();
- } 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 GetTabAt(index)->widget();
- } else {
- return NULL;
- }
- }
- }
-
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, TabStripModelObserver implementation:
-
-void TabStripGtk::TabInsertedAt(WebContents* contents,
- int index,
- bool foreground) {
- TRACE_EVENT0("ui::gtk", "TabStripGtk::TabInsertedAt");
-
- DCHECK(contents);
- DCHECK(index == TabStripModel::kNoTab || model_->ContainsIndex(index));
-
- StopAnimation();
-
- bool contains_tab = false;
- TabGtk* tab = NULL;
- // First see if this Tab is one that was dragged out of this TabStrip and is
- // now being dragged back in. In this case, the DraggedTabController actually
- // has the Tab already constructed and we can just insert it into our list
- // again.
- if (IsDragSessionActive()) {
- tab = drag_controller_->GetDraggedTabForContents(contents);
- if (tab) {
- // If the Tab was detached, it would have been animated closed but not
- // removed, so we need to reset this property.
- tab->set_closing(false);
- tab->ValidateLoadingAnimation(TabRendererGtk::ANIMATION_NONE);
- tab->SetVisible(true);
- }
-
- // See if we're already in the list. We don't want to add ourselves twice.
- std::vector<TabData>::const_iterator iter = tab_data_.begin();
- for (; iter != tab_data_.end() && !contains_tab; ++iter) {
- if (iter->tab == tab)
- contains_tab = true;
- }
- }
-
- if (!tab)
- tab = new TabGtk(this);
-
- // Only insert if we're not already in the list.
- if (!contains_tab) {
- TabData d = { tab, gfx::Rect() };
- tab_data_.insert(tab_data_.begin() + index, d);
- tab->UpdateData(contents, model_->IsAppTab(index), false);
- }
- tab->set_mini(model_->IsMiniTab(index));
- tab->set_app(model_->IsAppTab(index));
- tab->SetBlocked(model_->IsTabBlocked(index));
-
- if (gtk_widget_get_parent(tab->widget()) != tabstrip_.get())
- gtk_fixed_put(GTK_FIXED(tabstrip_.get()), tab->widget(), 0, 0);
-
- // Don't animate the first tab; it looks weird.
- if (GetTabCount() > 1) {
- StartInsertTabAnimation(index);
- // We added the tab at 0x0, we need to force an animation step otherwise
- // if GTK paints before the animation event the tab is painted at 0x0
- // which is most likely not where it should be positioned.
- active_animation_->AnimationProgressed(NULL);
- } else {
- Layout();
- }
-
- ReStack();
-}
-
-void TabStripGtk::TabDetachedAt(WebContents* 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 TabStripGtk::ActiveTabChanged(WebContents* old_contents,
- WebContents* new_contents,
- int index,
- int reason) {
- TRACE_EVENT0("ui::gtk", "TabStripGtk::ActiveTabChanged");
- ReStack();
-}
-
-void TabStripGtk::TabSelectionChanged(TabStripModel* tab_strip_model,
- const ui::ListSelectionModel& old_model) {
- // 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() && (!needs_resize_layout_ || tiny_tabs))
- Layout();
-
- if (model_->active_index() >= 0)
- GetTabAt(model_->active_index())->SchedulePaint();
-
- if (old_model.active() >= 0) {
- GetTabAt(old_model.active())->SchedulePaint();
- GetTabAt(old_model.active())->StopMiniTabTitleAnimation();
- }
-
- std::vector<int> indices_affected;
- std::insert_iterator<std::vector<int> > it1(indices_affected,
- indices_affected.begin());
- std::set_symmetric_difference(
- old_model.selected_indices().begin(),
- old_model.selected_indices().end(),
- model_->selection_model().selected_indices().begin(),
- model_->selection_model().selected_indices().end(),
- it1);
- for (std::vector<int>::iterator it = indices_affected.begin();
- it != indices_affected.end(); ++it) {
- // SchedulePaint() has already been called for the active tab and
- // the previously active tab (if it still exists).
- if (*it != model_->active_index() && *it != old_model.active())
- GetTabAtAdjustForAnimation(*it)->SchedulePaint();
- }
-
- ui::ListSelectionModel::SelectedIndices no_longer_selected =
- base::STLSetDifference<ui::ListSelectionModel::SelectedIndices>(
- old_model.selected_indices(),
- model_->selection_model().selected_indices());
- for (std::vector<int>::iterator it = no_longer_selected.begin();
- it != no_longer_selected.end(); ++it) {
- GetTabAtAdjustForAnimation(*it)->StopMiniTabTitleAnimation();
- }
-}
-
-void TabStripGtk::TabMoved(WebContents* contents,
- int from_index,
- int to_index) {
- gfx::Rect start_bounds = GetIdealBounds(from_index);
- TabGtk* 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);
- GenerateIdealBounds();
- StartMoveTabAnimation(from_index, to_index);
- ReStack();
-}
-
-void TabStripGtk::TabChangedAt(WebContents* 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.
- TabGtk* tab = GetTabAtAdjustForAnimation(index);
- if (change_type == TITLE_NOT_LOADING) {
- if (tab->mini() && !tab->IsActive())
- tab->StartMiniTabTitleAnimation();
- // We'll receive another notification of the change asynchronously.
- return;
- }
- tab->UpdateData(contents,
- model_->IsAppTab(index),
- change_type == LOADING_ONLY);
- tab->UpdateFromModel();
-}
-
-void TabStripGtk::TabReplacedAt(TabStripModel* tab_strip_model,
- WebContents* old_contents,
- WebContents* new_contents,
- int index) {
- TabChangedAt(new_contents, index, ALL);
-}
-
-void TabStripGtk::TabMiniStateChanged(WebContents* contents, int index) {
- // Don't do anything if we've already picked up the change from TabMoved.
- if (GetTabAt(index)->mini() == model_->IsMiniTab(index))
- return;
-
- 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 (window_ && window_->window() &&
- gtk_widget_get_visible(GTK_WIDGET(window_->window()))) {
- StartMiniTabAnimation(index);
- } else {
- Layout();
- }
-}
-
-void TabStripGtk::TabBlockedStateChanged(WebContents* contents, int index) {
- GetTabAt(index)->SetBlocked(model_->IsTabBlocked(index));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, TabGtk::TabDelegate implementation:
-
-bool TabStripGtk::IsTabActive(const TabGtk* tab) const {
- if (tab->closing())
- return false;
-
- return GetIndexOfTab(tab) == model_->active_index();
-}
-
-bool TabStripGtk::IsTabSelected(const TabGtk* tab) const {
- if (tab->closing())
- return false;
-
- return model_->IsTabSelected(GetIndexOfTab(tab));
-}
-
-bool TabStripGtk::IsTabDetached(const TabGtk* tab) const {
- if (drag_controller_.get())
- return drag_controller_->IsTabDetached(tab);
- return false;
-}
-
-void TabStripGtk::GetCurrentTabWidths(double* unselected_width,
- double* selected_width) const {
- *unselected_width = current_unselected_width_;
- *selected_width = current_selected_width_;
-}
-
-bool TabStripGtk::IsTabPinned(const TabGtk* tab) const {
- if (tab->closing())
- return false;
-
- return model_->IsTabPinned(GetIndexOfTab(tab));
-}
-
-void TabStripGtk::ActivateTab(TabGtk* tab) {
- int index = GetIndexOfTab(tab);
- if (model_->ContainsIndex(index))
- model_->ActivateTabAt(index, true);
-}
-
-void TabStripGtk::ToggleTabSelection(TabGtk* tab) {
- int index = GetIndexOfTab(tab);
- model_->ToggleSelectionAt(index);
-}
-
-void TabStripGtk::ExtendTabSelection(TabGtk* tab) {
- int index = GetIndexOfTab(tab);
- if (model_->ContainsIndex(index))
- model_->ExtendSelectionTo(index);
-}
-
-void TabStripGtk::CloseTab(TabGtk* tab) {
- int tab_index = GetIndexOfTab(tab);
- if (model_->ContainsIndex(tab_index)) {
- TabGtk* 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;
- // We hook into the message loop in order to receive mouse move events when
- // the mouse is outside of the tabstrip. We unhook once the resize layout
- // animation is started.
- AddMessageLoopObserver();
- model_->CloseWebContentsAt(tab_index,
- TabStripModel::CLOSE_USER_GESTURE |
- TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
- }
-}
-
-bool TabStripGtk::IsCommandEnabledForTab(
- TabStripModel::ContextMenuCommand command_id, const TabGtk* tab) const {
- int index = GetIndexOfTab(tab);
- if (model_->ContainsIndex(index))
- return model_->IsContextMenuCommandEnabled(index, command_id);
- return false;
-}
-
-void TabStripGtk::ExecuteCommandForTab(
- TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
- int index = GetIndexOfTab(tab);
- if (model_->ContainsIndex(index))
- model_->ExecuteContextMenuCommand(index, command_id);
-}
-
-void TabStripGtk::StartHighlightTabsForCommand(
- TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
- if (command_id == TabStripModel::CommandCloseOtherTabs ||
- command_id == TabStripModel::CommandCloseTabsToRight) {
- NOTIMPLEMENTED();
- }
-}
-
-void TabStripGtk::StopHighlightTabsForCommand(
- TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
- if (command_id == TabStripModel::CommandCloseTabsToRight ||
- command_id == TabStripModel::CommandCloseOtherTabs) {
- // Just tell all Tabs to stop pulsing - it's safe.
- StopAllHighlighting();
- }
-}
-
-void TabStripGtk::StopAllHighlighting() {
- // TODO(jhawkins): Hook up animations.
- NOTIMPLEMENTED();
-}
-
-void TabStripGtk::MaybeStartDrag(TabGtk* tab, const gfx::Point& point) {
- // Don't accidentally start any drag operations during animations if the
- // mouse is down.
- if (IsAnimating() || tab->closing() || !HasAvailableDragActions())
- return;
-
- std::vector<TabGtk*> tabs;
- for (size_t i = 0; i < model()->selection_model().size(); i++) {
- TabGtk* selected_tab =
- GetTabAtAdjustForAnimation(
- model()->selection_model().selected_indices()[i]);
- if (!selected_tab->closing())
- tabs.push_back(selected_tab);
- }
-
- drag_controller_.reset(new DraggedTabControllerGtk(this, tab, tabs));
- drag_controller_->CaptureDragInfo(point);
-}
-
-void TabStripGtk::ContinueDrag(GdkDragContext* context) {
- // We can get called even if |MaybeStartDrag| wasn't called in the event of
- // a TabStrip animation when the mouse button is down. In this case we should
- // _not_ continue the drag because it can lead to weird bugs.
- if (drag_controller_.get())
- drag_controller_->Drag();
-}
-
-bool TabStripGtk::EndDrag(bool canceled) {
- return drag_controller_.get() ? drag_controller_->EndDrag(canceled) : false;
-}
-
-bool TabStripGtk::HasAvailableDragActions() const {
- return model_->delegate()->GetDragActions() != 0;
-}
-
-GtkThemeService* TabStripGtk::GetThemeProvider() {
- return theme_service_;
-}
-
-TabStripMenuController* TabStripGtk::GetTabStripMenuControllerForTab(
- TabGtk* tab) {
- return new TabStripMenuController(tab, model(), GetIndexOfTab(tab));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, MessageLoop::Observer implementation:
-
-void TabStripGtk::WillProcessEvent(GdkEvent* event) {
- // Nothing to do.
-}
-
-void TabStripGtk::DidProcessEvent(GdkEvent* event) {
- switch (event->type) {
- case GDK_MOTION_NOTIFY:
- case GDK_LEAVE_NOTIFY:
- HandleGlobalMouseMoveEvent();
- break;
- default:
- break;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, content::NotificationObserver implementation:
-
-void TabStripGtk::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- DCHECK_EQ(type, chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
- SetNewTabButtonBackground();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, private:
-
-int TabStripGtk::GetTabCount() const {
- return static_cast<int>(tab_data_.size());
-}
-
-int TabStripGtk::GetMiniTabCount() const {
- int mini_count = 0;
- for (size_t i = 0; i < tab_data_.size(); ++i) {
- if (tab_data_[i].tab->mini())
- mini_count++;
- else
- return mini_count;
- }
- return mini_count;
-}
-
-int TabStripGtk::GetAvailableWidthForTabs(TabGtk* last_tab) const {
- if (!base::i18n::IsRTL())
- return last_tab->x() - bounds_.x() + last_tab->width();
- else
- return bounds_.width() - last_tab->x();
-}
-
-int TabStripGtk::GetIndexOfTab(const TabGtk* tab) const {
- for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
- TabGtk* current_tab = GetTabAt(i);
- if (current_tab->closing()) {
- --index;
- } else if (current_tab == tab) {
- return index;
- }
- }
- return -1;
-}
-
-TabGtk* TabStripGtk::GetTabAt(int index) const {
- DCHECK_GE(index, 0);
- DCHECK_LT(index, GetTabCount());
- return tab_data_.at(index).tab;
-}
-
-TabGtk* TabStripGtk::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);
-}
-
-void TabStripGtk::RemoveTabAt(int index) {
- TabGtk* removed = tab_data_.at(index).tab;
-
- // Remove the Tab from the TabStrip's list.
- tab_data_.erase(tab_data_.begin() + index);
-
- if (!removed->dragging()) {
- gtk_container_remove(GTK_CONTAINER(tabstrip_.get()), removed->widget());
- delete removed;
- }
-}
-
-void TabStripGtk::HandleGlobalMouseMoveEvent() {
- if (!IsCursorInTabStripZone()) {
- // Mouse moved outside the tab slop zone, start a timer to do a resize
- // layout after a short while...
- if (!weak_factory_.HasWeakPtrs()) {
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&TabStripGtk::ResizeLayoutTabs,
- weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(kResizeTabsTimeMs));
- }
- } else {
- // Mouse moved quickly out of the tab strip and then into it again, so
- // cancel the timer so that the strip doesn't move when the mouse moves
- // back over it.
- weak_factory_.InvalidateWeakPtrs();
- }
-}
-
-void TabStripGtk::GenerateIdealBounds() {
- int tab_count = GetTabCount();
- double unselected, selected;
- GetDesiredTabWidths(tab_count, GetMiniTabCount(), &unselected, &selected);
-
- current_unselected_width_ = unselected;
- current_selected_width_ = selected;
-
- // NOTE: This currently assumes a tab's height doesn't differ based on
- // selected state or the number of tabs in the strip!
- int tab_height = TabGtk::GetStandardSize().height();
- double tab_x = tab_start_x();
- for (int i = 0; i < tab_count; ++i) {
- TabGtk* tab = GetTabAt(i);
- double tab_width = unselected;
- if (tab->mini())
- tab_width = TabGtk::GetMiniWidth();
- else if (tab->IsActive())
- 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_data_.at(i).ideal_bounds = state;
- tab_x = end_of_tab + GetTabHOffset(i + 1);
- }
-}
-
-void TabStripGtk::LayoutNewTabButton(double last_tab_right,
- double unselected_width) {
- GtkWidget* toplevel = gtk_widget_get_ancestor(widget(), GTK_TYPE_WINDOW);
- bool is_maximized = false;
- if (toplevel) {
- GdkWindow* gdk_window = gtk_widget_get_window(toplevel);
- is_maximized = (gdk_window_get_state(gdk_window) &
- GDK_WINDOW_STATE_MAXIMIZED) != 0;
- }
-
- int y = is_maximized ? 0 : kNewTabButtonVOffset;
- int height = newtab_surface_bounds_.height() + kNewTabButtonVOffset - y;
-
- gfx::Rect bounds(0, y, newtab_surface_bounds_.width(), height);
- int delta = abs(Round(unselected_width) - TabGtk::GetStandardSize().width());
- 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.
- bounds.set_x(bounds_.width() - newtab_button_->WidgetAllocation().width);
- } else {
- bounds.set_x(Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset);
- }
- bounds.set_x(gtk_util::MirroredLeftPointForRect(tabstrip_.get(), bounds));
-
- gtk_fixed_move(GTK_FIXED(tabstrip_.get()), newtab_button_->widget(),
- bounds.x(), bounds.y());
- gtk_widget_set_size_request(newtab_button_->widget(), bounds.width(),
- bounds.height());
-}
-
-void TabStripGtk::GetDesiredTabWidths(int tab_count,
- int mini_tab_count,
- double* unselected_width,
- double* selected_width) const {
- DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
- const double min_unselected_width =
- TabGtk::GetMinimumUnselectedSize().width();
- const double min_selected_width =
- TabGtk::GetMinimumSelectedSize().width();
-
- *unselected_width = min_unselected_width;
- *selected_width = min_selected_width;
-
- if (tab_count == 0) {
- // Return immediately to avoid divide-by-zero below.
- return;
- }
-
- // Determine how much space we can actually allocate to tabs.
- GtkAllocation tabstrip_allocation;
- gtk_widget_get_allocation(tabstrip_.get(), &tabstrip_allocation);
- int available_width = tabstrip_allocation.width;
- if (available_width_for_tabs_ < 0) {
- available_width = bounds_.width();
- available_width -=
- (kNewTabButtonHOffset + newtab_button_->WidgetAllocation().width);
- } else {
- // Interesting corner case: if |available_width_for_tabs_| > the result
- // of the calculation in the conditional arm above, the strip is in
- // overflow. We can either use the specified width or the true available
- // width here; the first preserves the consistent "leave the last tab under
- // the user's mouse so they can close many tabs" behavior at the cost of
- // prolonging the glitchy appearance of the overflow state, while the second
- // gets us out of overflow as soon as possible but forces the user to move
- // their mouse for a few tabs' worth of closing. We choose visual
- // imperfection over behavioral imperfection and select the first option.
- available_width = available_width_for_tabs_;
- }
-
- if (mini_tab_count > 0) {
- available_width -= mini_tab_count * (TabGtk::GetMiniWidth() + kTabHOffset);
- tab_count -= mini_tab_count;
- if (tab_count == 0) {
- *selected_width = *unselected_width = TabGtk::GetStandardSize().width();
- return;
- }
- // Account for gap between the last mini-tab and first normal tab.
- available_width -= mini_to_non_mini_gap_;
- }
-
- // Calculate the desired tab widths by dividing the available space into equal
- // portions. Don't let tabs get larger than the "standard width" or smaller
- // than the minimum width for each type, respectively.
- const int total_offset = kTabHOffset * (tab_count - 1);
- const double desired_tab_width = std::min(
- (static_cast<double>(available_width - total_offset) /
- static_cast<double>(tab_count)),
- static_cast<double>(TabGtk::GetStandardSize().width()));
-
- *unselected_width = std::max(desired_tab_width, min_unselected_width);
- *selected_width = std::max(desired_tab_width, min_selected_width);
-
- // When there are multiple tabs, we'll have one selected and some unselected
- // tabs. If the desired width was between the minimum sizes of these types,
- // try to shrink the tabs with the smaller minimum. For example, if we have
- // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5. If
- // selected tabs have a minimum width of 4 and unselected tabs have a minimum
- // width of 1, the above code would set *unselected_width = 2.5,
- // *selected_width = 4, which results in a total width of 11.5. Instead, we
- // want to set *unselected_width = 2, *selected_width = 4, for a total width
- // of 10.
- if (tab_count > 1) {
- if ((min_unselected_width < min_selected_width) &&
- (desired_tab_width < min_selected_width)) {
- double calc_width =
- static_cast<double>(
- available_width - total_offset - min_selected_width) /
- static_cast<double>(tab_count - 1);
- *unselected_width = std::max(calc_width, min_unselected_width);
- } else if ((min_unselected_width > min_selected_width) &&
- (desired_tab_width < min_unselected_width)) {
- *selected_width = std::max(available_width - total_offset -
- (min_unselected_width * (tab_count - 1)), min_selected_width);
- }
- }
-}
-
-int TabStripGtk::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;
-}
-
-int TabStripGtk::tab_start_x() const {
- return 0;
-}
-
-void TabStripGtk::ResizeLayoutTabs() {
- weak_factory_.InvalidateWeakPtrs();
- layout_factory_.InvalidateWeakPtrs();
-
- // It is critically important that this is unhooked here, otherwise we will
- // keep spying on messages forever.
- RemoveMessageLoopObserver();
-
- available_width_for_tabs_ = -1;
- int mini_tab_count = GetMiniTabCount();
- if (mini_tab_count == GetTabCount()) {
- // Only mini tabs, we know the tab widths won't have changed (all mini-tabs
- // have the same width), so there is nothing to do.
- return;
- }
- TabGtk* first_tab = GetTabAt(mini_tab_count);
- double unselected, selected;
- GetDesiredTabWidths(GetTabCount(), mini_tab_count, &unselected, &selected);
- int w = Round(first_tab->IsActive() ? selected : unselected);
-
- // We only want to run the animation if we're not already at the desired
- // size.
- if (abs(first_tab->width() - w) > 1)
- StartResizeLayoutAnimation();
-}
-
-bool TabStripGtk::IsCursorInTabStripZone() const {
- gfx::Point tabstrip_topleft;
- gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &tabstrip_topleft);
-
- gfx::Rect bds = bounds();
- bds.set_origin(tabstrip_topleft);
- bds.set_height(bds.height() + kTabStripAnimationVSlop);
-
- GdkScreen* screen = gdk_screen_get_default();
- GdkDisplay* display = gdk_screen_get_display(screen);
- gint x, y;
- gdk_display_get_pointer(display, NULL, &x, &y, NULL);
- gfx::Point cursor_point(x, y);
-
- return bds.Contains(cursor_point);
-}
-
-void TabStripGtk::ReStack() {
- TRACE_EVENT0("ui::gtk", "TabStripGtk::ReStack");
-
- if (!gtk_widget_get_realized(tabstrip_.get())) {
- // If the window isn't realized yet, we can't stack them yet. It will be
- // done by the OnMap signal handler.
- return;
- }
- int tab_count = GetTabCount();
- TabGtk* active_tab = NULL;
- for (int i = tab_count - 1; i >= 0; --i) {
- TabGtk* tab = GetTabAt(i);
- if (tab->IsActive())
- active_tab = tab;
- else
- tab->Raise();
- }
- if (active_tab)
- active_tab->Raise();
-}
-
-void TabStripGtk::AddMessageLoopObserver() {
- if (!added_as_message_loop_observer_) {
- base::MessageLoopForUI::current()->AddObserver(this);
- added_as_message_loop_observer_ = true;
- }
-}
-
-void TabStripGtk::RemoveMessageLoopObserver() {
- if (added_as_message_loop_observer_) {
- base::MessageLoopForUI::current()->RemoveObserver(this);
- added_as_message_loop_observer_ = false;
- }
-}
-
-gfx::Rect TabStripGtk::GetDropBounds(int drop_index,
- bool drop_before,
- bool* is_beneath) {
- DCHECK_NE(drop_index, -1);
- int center_x;
- if (drop_index < GetTabCount()) {
- TabGtk* tab = GetTabAt(drop_index);
- gfx::Rect bounds = tab->GetNonMirroredBounds(tabstrip_.get());
- // TODO(sky): update these for pinned tabs.
- if (drop_before)
- center_x = bounds.x() - (kTabHOffset / 2);
- else
- center_x = bounds.x() + (bounds.width() / 2);
- } else {
- TabGtk* last_tab = GetTabAt(drop_index - 1);
- gfx::Rect bounds = last_tab->GetNonMirroredBounds(tabstrip_.get());
- center_x = bounds.x() + bounds.width() + (kTabHOffset / 2);
- }
-
- center_x = gtk_util::MirroredXCoordinate(tabstrip_.get(), center_x);
-
- // Determine the screen bounds.
- gfx::Point drop_loc(center_x - drop_indicator_width / 2,
- -drop_indicator_height);
- gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &drop_loc);
- gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
- drop_indicator_height);
-
- // TODO(jhawkins): We always display the arrow underneath the tab because we
- // don't have custom frame support yet.
- *is_beneath = true;
- if (*is_beneath)
- drop_bounds.Offset(0, drop_bounds.height() + bounds().height());
-
- return drop_bounds;
-}
-
-void TabStripGtk::UpdateDropIndex(GdkDragContext* context, gint x, gint y) {
- // If the UI layout is right-to-left, we need to mirror the mouse
- // coordinates since we calculate the drop index based on the
- // original (and therefore non-mirrored) positions of the tabs.
- x = gtk_util::MirroredXCoordinate(tabstrip_.get(), x);
- // We don't allow replacing the urls of mini-tabs.
- for (int i = GetMiniTabCount(); i < GetTabCount(); ++i) {
- TabGtk* tab = GetTabAt(i);
- gfx::Rect bounds = tab->GetNonMirroredBounds(tabstrip_.get());
- const int tab_max_x = bounds.x() + bounds.width();
- const int hot_width = bounds.width() / kTabEdgeRatioInverse;
- if (x < tab_max_x) {
- if (x < bounds.x() + hot_width)
- SetDropIndex(i, true);
- else if (x >= tab_max_x - hot_width)
- SetDropIndex(i + 1, true);
- else
- SetDropIndex(i, false);
- return;
- }
- }
-
- // The drop isn't over a tab, add it to the end.
- SetDropIndex(GetTabCount(), true);
-}
-
-void TabStripGtk::SetDropIndex(int index, bool drop_before) {
- bool is_beneath;
- gfx::Rect drop_bounds = GetDropBounds(index, drop_before, &is_beneath);
-
- // Perform a delayed tab transition if hovering directly over a tab;
- // otherwise, cancel the pending one.
- if (index != -1 && !drop_before)
- hover_tab_selector_.StartTabTransition(index);
- else
- hover_tab_selector_.CancelTabTransition();
-
- if (!drop_info_.get()) {
- drop_info_.reset(new DropInfo(index, drop_before, !is_beneath));
- } else {
- if (!GTK_IS_WIDGET(drop_info_->container)) {
- drop_info_->CreateContainer();
- } else if (drop_info_->drop_index == index &&
- drop_info_->drop_before == drop_before) {
- return;
- }
-
- drop_info_->drop_index = index;
- drop_info_->drop_before = drop_before;
- if (is_beneath == drop_info_->point_down) {
- drop_info_->point_down = !is_beneath;
- drop_info_->drop_arrow = GetDropArrowImage(drop_info_->point_down);
- }
- }
-
- gtk_window_move(GTK_WINDOW(drop_info_->container),
- drop_bounds.x(), drop_bounds.y());
- gtk_window_resize(GTK_WINDOW(drop_info_->container),
- drop_bounds.width(), drop_bounds.height());
-}
-
-bool TabStripGtk::CompleteDrop(const guchar* data, bool is_plain_text) {
- if (!drop_info_.get())
- return false;
-
- const int drop_index = drop_info_->drop_index;
- const bool drop_before = drop_info_->drop_before;
-
- // Destroy the drop indicator.
- drop_info_.reset();
-
- // Cancel any pending tab transition.
- hover_tab_selector_.CancelTabTransition();
-
- GURL url;
- if (is_plain_text) {
- AutocompleteMatch match;
- AutocompleteClassifierFactory::GetForProfile(model_->profile())->Classify(
- base::UTF8ToUTF16(reinterpret_cast<const char*>(data)),
- false, false, AutocompleteInput::INVALID_SPEC, &match, NULL);
- url = match.destination_url;
- } else {
- std::string url_string(reinterpret_cast<const char*>(data));
- url = GURL(url_string.substr(0, url_string.find_first_of('\n')));
- }
- if (!url.is_valid())
- return false;
-
- chrome::NavigateParams params(window()->browser(), url,
- content::PAGE_TRANSITION_LINK);
- params.tabstrip_index = drop_index;
-
- if (drop_before) {
- params.disposition = NEW_FOREGROUND_TAB;
- } else {
- params.disposition = CURRENT_TAB;
- params.source_contents = model_->GetWebContentsAt(drop_index);
- }
-
- chrome::Navigate(&params);
-
- return true;
-}
-
-// static
-gfx::Image* TabStripGtk::GetDropArrowImage(bool is_down) {
- return &ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
- is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
-}
-
-// TabStripGtk::DropInfo -------------------------------------------------------
-
-TabStripGtk::DropInfo::DropInfo(int drop_index, bool drop_before,
- bool point_down)
- : drop_index(drop_index),
- drop_before(drop_before),
- point_down(point_down) {
- CreateContainer();
- drop_arrow = GetDropArrowImage(point_down);
-}
-
-TabStripGtk::DropInfo::~DropInfo() {
- DestroyContainer();
-}
-
-gboolean TabStripGtk::DropInfo::OnExposeEvent(GtkWidget* widget,
- GdkEventExpose* event) {
- TRACE_EVENT0("ui::gtk", "TabStripGtk::DropInfo::OnExposeEvent");
-
- if (ui::IsScreenComposited()) {
- SetContainerTransparency();
- } else {
- SetContainerShapeMask();
- }
-
- cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
- gdk_cairo_rectangle(cr, &event->area);
- cairo_clip(cr);
-
- drop_arrow->ToCairo()->SetSource(cr, widget, 0, 0);
- cairo_paint(cr);
-
- cairo_destroy(cr);
-
- return FALSE;
-}
-
-// Sets the color map of the container window to allow the window to be
-// transparent.
-void TabStripGtk::DropInfo::SetContainerColorMap() {
- GdkScreen* screen = gtk_widget_get_screen(container);
- GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);
-
- // If rgba is not available, use rgb instead.
- if (!colormap)
- colormap = gdk_screen_get_rgb_colormap(screen);
-
- gtk_widget_set_colormap(container, colormap);
-}
-
-// Sets full transparency for the container window. This is used if
-// compositing is available for the screen.
-void TabStripGtk::DropInfo::SetContainerTransparency() {
- cairo_t* cairo_context = gdk_cairo_create(gtk_widget_get_window(container));
- if (!cairo_context)
- return;
-
- // Make the background of the dragged tab window fully transparent. All of
- // the content of the window (child widgets) will be completely opaque.
-
- cairo_scale(cairo_context, static_cast<double>(drop_indicator_width),
- static_cast<double>(drop_indicator_height));
- cairo_set_source_rgba(cairo_context, 1.0f, 1.0f, 1.0f, 0.0f);
- cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
- cairo_paint(cairo_context);
- cairo_destroy(cairo_context);
-}
-
-// Sets the shape mask for the container window to emulate a transparent
-// container window. This is used if compositing is not available for the
-// screen.
-void TabStripGtk::DropInfo::SetContainerShapeMask() {
- // Create a 1bpp bitmap the size of |container|.
- GdkPixmap* pixmap = gdk_pixmap_new(NULL,
- drop_indicator_width,
- drop_indicator_height, 1);
- cairo_t* cairo_context = gdk_cairo_create(GDK_DRAWABLE(pixmap));
-
- // Set the transparency.
- cairo_set_source_rgba(cairo_context, 1, 1, 1, 0);
-
- // Blit the rendered bitmap into a pixmap. Any pixel set in the pixmap will
- // be opaque in the container window.
- cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
- // We don't use CairoCachedSurface::SetSource() here because we're not
- // rendering on a display server.
- gdk_cairo_set_source_pixbuf(cairo_context, drop_arrow->ToGdkPixbuf(), 0, 0);
- cairo_paint(cairo_context);
- cairo_destroy(cairo_context);
-
- // Set the shape mask.
- GdkWindow* gdk_window = gtk_widget_get_window(container);
- gdk_window_shape_combine_mask(gdk_window, pixmap, 0, 0);
- g_object_unref(pixmap);
-}
-
-void TabStripGtk::DropInfo::CreateContainer() {
- container = gtk_window_new(GTK_WINDOW_POPUP);
- SetContainerColorMap();
- gtk_widget_set_app_paintable(container, TRUE);
- g_signal_connect(container, "expose-event",
- G_CALLBACK(OnExposeEventThunk), this);
- gtk_widget_add_events(container, GDK_STRUCTURE_MASK);
- gtk_window_move(GTK_WINDOW(container), 0, 0);
- gtk_window_resize(GTK_WINDOW(container),
- drop_indicator_width, drop_indicator_height);
- gtk_widget_show_all(container);
-}
-
-void TabStripGtk::DropInfo::DestroyContainer() {
- if (GTK_IS_WIDGET(container))
- gtk_widget_destroy(container);
-}
-
-void TabStripGtk::StopAnimation() {
- if (active_animation_.get())
- active_animation_->Stop();
-}
-
-// Called from:
-// - animation tick
-void TabStripGtk::AnimationLayout(double unselected_width) {
- int tab_height = TabGtk::GetStandardSize().height();
- double tab_x = tab_start_x();
- 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);
- TabGtk* tab = GetTabAt(i);
- gfx::Rect bounds(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
- tab_height);
- SetTabBounds(tab, bounds);
- tab_x = end_of_tab + GetTabHOffset(i + 1);
- }
- LayoutNewTabButton(tab_x, unselected_width);
-}
-
-void TabStripGtk::StartInsertTabAnimation(int index) {
- // The TabStrip can now use its entire width to lay out Tabs.
- available_width_for_tabs_ = -1;
- StopAnimation();
- active_animation_.reset(new InsertTabAnimation(this, index));
- active_animation_->Start();
-}
-
-void TabStripGtk::StartRemoveTabAnimation(int index, WebContents* 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();
-}
-
-void TabStripGtk::StartMoveTabAnimation(int from_index, int to_index) {
- StopAnimation();
- active_animation_.reset(new MoveTabAnimation(this, from_index, to_index));
- active_animation_->Start();
-}
-
-void TabStripGtk::StartResizeLayoutAnimation() {
- StopAnimation();
- active_animation_.reset(new ResizeLayoutAnimation(this));
- active_animation_->Start();
-}
-
-void TabStripGtk::StartMiniTabAnimation(int index) {
- StopAnimation();
- active_animation_.reset(new MiniTabAnimation(this, index));
- active_animation_->Start();
-}
-
-void TabStripGtk::StartMiniMoveTabAnimation(int from_index,
- int to_index,
- const gfx::Rect& start_bounds) {
- StopAnimation();
- active_animation_.reset(
- new MiniMoveAnimation(this, from_index, to_index, start_bounds));
- active_animation_->Start();
-}
-
-void TabStripGtk::FinishAnimation(TabStripGtk::TabAnimation* animation,
- bool layout) {
- active_animation_.reset(NULL);
-
- // 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 TabStripGtk::OnMap(GtkWidget* widget) {
- ReStack();
-}
-
-gboolean TabStripGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event) {
- TRACE_EVENT0("ui::gtk", "TabStripGtk::OnExpose");
-
- if (gdk_region_empty(event->region))
- return TRUE;
-
- // If we're only repainting favicons, optimize the paint path and only draw
- // the favicons.
- GdkRectangle* rects;
- gint num_rects;
- gdk_region_get_rectangles(event->region, &rects, &num_rects);
- qsort(rects, num_rects, sizeof(GdkRectangle), CompareGdkRectangles);
- std::vector<int> tabs_to_repaint;
- if (!IsDragSessionActive() &&
- CanPaintOnlyFavicons(rects, num_rects, &tabs_to_repaint)) {
- PaintOnlyFavicons(event, tabs_to_repaint);
- g_free(rects);
- return TRUE;
- }
- g_free(rects);
-
- // Ideally we'd like to only draw what's needed in the damage rect, but the
- // tab widgets overlap each other. To get the proper visual look, we need to
- // draw tabs from the rightmost to the leftmost tab. So if we have a dirty
- // rectangle in the center of the tabstrip, we'll have to draw all the tabs
- // to the left of it.
- //
- // TODO(erg): Figure out why we can't have clip rects that don't start from
- // x=0. jhawkins had a big comment here about how painting on one widget will
- // cause an expose-event to be sent to the widgets underneath, but that
- // should still obey clip rects, but doesn't seem to.
- if (active_animation_.get() || drag_controller_.get()) {
- // If we have an active animation or the tab is being dragged, no matter
- // what GTK tells us our dirty rectangles are, we need to redraw the entire
- // tabstrip.
- event->area.width = bounds_.width();
- } else {
- // Expand whatever dirty rectangle we were given to the area from the
- // leftmost edge of the tabstrip to the rightmost edge of the dirty
- // rectangle given.
- //
- // Doing this frees up CPU when redrawing the tabstrip with throbbing
- // tabs. The most likely tabs to throb are pinned or minitabs which live on
- // the very leftmost of the tabstrip.
- event->area.width += event->area.x;
- }
-
- event->area.x = 0;
- event->area.y = 0;
- event->area.height = bounds_.height();
- gdk_region_union_with_rect(event->region, &event->area);
-
- // Paint the New Tab button.
- gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
- newtab_button_->widget(), event);
-
- // Paint the tabs in reverse order, so they stack to the left.
- TabGtk* selected_tab = NULL;
- int tab_count = GetTabCount();
- for (int i = tab_count - 1; i >= 0; --i) {
- TabGtk* 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 WebContents has been destroyed.
- if (!tab->IsActive()) {
- gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
- tab->widget(), event);
- } else {
- selected_tab = tab;
- }
- }
-
- // Paint the selected tab last, so it overlaps all the others.
- if (selected_tab) {
- gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
- selected_tab->widget(), event);
- }
-
- return TRUE;
-}
-
-void TabStripGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) {
- TRACE_EVENT0("ui::gtk", "TabStripGtk::OnSizeAllocate");
-
- gfx::Rect bounds = gfx::Rect(allocation->x, allocation->y,
- allocation->width, allocation->height);
-
- // Nothing to do if the bounds are the same. If we don't catch this, we'll
- // get an infinite loop of size-allocate signals.
- if (bounds_ == bounds)
- return;
-
- SetBounds(bounds);
-
- // No tabs, nothing to layout. This happens when a browser window is created
- // and shown before tabs are added (as in a popup window).
- if (GetTabCount() == 0)
- return;
-
- // When there is only one tab, Layout() so we don't animate it. With more
- // tabs, we should always attempt a resize unless we already have one coming
- // up in our message loop.
- if (GetTabCount() == 1) {
- Layout();
- } else if (!layout_factory_.HasWeakPtrs()) {
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&TabStripGtk::Layout, layout_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(kLayoutAfterSizeAllocateMs));
- }
-}
-
-gboolean TabStripGtk::OnDragMotion(GtkWidget* widget, GdkDragContext* context,
- gint x, gint y, guint time) {
- UpdateDropIndex(context, x, y);
- return TRUE;
-}
-
-gboolean TabStripGtk::OnDragDrop(GtkWidget* widget, GdkDragContext* context,
- gint x, gint y, guint time) {
- if (!drop_info_.get())
- return FALSE;
-
- GdkAtom target = gtk_drag_dest_find_target(widget, context, NULL);
- if (target != GDK_NONE)
- gtk_drag_finish(context, FALSE, FALSE, time);
- else
- gtk_drag_get_data(widget, context, target, time);
-
- return TRUE;
-}
-
-gboolean TabStripGtk::OnDragLeave(GtkWidget* widget, GdkDragContext* context,
- guint time) {
- // Destroy the drop indicator.
- drop_info_->DestroyContainer();
-
- // Cancel any pending tab transition.
- hover_tab_selector_.CancelTabTransition();
-
- return FALSE;
-}
-
-gboolean TabStripGtk::OnDragDataReceived(GtkWidget* widget,
- GdkDragContext* context,
- gint x, gint y,
- GtkSelectionData* data,
- guint info, guint time) {
- bool success = false;
-
- if (info == ui::TEXT_URI_LIST ||
- info == ui::NETSCAPE_URL ||
- info == ui::TEXT_PLAIN) {
- success = CompleteDrop(gtk_selection_data_get_data(data),
- info == ui::TEXT_PLAIN);
- }
-
- gtk_drag_finish(context, success, FALSE, time);
- return TRUE;
-}
-
-void TabStripGtk::OnNewTabClicked(GtkWidget* widget) {
- GdkEvent* event = gtk_get_current_event();
- DCHECK_EQ(event->type, GDK_BUTTON_RELEASE);
- int mouse_button = event->button.button;
- gdk_event_free(event);
-
- switch (mouse_button) {
- case 1:
- content::RecordAction(UserMetricsAction("NewTab_Button"));
- UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_BUTTON,
- TabStripModel::NEW_TAB_ENUM_COUNT);
- model_->delegate()->AddTabAt(GURL(), -1, true);
- break;
- case 2: {
- // On middle-click, try to parse the PRIMARY selection as a URL and load
- // it instead of creating a blank page.
- GURL url;
- if (!gtk_util::URLFromPrimarySelection(model_->profile(), &url))
- return;
-
- Browser* browser = window_->browser();
- DCHECK(browser);
- chrome::AddSelectedTabWithURL(
- browser, url, content::PAGE_TRANSITION_TYPED);
- break;
- }
- default:
- NOTREACHED() << "Got click on new tab button with unhandled mouse "
- << "button " << mouse_button;
- }
-}
-
-void TabStripGtk::SetTabBounds(TabGtk* tab, const gfx::Rect& bounds) {
- gfx::Rect bds = bounds;
- bds.set_x(gtk_util::MirroredLeftPointForRect(tabstrip_.get(), bounds));
- tab->SetBounds(bds);
- gtk_fixed_move(GTK_FIXED(tabstrip_.get()), tab->widget(),
- bds.x(), bds.y());
-}
-
-bool TabStripGtk::CanPaintOnlyFavicons(const GdkRectangle* rects,
- int num_rects, std::vector<int>* tabs_to_paint) {
- // |rects| are sorted so we just need to scan from left to right and compare
- // it to the tab favicon positions from left to right.
- int t = 0;
- for (int r = 0; r < num_rects; ++r) {
- while (t < GetTabCount()) {
- TabGtk* tab = GetTabAt(t);
- if (GdkRectMatchesTabFaviconBounds(rects[r], tab) &&
- tab->ShouldShowIcon()) {
- tabs_to_paint->push_back(t);
- ++t;
- break;
- }
- ++t;
- }
- }
- return static_cast<int>(tabs_to_paint->size()) == num_rects;
-}
-
-void TabStripGtk::PaintOnlyFavicons(GdkEventExpose* event,
- const std::vector<int>& tabs_to_paint) {
- cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
- for (size_t i = 0; i < tabs_to_paint.size(); ++i) {
- cairo_save(cr);
- GetTabAt(tabs_to_paint[i])->PaintFaviconArea(tabstrip_.get(), cr);
- cairo_restore(cr);
- }
-
- cairo_destroy(cr);
-}
-
-CustomDrawButton* TabStripGtk::MakeNewTabButton() {
- CustomDrawButton* button = new CustomDrawButton(IDR_NEWTAB_BUTTON,
- IDR_NEWTAB_BUTTON_P, IDR_NEWTAB_BUTTON_H, 0);
-
- gtk_widget_set_tooltip_text(button->widget(),
- l10n_util::GetStringUTF8(IDS_TOOLTIP_NEW_TAB).c_str());
-
- // Let the middle mouse button initiate clicks as well.
- gtk_util::SetButtonTriggersNavigation(button->widget());
- g_signal_connect(button->widget(), "clicked",
- G_CALLBACK(OnNewTabClickedThunk), this);
- gtk_widget_set_can_focus(button->widget(), FALSE);
- gtk_fixed_put(GTK_FIXED(tabstrip_.get()), button->widget(), 0, 0);
-
- return button;
-}
-
-void TabStripGtk::SetNewTabButtonBackground() {
- SkColor color = theme_service_->GetColor(
- ThemeProperties::COLOR_BUTTON_BACKGROUND);
- SkBitmap background = theme_service_->GetImageNamed(
- IDR_THEME_WINDOW_CONTROL_BACKGROUND).AsBitmap();
- SkBitmap mask = theme_service_->GetImageNamed(
- IDR_NEWTAB_BUTTON_MASK).AsBitmap();
- newtab_button_->SetBackground(color, background, mask);
-}

Powered by Google App Engine
This is Rietveld 408576698