| 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(¶ms);
|
| -
|
| - 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);
|
| -}
|
|
|