diff options
Diffstat (limited to 'chrome/browser/views/tabs/tab_strip.cc')
-rw-r--r-- | chrome/browser/views/tabs/tab_strip.cc | 982 |
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); -} |