summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/tabs/tab_strip.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/tabs/tab_strip.cc')
-rw-r--r--chrome/browser/views/tabs/tab_strip.cc982
1 files changed, 0 insertions, 982 deletions
diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc
deleted file mode 100644
index 9b25dfa..0000000
--- a/chrome/browser/views/tabs/tab_strip.cc
+++ /dev/null
@@ -1,982 +0,0 @@
-// Copyright (c) 2010 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/views/tabs/tab_strip.h"
-
-#include "app/animation_container.h"
-#include "app/drag_drop_types.h"
-#include "app/l10n_util.h"
-#include "app/resource_bundle.h"
-#include "base/compiler_specific.h"
-#include "base/stl_util-inl.h"
-#include "chrome/browser/browser.h"
-#include "chrome/browser/defaults.h"
-#include "chrome/browser/themes/browser_theme_provider.h"
-#include "chrome/browser/view_ids.h"
-#include "chrome/browser/views/tabs/tab.h"
-#include "chrome/browser/views/tabs/tab_strip_controller.h"
-#include "chrome/common/pref_names.h"
-#include "gfx/canvas_skia.h"
-#include "gfx/path.h"
-#include "gfx/size.h"
-#include "grit/generated_resources.h"
-#include "grit/theme_resources.h"
-#include "views/controls/image_view.h"
-#include "views/widget/default_theme_provider.h"
-#include "views/window/non_client_view.h"
-#include "views/window/window.h"
-
-#if defined(OS_WIN)
-#include "app/win_util.h"
-#include "views/widget/widget_win.h"
-#elif defined(OS_LINUX)
-#include "views/widget/widget_gtk.h"
-#endif
-
-#undef min
-#undef max
-
-#if defined(COMPILER_GCC)
-// Squash false positive signed overflow warning in GenerateStartAndEndWidths
-// when doing 'start_tab_count < end_tab_count'.
-#pragma GCC diagnostic ignored "-Wstrict-overflow"
-#endif
-
-using views::DropTargetEvent;
-
-static const int kNewTabButtonHOffset = -5;
-static const int kNewTabButtonVOffset = 5;
-static const int kSuspendAnimationsTimeMs = 200;
-static const int kTabHOffset = -16;
-static const int kTabStripAnimationVSlop = 40;
-
-// Size of the drop indicator.
-static int drop_indicator_width;
-static int drop_indicator_height;
-
-static inline int Round(double x) {
- // Why oh why is this not in a standard header?
- return static_cast<int>(floor(x + 0.5));
-}
-
-namespace {
-
-///////////////////////////////////////////////////////////////////////////////
-// NewTabButton
-//
-// A subclass of button that hit-tests to the shape of the new tab button.
-
-class NewTabButton : public views::ImageButton {
- public:
- explicit NewTabButton(views::ButtonListener* listener)
- : views::ImageButton(listener) {
- }
- virtual ~NewTabButton() {}
-
- protected:
- // Overridden from views::View:
- virtual bool HasHitTestMask() const {
- // When the button is sized to the top of the tab strip we want the user to
- // be able to click on complete bounds, and so don't return a custom hit
- // mask.
- return !browser_defaults::kSizeTabButtonToTopOfTabStrip;
- }
- virtual void GetHitTestMask(gfx::Path* path) const {
- DCHECK(path);
-
- SkScalar w = SkIntToScalar(width());
-
- // These values are defined by the shape of the new tab bitmap. Should that
- // bitmap ever change, these values will need to be updated. They're so
- // custom it's not really worth defining constants for.
- path->moveTo(0, 1);
- path->lineTo(w - 7, 1);
- path->lineTo(w - 4, 4);
- path->lineTo(w, 16);
- path->lineTo(w - 1, 17);
- path->lineTo(7, 17);
- path->lineTo(4, 13);
- path->lineTo(0, 1);
- path->close();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NewTabButton);
-};
-
-} // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStrip, public:
-
-// static
-const int TabStrip::mini_to_non_mini_gap_ = 3;
-
-TabStrip::TabStrip(TabStripController* controller)
- : BaseTabStrip(controller, BaseTabStrip::HORIZONTAL_TAB_STRIP),
- current_unselected_width_(Tab::GetStandardSize().width()),
- current_selected_width_(Tab::GetStandardSize().width()),
- available_width_for_tabs_(-1),
- in_tab_close_(false),
- animation_container_(new AnimationContainer()) {
- Init();
-}
-
-TabStrip::~TabStrip() {
- // The animations may reference the tabs. Shut down the animation before we
- // delete the tabs.
- StopAnimating(false);
-
- DestroyDragController();
-
- // Make sure we unhook ourselves as a message loop observer so that we don't
- // crash in the case where the user closes the window after closing a tab
- // but before moving the mouse.
- RemoveMessageLoopObserver();
-
- // The children (tabs) may callback to us from their destructor. Delete them
- // so that if they call back we aren't in a weird state.
- RemoveAllChildViews(true);
-}
-
-void TabStrip::InitTabStripButtons() {
- newtab_button_ = new NewTabButton(this);
- if (browser_defaults::kSizeTabButtonToTopOfTabStrip) {
- newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
- views::ImageButton::ALIGN_BOTTOM);
- }
- LoadNewTabButtonImage();
- newtab_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_NEWTAB));
- AddChildView(newtab_button_);
-}
-
-gfx::Rect TabStrip::GetNewTabButtonBounds() {
- return newtab_button_->bounds();
-}
-
-void TabStrip::MouseMovedOutOfView() {
- ResizeLayoutTabs();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TabStrip, BaseTabStrip implementation:
-
-int TabStrip::GetPreferredHeight() {
- return GetPreferredSize().height();
-}
-
-void TabStrip::SetBackgroundOffset(const gfx::Point& offset) {
- for (int i = 0; i < tab_count(); ++i)
- GetTabAtTabDataIndex(i)->SetBackgroundOffset(offset);
-}
-
-bool TabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
- views::View* v = GetViewForPoint(point);
-
- // If there is no control at this location, claim the hit was in the title
- // bar to get a move action.
- if (v == this)
- return true;
-
- // Check to see if the point is within the non-button parts of the new tab
- // button. The button has a non-rectangular shape, so if it's not in the
- // visual portions of the button we treat it as a click to the caption.
- gfx::Point point_in_newtab_coords(point);
- View::ConvertPointToView(this, newtab_button_, &point_in_newtab_coords);
- if (newtab_button_->bounds().Contains(point) &&
- !newtab_button_->HitTest(point_in_newtab_coords)) {
- return true;
- }
-
- // All other regions, including the new Tab button, should be considered part
- // of the containing Window's client area so that regular events can be
- // processed for them.
- return false;
-}
-
-void TabStrip::SetDraggedTabBounds(int tab_index, const gfx::Rect& tab_bounds) {
-}
-
-TabStrip* TabStrip::AsTabStrip() {
- return this;
-}
-
-void TabStrip::PrepareForCloseAt(int model_index) {
- if (!in_tab_close_ && IsAnimating()) {
- // Cancel any current animations. We do this as remove uses the current
- // ideal bounds and we need to know ideal bounds is in a good state.
- StopAnimating(true);
- }
-
- int model_count = GetModelCount();
- if (model_index + 1 != model_count && model_count > 1) {
- // The user is about to close a tab other than the last tab. Set
- // available_width_for_tabs_ so that if we do a layout we don't position a
- // tab past the end of the second to last tab. We do this so that as the
- // user closes tabs with the mouse a tab continues to fall under the mouse.
- available_width_for_tabs_ = GetAvailableWidthForTabs(
- GetTabAtModelIndex(model_count - 2));
- }
-
- in_tab_close_ = true;
- AddMessageLoopObserver();
-}
-
-void TabStrip::RemoveTabAt(int model_index) {
- if (in_tab_close_ && model_index != GetModelCount())
- StartMouseInitiatedRemoveTabAnimation(model_index);
- else
- StartRemoveTabAnimation(model_index);
-}
-
-void TabStrip::SelectTabAt(int old_model_index, int new_model_index) {
- // 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() && (!in_tab_close_ || tiny_tabs)) {
- DoLayout();
- } else {
- SchedulePaint();
- }
-
- if (old_model_index >= 0) {
- GetTabAtTabDataIndex(ModelIndexToTabIndex(old_model_index))->
- StopMiniTabTitleAnimation();
- }
-}
-
-void TabStrip::TabTitleChangedNotLoading(int model_index) {
- Tab* tab = GetTabAtModelIndex(model_index);
- if (tab->data().mini && !tab->IsSelected())
- tab->StartMiniTabTitleAnimation();
-}
-
-void TabStrip::StartHighlight(int model_index) {
- GetTabAtModelIndex(model_index)->StartPulse();
-}
-
-void TabStrip::StopAllHighlighting() {
- for (int i = 0; i < tab_count(); ++i)
- GetTabAtTabDataIndex(i)->StopPulse();
-}
-
-BaseTab* TabStrip::CreateTabForDragging() {
- Tab* tab = new Tab(NULL);
- // Make sure the dragged tab shares our theme provider. We need to explicitly
- // do this as during dragging there isn't a theme provider.
- tab->set_theme_provider(GetThemeProvider());
- return tab;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStrip, views::View overrides:
-
-void TabStrip::PaintChildren(gfx::Canvas* canvas) {
- // Tabs are painted in reverse order, so they stack to the left.
- Tab* selected_tab = NULL;
- Tab* dragging_tab = NULL;
-
- for (int i = tab_count() - 1; i >= 0; --i) {
- Tab* tab = GetTabAtTabDataIndex(i);
- // We must ask the _Tab's_ model, not ourselves, because in some situations
- // the model will be different to this object, e.g. when a Tab is being
- // removed after its TabContents has been destroyed.
- if (tab->dragging()) {
- dragging_tab = tab;
- } else if (!tab->IsSelected()) {
- tab->ProcessPaint(canvas);
- } else {
- selected_tab = tab;
- }
- }
-
- if (GetWindow()->GetNonClientView()->UseNativeFrame()) {
- // Make sure unselected tabs are somewhat transparent.
- SkPaint paint;
- paint.setColor(SkColorSetARGB(200, 255, 255, 255));
- paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
- paint.setStyle(SkPaint::kFill_Style);
- canvas->DrawRectInt(0, 0, width(),
- height() - 2, // Visible region that overlaps the toolbar.
- paint);
- }
-
- // Paint the selected tab last, so it overlaps all the others.
- if (selected_tab)
- selected_tab->ProcessPaint(canvas);
-
- // Paint the New Tab button.
- newtab_button_->ProcessPaint(canvas);
-
- // And the dragged tab.
- if (dragging_tab)
- dragging_tab->ProcessPaint(canvas);
-}
-
-// Overridden to support automation. See automation_proxy_uitest.cc.
-views::View* TabStrip::GetViewByID(int view_id) const {
- if (tab_count() > 0) {
- if (view_id == VIEW_ID_TAB_LAST) {
- return GetTabAtTabDataIndex(tab_count() - 1);
- } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
- int index = view_id - VIEW_ID_TAB_0;
- if (index >= 0 && index < tab_count()) {
- return GetTabAtTabDataIndex(index);
- } else {
- return NULL;
- }
- }
- }
-
- return View::GetViewByID(view_id);
-}
-
-gfx::Size TabStrip::GetPreferredSize() {
- return gfx::Size(0, Tab::GetMinimumUnselectedSize().height());
-}
-
-void TabStrip::OnDragEntered(const DropTargetEvent& event) {
- // Force animations to stop, otherwise it makes the index calculation tricky.
- StopAnimating(true);
-
- UpdateDropIndex(event);
-}
-
-int TabStrip::OnDragUpdated(const DropTargetEvent& event) {
- UpdateDropIndex(event);
- return GetDropEffect(event);
-}
-
-void TabStrip::OnDragExited() {
- SetDropIndex(-1, false);
-}
-
-int TabStrip::OnPerformDrop(const DropTargetEvent& event) {
- if (!drop_info_.get())
- return DragDropTypes::DRAG_NONE;
-
- const int drop_index = drop_info_->drop_index;
- const bool drop_before = drop_info_->drop_before;
-
- // Hide the drop indicator.
- SetDropIndex(-1, false);
-
- GURL url;
- std::wstring title;
- if (!event.GetData().GetURLAndTitle(&url, &title) || !url.is_valid())
- return DragDropTypes::DRAG_NONE;
-
- controller()->PerformDrop(drop_before, drop_index, url);
-
- return GetDropEffect(event);
-}
-
-AccessibilityTypes::Role TabStrip::GetAccessibleRole() {
- return AccessibilityTypes::ROLE_PAGETABLIST;
-}
-
-views::View* TabStrip::GetViewForPoint(const gfx::Point& point) {
- // Return any view that isn't a Tab or this TabStrip immediately. We don't
- // want to interfere.
- views::View* v = View::GetViewForPoint(point);
- if (v && v != this && v->GetClassName() != Tab::kViewClassName)
- return v;
-
- // The display order doesn't necessarily match the child list order, so we
- // walk the display list hit-testing Tabs. Since the selected tab always
- // renders on top of adjacent tabs, it needs to be hit-tested before any
- // left-adjacent Tab, so we look ahead for it as we walk.
- for (int i = 0; i < tab_count(); ++i) {
- Tab* next_tab = i < (tab_count() - 1) ? GetTabAtTabDataIndex(i + 1) : NULL;
- if (next_tab && next_tab->IsSelected() && IsPointInTab(next_tab, point))
- return next_tab;
- Tab* tab = GetTabAtTabDataIndex(i);
- if (IsPointInTab(tab, point))
- return tab;
- }
-
- // No need to do any floating view stuff, we don't use them in the TabStrip.
- return this;
-}
-
-void TabStrip::OnThemeChanged() {
- LoadNewTabButtonImage();
-}
-
-BaseTab* TabStrip::CreateTab() {
- Tab* tab = new Tab(this);
- tab->set_animation_container(animation_container_.get());
- return tab;
-}
-
-void TabStrip::StartInsertTabAnimation(int model_index, bool foreground) {
- PrepareForAnimation();
-
- // The TabStrip can now use its entire width to lay out Tabs.
- in_tab_close_ = false;
- available_width_for_tabs_ = -1;
-
- GenerateIdealBounds();
-
- int tab_data_index = ModelIndexToTabIndex(model_index);
- BaseTab* tab = base_tab_at_tab_index(tab_data_index);
- if (model_index == 0) {
- tab->SetBounds(0, ideal_bounds(tab_data_index).y(), 0,
- ideal_bounds(tab_data_index).height());
- } else {
- BaseTab* last_tab = base_tab_at_tab_index(tab_data_index - 1);
- tab->SetBounds(last_tab->bounds().right() + kTabHOffset,
- ideal_bounds(tab_data_index).y(), 0,
- ideal_bounds(tab_data_index).height());
- }
-
- AnimateToIdealBounds();
-}
-
-void TabStrip::StartMoveTabAnimation() {
- PrepareForAnimation();
-
- GenerateIdealBounds();
- AnimateToIdealBounds();
-}
-
-void TabStrip::AnimateToIdealBounds() {
- for (int i = 0; i < tab_count(); ++i) {
- Tab* tab = GetTabAtTabDataIndex(i);
- if (!tab->closing() && !tab->dragging())
- bounds_animator().AnimateViewTo(tab, ideal_bounds(i));
- }
-
- bounds_animator().AnimateViewTo(newtab_button_, newtab_button_bounds_);
-}
-
-bool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
- return in_tab_close_;
-}
-
-void TabStrip::DoLayout() {
- BaseTabStrip::DoLayout();
-
- newtab_button_->SetBounds(newtab_button_bounds_);
-}
-
-void TabStrip::ViewHierarchyChanged(bool is_add,
- views::View* parent,
- views::View* child) {
- if (is_add && child == this)
- InitTabStripButtons();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStrip, Tab::Delegate implementation:
-
-bool TabStrip::IsTabSelected(const BaseTab* btr) const {
- const Tab* tab = static_cast<const Tab*>(btr);
- return !tab->closing() && BaseTabStrip::IsTabSelected(btr);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStrip, views::BaseButton::ButtonListener implementation:
-
-void TabStrip::ButtonPressed(views::Button* sender, const views::Event& event) {
- if (sender == newtab_button_)
- controller()->CreateNewTab();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabStrip, private:
-
-void TabStrip::Init() {
- SetID(VIEW_ID_TAB_STRIP);
- newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight);
- if (browser_defaults::kSizeTabButtonToTopOfTabStrip) {
- newtab_button_bounds_.set_height(
- kNewTabButtonHeight + kNewTabButtonVOffset);
- }
- if (drop_indicator_width == 0) {
- // Direction doesn't matter, both images are the same size.
- SkBitmap* drop_image = GetDropArrowImage(true);
- drop_indicator_width = drop_image->width();
- drop_indicator_height = drop_image->height();
- }
-}
-
-void TabStrip::LoadNewTabButtonImage() {
- ThemeProvider* tp = GetThemeProvider();
-
- // If we don't have a theme provider yet, it means we do not have a
- // root view, and are therefore in a test.
- bool in_test = false;
- if (tp == NULL) {
- tp = new views::DefaultThemeProvider();
- in_test = true;
- }
-
- SkBitmap* bitmap = tp->GetBitmapNamed(IDR_NEWTAB_BUTTON);
- SkColor color = tp->GetColor(BrowserThemeProvider::COLOR_BUTTON_BACKGROUND);
- SkBitmap* background = tp->GetBitmapNamed(
- IDR_THEME_WINDOW_CONTROL_BACKGROUND);
-
- newtab_button_->SetImage(views::CustomButton::BS_NORMAL, bitmap);
- newtab_button_->SetImage(views::CustomButton::BS_PUSHED,
- tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_P));
- newtab_button_->SetImage(views::CustomButton::BS_HOT,
- tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_H));
- newtab_button_->SetBackground(color, background,
- tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_MASK));
- if (in_test)
- delete tp;
-}
-
-Tab* TabStrip::GetTabAtTabDataIndex(int tab_data_index) const {
- return static_cast<Tab*>(base_tab_at_tab_index(tab_data_index));
-}
-
-Tab* TabStrip::GetTabAtModelIndex(int model_index) const {
- return GetTabAtTabDataIndex(ModelIndexToTabIndex(model_index));
-}
-
-void TabStrip::GetCurrentTabWidths(double* unselected_width,
- double* selected_width) const {
- *unselected_width = current_unselected_width_;
- *selected_width = current_selected_width_;
-}
-
-void TabStrip::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 = Tab::GetMinimumUnselectedSize().width();
- const double min_selected_width = Tab::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.
- int available_width;
- if (available_width_for_tabs_ < 0) {
- available_width = width();
- available_width -= (kNewTabButtonHOffset + newtab_button_bounds_.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 * (Tab::GetMiniWidth() + kTabHOffset);
- tab_count -= mini_tab_count;
- if (tab_count == 0) {
- *selected_width = *unselected_width = Tab::GetStandardSize().width();
- return;
- }
- // Account for gap between the last mini-tab and first non-mini-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>(Tab::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)) {
- // Unselected width = (total width - selected width) / (num_tabs - 1)
- *unselected_width = std::max(static_cast<double>(
- available_width - total_offset - min_selected_width) /
- static_cast<double>(tab_count - 1), min_unselected_width);
- } else if ((min_unselected_width > min_selected_width) &&
- (desired_tab_width < min_unselected_width)) {
- // Selected width = (total width - (unselected width * (num_tabs - 1)))
- *selected_width = std::max(available_width - total_offset -
- (min_unselected_width * (tab_count - 1)), min_selected_width);
- }
- }
-}
-
-void TabStrip::ResizeLayoutTabs() {
- // We've been called back after the TabStrip has been emptied out (probably
- // just prior to the window being destroyed). We need to do nothing here or
- // else GetTabAt below will crash.
- if (tab_count() == 0)
- return;
-
- // It is critically important that this is unhooked here, otherwise we will
- // keep spying on messages forever.
- RemoveMessageLoopObserver();
-
- in_tab_close_ = false;
- available_width_for_tabs_ = -1;
- int mini_tab_count = GetMiniTabCount();
- if (mini_tab_count == tab_count()) {
- // 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;
- }
- Tab* first_tab = GetTabAtTabDataIndex(mini_tab_count);
- double unselected, selected;
- GetDesiredTabWidths(tab_count(), mini_tab_count, &unselected, &selected);
- int w = Round(first_tab->IsSelected() ? selected : selected);
-
- // We only want to run the animation if we're not already at the desired
- // size.
- if (abs(first_tab->width() - w) > 1)
- StartResizeLayoutAnimation();
-}
-
-void TabStrip::AddMessageLoopObserver() {
- if (!mouse_watcher_.get()) {
- mouse_watcher_.reset(
- new views::MouseWatcher(this, this,
- gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)));
- }
- mouse_watcher_->Start();
-}
-
-void TabStrip::RemoveMessageLoopObserver() {
- mouse_watcher_.reset(NULL);
-}
-
-gfx::Rect TabStrip::GetDropBounds(int drop_index,
- bool drop_before,
- bool* is_beneath) {
- DCHECK(drop_index != -1);
- int center_x;
- if (drop_index < tab_count()) {
- Tab* tab = GetTabAtTabDataIndex(drop_index);
- if (drop_before)
- center_x = tab->x() - (kTabHOffset / 2);
- else
- center_x = tab->x() + (tab->width() / 2);
- } else {
- Tab* last_tab = GetTabAtTabDataIndex(drop_index - 1);
- center_x = last_tab->x() + last_tab->width() + (kTabHOffset / 2);
- }
-
- // Mirror the center point if necessary.
- center_x = MirroredXCoordinateInsideView(center_x);
-
- // Determine the screen bounds.
- gfx::Point drop_loc(center_x - drop_indicator_width / 2,
- -drop_indicator_height);
- ConvertPointToScreen(this, &drop_loc);
- gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
- drop_indicator_height);
-
- // If the rect doesn't fit on the monitor, push the arrow to the bottom.
-#if defined(OS_WIN)
- gfx::Rect monitor_bounds = win_util::GetMonitorBoundsForRect(drop_bounds);
- *is_beneath = (monitor_bounds.IsEmpty() ||
- !monitor_bounds.Contains(drop_bounds));
-#else
- *is_beneath = false;
- NOTIMPLEMENTED();
-#endif
- if (*is_beneath)
- drop_bounds.Offset(0, drop_bounds.height() + height());
-
- return drop_bounds;
-}
-
-void TabStrip::UpdateDropIndex(const DropTargetEvent& event) {
- // 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.
- const int x = MirroredXCoordinateInsideView(event.x());
- // We don't allow replacing the urls of mini-tabs.
- for (int i = GetMiniTabCount(); i < tab_count(); ++i) {
- Tab* tab = GetTabAtTabDataIndex(i);
- const int tab_max_x = tab->x() + tab->width();
- const int hot_width = tab->width() / 3;
- if (x < tab_max_x) {
- if (x < tab->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(tab_count(), true);
-}
-
-void TabStrip::SetDropIndex(int tab_data_index, bool drop_before) {
- if (tab_data_index == -1) {
- if (drop_info_.get())
- drop_info_.reset(NULL);
- return;
- }
-
- if (drop_info_.get() && drop_info_->drop_index == tab_data_index &&
- drop_info_->drop_before == drop_before) {
- return;
- }
-
- bool is_beneath;
- gfx::Rect drop_bounds = GetDropBounds(tab_data_index, drop_before,
- &is_beneath);
-
- if (!drop_info_.get()) {
- drop_info_.reset(new DropInfo(tab_data_index, drop_before, !is_beneath));
- } else {
- drop_info_->drop_index = tab_data_index;
- drop_info_->drop_before = drop_before;
- if (is_beneath == drop_info_->point_down) {
- drop_info_->point_down = !is_beneath;
- drop_info_->arrow_view->SetImage(
- GetDropArrowImage(drop_info_->point_down));
- }
- }
-
- // Reposition the window. Need to show it too as the window is initially
- // hidden.
-
-#if defined(OS_WIN)
- drop_info_->arrow_window->SetWindowPos(
- HWND_TOPMOST, drop_bounds.x(), drop_bounds.y(), drop_bounds.width(),
- drop_bounds.height(), SWP_NOACTIVATE | SWP_SHOWWINDOW);
-#else
- drop_info_->arrow_window->SetBounds(drop_bounds);
- drop_info_->arrow_window->Show();
-#endif
-}
-
-int TabStrip::GetDropEffect(const views::DropTargetEvent& event) {
- const int source_ops = event.GetSourceOperations();
- if (source_ops & DragDropTypes::DRAG_COPY)
- return DragDropTypes::DRAG_COPY;
- if (source_ops & DragDropTypes::DRAG_LINK)
- return DragDropTypes::DRAG_LINK;
- return DragDropTypes::DRAG_MOVE;
-}
-
-// static
-SkBitmap* TabStrip::GetDropArrowImage(bool is_down) {
- return ResourceBundle::GetSharedInstance().GetBitmapNamed(
- is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
-}
-
-// TabStrip::DropInfo ----------------------------------------------------------
-
-TabStrip::DropInfo::DropInfo(int drop_index, bool drop_before, bool point_down)
- : drop_index(drop_index),
- drop_before(drop_before),
- point_down(point_down) {
- arrow_view = new views::ImageView;
- arrow_view->SetImage(GetDropArrowImage(point_down));
-
-#if defined(OS_WIN)
- arrow_window = new views::WidgetWin;
- arrow_window->set_window_style(WS_POPUP);
- arrow_window->set_window_ex_style(WS_EX_TOPMOST | WS_EX_NOACTIVATE |
- WS_EX_LAYERED | WS_EX_TRANSPARENT);
-#else
- arrow_window = new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP);
- arrow_window->MakeTransparent();
-#endif
- arrow_window->Init(
- NULL,
- gfx::Rect(0, 0, drop_indicator_width, drop_indicator_height));
- arrow_window->SetContentsView(arrow_view);
-}
-
-TabStrip::DropInfo::~DropInfo() {
- // Close eventually deletes the window, which deletes arrow_view too.
- arrow_window->Close();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// Called from:
-// - BasicLayout
-// - Tab insertion/removal
-// - Tab reorder
-void TabStrip::GenerateIdealBounds() {
- int non_closing_tab_count = 0;
- int mini_tab_count = 0;
- for (int i = 0; i < tab_count(); ++i) {
- BaseTab* tab = base_tab_at_tab_index(i);
- if (!tab->closing()) {
- ++non_closing_tab_count;
- if (tab->data().mini)
- mini_tab_count++;
- }
- }
-
- double unselected, selected;
- GetDesiredTabWidths(non_closing_tab_count, mini_tab_count, &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 = Tab::GetStandardSize().height();
- double tab_x = 0;
- bool last_was_mini = false;
- for (int i = 0; i < tab_count(); ++i) {
- Tab* tab = GetTabAtTabDataIndex(i);
- if (!tab->closing()) {
- double tab_width = unselected;
- if (tab->data().mini) {
- tab_width = Tab::GetMiniWidth();
- } else {
- if (last_was_mini) {
- // Give a bigger gap between mini and non-mini tabs.
- tab_x += mini_to_non_mini_gap_;
- }
- if (tab->IsSelected())
- tab_width = selected;
- }
- double end_of_tab = tab_x + tab_width;
- int rounded_tab_x = Round(tab_x);
- set_ideal_bounds(i,
- gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
- tab_height));
- tab_x = end_of_tab + kTabHOffset;
- last_was_mini = tab->data().mini;
- }
- }
-
- // Update bounds of new tab button.
- int new_tab_x;
- int new_tab_y = browser_defaults::kSizeTabButtonToTopOfTabStrip ?
- 0 : kNewTabButtonVOffset;
- if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 &&
- !in_tab_close_) {
- // We're shrinking tabs, so we need to anchor the New Tab button to the
- // right edge of the TabStrip's bounds, rather than the right edge of the
- // right-most Tab, otherwise it'll bounce when animating.
- new_tab_x = width() - newtab_button_bounds_.width();
- } else {
- new_tab_x = Round(tab_x - kTabHOffset) + kNewTabButtonHOffset;
- }
- newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
-}
-
-void TabStrip::StartResizeLayoutAnimation() {
- PrepareForAnimation();
- GenerateIdealBounds();
- AnimateToIdealBounds();
-}
-
-void TabStrip::StartMiniTabAnimation() {
- in_tab_close_ = false;
- available_width_for_tabs_ = -1;
-
- PrepareForAnimation();
-
- GenerateIdealBounds();
- AnimateToIdealBounds();
-}
-
-void TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) {
- // The user initiated the close. We want to persist the bounds of all the
- // existing tabs, so we manually shift ideal_bounds then animate.
- int tab_data_index = ModelIndexToTabIndex(model_index);
- DCHECK(tab_data_index != tab_count());
- BaseTab* tab_closing = base_tab_at_tab_index(tab_data_index);
- int delta = tab_closing->width() + kTabHOffset;
- if (tab_closing->data().mini && model_index + 1 < GetModelCount() &&
- !GetBaseTabAtModelIndex(model_index + 1)->data().mini) {
- delta += mini_to_non_mini_gap_;
- }
-
- for (int i = tab_data_index + 1; i < tab_count(); ++i) {
- BaseTab* tab = base_tab_at_tab_index(i);
- if (!tab->closing()) {
- gfx::Rect bounds = ideal_bounds(i);
- bounds.set_x(bounds.x() - delta);
- set_ideal_bounds(i, bounds);
- }
- }
-
- newtab_button_bounds_.set_x(newtab_button_bounds_.x() - delta);
-
- PrepareForAnimation();
-
- // Mark the tab as closing.
- tab_closing->set_closing(true);
-
- AnimateToIdealBounds();
-
- gfx::Rect tab_bounds = tab_closing->bounds();
- if (type() == HORIZONTAL_TAB_STRIP)
- tab_bounds.set_width(0);
- else
- tab_bounds.set_height(0);
- bounds_animator().AnimateViewTo(tab_closing, tab_bounds);
-
- // Register delegate to do cleanup when done, BoundsAnimator takes
- // ownership of RemoveTabDelegate.
- bounds_animator().SetAnimationDelegate(tab_closing,
- CreateRemoveTabDelegate(tab_closing),
- true);
-}
-
-void TabStrip::StopAnimating(bool layout) {
- if (!IsAnimating())
- return;
-
- bounds_animator().Cancel();
-
- DCHECK(!IsAnimating());
-
- if (layout)
- DoLayout();
-}
-
-int TabStrip::GetMiniTabCount() const {
- int mini_count = 0;
- for (int i = 0; i < tab_count(); ++i) {
- if (base_tab_at_tab_index(i)->data().mini)
- mini_count++;
- else
- return mini_count;
- }
- return mini_count;
-}
-
-int TabStrip::GetAvailableWidthForTabs(Tab* last_tab) const {
- return last_tab->x() + last_tab->width();
-}
-
-bool TabStrip::IsPointInTab(Tab* tab,
- const gfx::Point& point_in_tabstrip_coords) {
- gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
- View::ConvertPointToView(this, tab, &point_in_tab_coords);
- return tab->HitTest(point_in_tab_coords);
-}