172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/tabs/tab_strip.h" 672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include <algorithm> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/compiler_specific.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/stl_util-inl.h" 113f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/utf_string_conversions.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/defaults.h" 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service.h" 1421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/view_ids.h" 1521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/tabs/tab.h" 1621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/tabs/tab_strip_controller.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h" 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/accessibility/accessible_view_state.h" 203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/animation_container.h" 2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/dragdrop/drag_drop_types.h" 2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h" 2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h" 2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/path.h" 2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/size.h" 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/image_view.h" 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/default_theme_provider.h" 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/non_client_view.h" 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/window.h" 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "views/widget/monitor_win.h" 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_win.h" 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#elif defined(OS_LINUX) 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_gtk.h" 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#undef min 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#undef max 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(COMPILER_GCC) 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Squash false positive signed overflow warning in GenerateStartAndEndWidths 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// when doing 'start_tab_count < end_tab_count'. 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#pragma GCC diagnostic ignored "-Wstrict-overflow" 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing views::DropTargetEvent; 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kNewTabButtonHOffset = -5; 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kNewTabButtonVOffset = 5; 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kSuspendAnimationsTimeMs = 200; 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kTabHOffset = -16; 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kTabStripAnimationVSlop = 40; 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Size of the drop indicator. 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int drop_indicator_width; 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int drop_indicator_height; 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic inline int Round(double x) { 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Why oh why is this not in a standard header? 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return static_cast<int>(floor(x + 0.5)); 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NewTabButton 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A subclass of button that hit-tests to the shape of the new tab button. 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass NewTabButton : public views::ImageButton { 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public: 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch explicit NewTabButton(views::ButtonListener* listener) 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : views::ImageButton(listener) { 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual ~NewTabButton() {} 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected: 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Overridden from views::View: 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual bool HasHitTestMask() const { 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // When the button is sized to the top of the tab strip we want the user to 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // be able to click on complete bounds, and so don't return a custom hit 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // mask. 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return !browser_defaults::kSizeTabButtonToTopOfTabStrip; 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual void GetHitTestMask(gfx::Path* path) const { 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(path); 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SkScalar w = SkIntToScalar(width()); 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // These values are defined by the shape of the new tab bitmap. Should that 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // bitmap ever change, these values will need to be updated. They're so 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // custom it's not really worth defining constants for. 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->moveTo(0, 1); 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(w - 7, 1); 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(w - 4, 4); 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(w, 16); 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(w - 1, 17); 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(7, 17); 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(4, 13); 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->lineTo(0, 1); 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path->close(); 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private: 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DISALLOW_COPY_AND_ASSIGN(NewTabButton); 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, public: 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int TabStrip::mini_to_non_mini_gap_ = 3; 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::TabStrip(TabStripController* controller) 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : BaseTabStrip(controller, BaseTabStrip::HORIZONTAL_TAB_STRIP), 120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newtab_button_(NULL), 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_unselected_width_(Tab::GetStandardSize().width()), 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_selected_width_(Tab::GetStandardSize().width()), 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width_for_tabs_(-1), 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_tab_close_(false), 1253f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen animation_container_(new ui::AnimationContainer()) { 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Init(); 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::~TabStrip() { 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The animations may reference the tabs. Shut down the animation before we 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // delete the tabs. 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StopAnimating(false); 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DestroyDragController(); 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Make sure we unhook ourselves as a message loop observer so that we don't 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // crash in the case where the user closes the window after closing a tab 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // but before moving the mouse. 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RemoveMessageLoopObserver(); 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The children (tabs) may callback to us from their destructor. Delete them 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so that if they call back we aren't in a weird state. 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RemoveAllChildViews(true); 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::InitTabStripButtons() { 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_ = new NewTabButton(this); 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browser_defaults::kSizeTabButtonToTopOfTabStrip) { 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch views::ImageButton::ALIGN_BOTTOM); 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LoadNewTabButtonImage(); 1533f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen newtab_button_->SetAccessibleName( 15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringUTF16(IDS_ACCNAME_NEWTAB)); 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AddChildView(newtab_button_); 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect TabStrip::GetNewTabButtonBounds() { 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return newtab_button_->bounds(); 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid TabStrip::MouseMovedOutOfView() { 1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ResizeLayoutTabs(); 1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//////////////////////////////////////////////////////////////////////////////// 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TabStrip, AbstractTabStripView implementation: 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStrip::IsPositionInWindowCaption(const gfx::Point& point) { 170dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen views::View* v = GetEventHandlerForPoint(point); 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there is no control at this location, claim the hit was in the title 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // bar to get a move action. 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (v == this) 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Check to see if the point is within the non-button parts of the new tab 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // button. The button has a non-rectangular shape, so if it's not in the 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // visual portions of the button we treat it as a click to the caption. 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Point point_in_newtab_coords(point); 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch View::ConvertPointToView(this, newtab_button_, &point_in_newtab_coords); 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (newtab_button_->bounds().Contains(point) && 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !newtab_button_->HitTest(point_in_newtab_coords)) { 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // All other regions, including the new Tab button, should be considered part 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // of the containing Window's client area so that regular events can be 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // processed for them. 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::SetBackgroundOffset(const gfx::Point& offset) { 194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (int i = 0; i < tab_count(); ++i) 195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen GetTabAtTabDataIndex(i)->set_background_offset(offset); 196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//////////////////////////////////////////////////////////////////////////////// 199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TabStrip, BaseTabStrip implementation: 200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::PrepareForCloseAt(int model_index) { 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!in_tab_close_ && IsAnimating()) { 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Cancel any current animations. We do this as remove uses the current 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // ideal bounds and we need to know ideal bounds is in a good state. 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StopAnimating(true); 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int model_count = GetModelCount(); 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (model_index + 1 != model_count && model_count > 1) { 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The user is about to close a tab other than the last tab. Set 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // available_width_for_tabs_ so that if we do a layout we don't position a 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // tab past the end of the second to last tab. We do this so that as the 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // user closes tabs with the mouse a tab continues to fall under the mouse. 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width_for_tabs_ = GetAvailableWidthForTabs( 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetTabAtModelIndex(model_count - 2)); 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_tab_close_ = true; 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AddMessageLoopObserver(); 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::RemoveTabAt(int model_index) { 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (in_tab_close_ && model_index != GetModelCount()) 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StartMouseInitiatedRemoveTabAnimation(model_index); 225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StartRemoveTabAnimation(model_index); 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::SelectTabAt(int old_model_index, int new_model_index) { 230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We have "tiny tabs" if the tabs are so tiny that the unselected ones are 231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // a different size to the selected ones. 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool tiny_tabs = current_unselected_width_ != current_selected_width_; 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) { 234731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DoLayout(); 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SchedulePaint(); 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (old_model_index >= 0) { 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetTabAtTabDataIndex(ModelIndexToTabIndex(old_model_index))-> 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StopMiniTabTitleAnimation(); 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::TabTitleChangedNotLoading(int model_index) { 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = GetTabAtModelIndex(model_index); 247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (tab->data().mini && !tab->IsActive()) 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab->StartMiniTabTitleAnimation(); 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartHighlight(int model_index) { 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetTabAtModelIndex(model_index)->StartPulse(); 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StopAllHighlighting() { 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < tab_count(); ++i) 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetTabAtTabDataIndex(i)->StopPulse(); 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBaseTab* TabStrip::CreateTabForDragging() { 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = new Tab(NULL); 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Make sure the dragged tab shares our theme provider. We need to explicitly 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // do this as during dragging there isn't a theme provider. 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab->set_theme_provider(GetThemeProvider()); 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return tab; 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, views::View overrides: 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::PaintChildren(gfx::Canvas* canvas) { 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tabs are painted in reverse order, so they stack to the left. 273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Tab* active_tab = NULL; 274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::vector<Tab*> tabs_dragging; 275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::vector<Tab*> selected_tabs; 276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool is_dragging = false; 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = tab_count() - 1; i >= 0; --i) { 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We must ask the _Tab's_ model, not ourselves, because in some situations 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the model will be different to this object, e.g. when a Tab is being 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // removed after its TabContents has been destroyed. 282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Tab* tab = GetTabAtTabDataIndex(i); 283731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (tab->dragging()) { 284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen is_dragging = true; 285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (tab->IsActive()) 286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen active_tab = tab; 287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen else 288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tabs_dragging.push_back(tab); 289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else if (!tab->IsActive()) { 290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!tab->IsSelected()) 291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tab->Paint(canvas); 292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen else 293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen selected_tabs.push_back(tab); 294731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } else { 295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen active_tab = tab; 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 299dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (GetWindow()->non_client_view()->UseNativeFrame()) { 300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool multiple_tabs_selected = (!selected_tabs.empty() || 301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tabs_dragging.size() > 1); 302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Make sure non-active tabs are somewhat transparent. 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SkPaint paint; 304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If there are multiple tabs selected, fade non-selected tabs more to make 305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the selected tabs more noticable. 306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen paint.setColor(SkColorSetARGB( 307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen multiple_tabs_selected ? 150 : 200, 255, 255, 255)); 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch paint.setXfermodeMode(SkXfermode::kDstIn_Mode); 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch paint.setStyle(SkPaint::kFill_Style); 3103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick canvas->DrawRectInt(0, 0, width(), 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch height() - 2, // Visible region that overlaps the toolbar. 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch paint); 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Now selected but not active. We don't want these dimmed if using native 316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // frame, so they're painted after initial pass. 317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (size_t i = 0; i < selected_tabs.size(); ++i) 318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen selected_tabs[i]->Paint(canvas); 319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Next comes the active tab. 321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (active_tab && !is_dragging) 322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen active_tab->Paint(canvas); 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Paint the New Tab button. 325dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen newtab_button_->Paint(canvas); 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // And the dragged tabs. 328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (size_t i = 0; i < tabs_dragging.size(); ++i) 329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tabs_dragging[i]->Paint(canvas); 330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If the active tab is being dragged, it goes last. 332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (active_tab && is_dragging) 333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen active_tab->Paint(canvas); 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Overridden to support automation. See automation_proxy_uitest.cc. 33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst views::View* TabStrip::GetViewByID(int view_id) const { 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_count() > 0) { 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (view_id == VIEW_ID_TAB_LAST) { 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GetTabAtTabDataIndex(tab_count() - 1); 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) { 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int index = view_id - VIEW_ID_TAB_0; 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (index >= 0 && index < tab_count()) { 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GetTabAtTabDataIndex(index); 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return View::GetViewByID(view_id); 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Size TabStrip::GetPreferredSize() { 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return gfx::Size(0, Tab::GetMinimumUnselectedSize().height()); 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::OnDragEntered(const DropTargetEvent& event) { 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Force animations to stop, otherwise it makes the index calculation tricky. 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StopAnimating(true); 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateDropIndex(event); 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::OnDragUpdated(const DropTargetEvent& event) { 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateDropIndex(event); 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GetDropEffect(event); 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::OnDragExited() { 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetDropIndex(-1, false); 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::OnPerformDrop(const DropTargetEvent& event) { 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!drop_info_.get()) 37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ui::DragDropTypes::DRAG_NONE; 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int drop_index = drop_info_->drop_index; 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const bool drop_before = drop_info_->drop_before; 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Hide the drop indicator. 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetDropIndex(-1, false); 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL url; 385dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen string16 title; 38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!event.data().GetURLAndTitle(&url, &title) || !url.is_valid()) 38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ui::DragDropTypes::DRAG_NONE; 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch controller()->PerformDrop(drop_before, drop_index, url); 390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GetDropEffect(event); 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 394ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::GetAccessibleState(ui::AccessibleViewState* state) { 395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 398dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenviews::View* TabStrip::GetEventHandlerForPoint(const gfx::Point& point) { 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Return any view that isn't a Tab or this TabStrip immediately. We don't 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // want to interfere. 401dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen views::View* v = View::GetEventHandlerForPoint(point); 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (v && v != this && v->GetClassName() != Tab::kViewClassName) 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return v; 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The display order doesn't necessarily match the child list order, so we 406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // walk the display list hit-testing Tabs. Since the active tab always 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // renders on top of adjacent tabs, it needs to be hit-tested before any 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // left-adjacent Tab, so we look ahead for it as we walk. 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < tab_count(); ++i) { 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* next_tab = i < (tab_count() - 1) ? GetTabAtTabDataIndex(i + 1) : NULL; 411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (next_tab && next_tab->IsActive() && IsPointInTab(next_tab, point)) 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return next_tab; 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = GetTabAtTabDataIndex(i); 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (IsPointInTab(tab, point)) 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return tab; 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // No need to do any floating view stuff, we don't use them in the TabStrip. 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return this; 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 4223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid TabStrip::OnThemeChanged() { 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LoadNewTabButtonImage(); 424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBaseTab* TabStrip::CreateTab() { 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = new Tab(this); 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab->set_animation_container(animation_container_.get()); 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return tab; 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::StartInsertTabAnimation(int model_index) { 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PrepareForAnimation(); 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The TabStrip can now use its entire width to lay out Tabs. 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_tab_close_ = false; 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width_for_tabs_ = -1; 438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GenerateIdealBounds(); 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int tab_data_index = ModelIndexToTabIndex(model_index); 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BaseTab* tab = base_tab_at_tab_index(tab_data_index); 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (model_index == 0) { 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab->SetBounds(0, ideal_bounds(tab_data_index).y(), 0, 445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ideal_bounds(tab_data_index).height()); 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BaseTab* last_tab = base_tab_at_tab_index(tab_data_index - 1); 448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab->SetBounds(last_tab->bounds().right() + kTabHOffset, 449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ideal_bounds(tab_data_index).y(), 0, 450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ideal_bounds(tab_data_index).height()); 451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AnimateToIdealBounds(); 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::AnimateToIdealBounds() { 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < tab_count(); ++i) { 458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = GetTabAtTabDataIndex(i); 459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!tab->closing() && !tab->dragging()) 460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bounds_animator().AnimateViewTo(tab, ideal_bounds(i)); 461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bounds_animator().AnimateViewTo(newtab_button_, newtab_button_bounds_); 464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStrip::ShouldHighlightCloseButtonAfterRemove() { 467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return in_tab_close_; 468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 470731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid TabStrip::DoLayout() { 471731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BaseTabStrip::DoLayout(); 472731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 47372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen newtab_button_->SetBoundsRect(newtab_button_bounds_); 474731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 475731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs, 477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BaseTab* active_tab, 478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const gfx::Point& location, 479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool initial_drag) { 480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::vector<gfx::Rect> bounds; 481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CalculateBoundsForDraggedTabs(tabs, &bounds); 482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK_EQ(tabs.size(), bounds.size()); 483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int active_tab_model_index = GetModelIndexOfBaseTab(active_tab); 484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int active_tab_index = static_cast<int>( 485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::find(tabs.begin(), tabs.end(), active_tab) - tabs.begin()); 486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (size_t i = 0; i < tabs.size(); ++i) { 487ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BaseTab* tab = tabs[i]; 488ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen gfx::Rect new_bounds = bounds[i]; 489ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen new_bounds.Offset(location.x(), location.y()); 490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int consecutive_index = 491ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen active_tab_model_index - (active_tab_index - static_cast<int>(i)); 492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If this is the initial layout during a drag and the tabs aren't 493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // consecutive animate the view into position. Do the same if the tab is 494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // already animating (which means we previously caused it to animate). 495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if ((initial_drag && 496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen GetModelIndexOfBaseTab(tabs[i]) != consecutive_index) || 497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bounds_animator().IsAnimating(tabs[i])) { 498ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bounds_animator().SetTargetBounds(tabs[i], new_bounds); 499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else { 500ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tab->SetBoundsRect(new_bounds); 501ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 502ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 503ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 505ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::CalculateBoundsForDraggedTabs(const std::vector<BaseTab*>& tabs, 506ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::vector<gfx::Rect>* bounds) { 507ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int x = 0; 508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (size_t i = 0; i < tabs.size(); ++i) { 509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BaseTab* tab = tabs[i]; 510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini) 511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen x += mini_to_non_mini_gap_; 512ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen gfx::Rect new_bounds = tab->bounds(); 513ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen new_bounds.set_origin(gfx::Point(x, 0)); 514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bounds->push_back(new_bounds); 515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen x += tab->width() + kTabHOffset; 516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 519ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint TabStrip::GetSizeNeededForTabs(const std::vector<BaseTab*>& tabs) { 520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int width = 0; 521ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (size_t i = 0; i < tabs.size(); ++i) { 522ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BaseTab* tab = tabs[i]; 523ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen width += tab->width(); 524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini) 525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen width += mini_to_non_mini_gap_; 526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (tabs.size() > 0) 528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen width += kTabHOffset * static_cast<int>(tabs.size() - 1); 529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return width; 530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::ViewHierarchyChanged(bool is_add, 533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch views::View* parent, 534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch views::View* child) { 535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_add && child == this) 536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch InitTabStripButtons(); 537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, views::BaseButton::ButtonListener implementation: 541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::ButtonPressed(views::Button* sender, const views::Event& event) { 543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (sender == newtab_button_) 544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch controller()->CreateNewTab(); 545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, private: 549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::Init() { 551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetID(VIEW_ID_TAB_STRIP); 552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight); 553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (browser_defaults::kSizeTabButtonToTopOfTabStrip) { 554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_bounds_.set_height( 555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kNewTabButtonHeight + kNewTabButtonVOffset); 556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (drop_indicator_width == 0) { 558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Direction doesn't matter, both images are the same size. 559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SkBitmap* drop_image = GetDropArrowImage(true); 560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_indicator_width = drop_image->width(); 561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_indicator_height = drop_image->height(); 562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::LoadNewTabButtonImage() { 56672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ui::ThemeProvider* tp = GetThemeProvider(); 567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If we don't have a theme provider yet, it means we do not have a 569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // root view, and are therefore in a test. 570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool in_test = false; 571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tp == NULL) { 572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tp = new views::DefaultThemeProvider(); 573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_test = true; 574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SkBitmap* bitmap = tp->GetBitmapNamed(IDR_NEWTAB_BUTTON); 577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkColor color = tp->GetColor(ThemeService::COLOR_BUTTON_BACKGROUND); 578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SkBitmap* background = tp->GetBitmapNamed( 579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IDR_THEME_WINDOW_CONTROL_BACKGROUND); 580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_->SetImage(views::CustomButton::BS_NORMAL, bitmap); 582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_->SetImage(views::CustomButton::BS_PUSHED, 583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_P)); 584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_->SetImage(views::CustomButton::BS_HOT, 585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_H)); 586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_->SetBackground(color, background, 587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_MASK)); 588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (in_test) 589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete tp; 590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 592c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTab* TabStrip::GetTabAtTabDataIndex(int tab_data_index) const { 593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return static_cast<Tab*>(base_tab_at_tab_index(tab_data_index)); 594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 596c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTab* TabStrip::GetTabAtModelIndex(int model_index) const { 597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GetTabAtTabDataIndex(ModelIndexToTabIndex(model_index)); 598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::GetCurrentTabWidths(double* unselected_width, 601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double* selected_width) const { 602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *unselected_width = current_unselected_width_; 603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *selected_width = current_selected_width_; 604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::GetDesiredTabWidths(int tab_count, 607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int mini_tab_count, 608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double* unselected_width, 609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double* selected_width) const { 610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count); 611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const double min_unselected_width = Tab::GetMinimumUnselectedSize().width(); 612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const double min_selected_width = Tab::GetMinimumSelectedSize().width(); 613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *unselected_width = min_unselected_width; 615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *selected_width = min_selected_width; 616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_count == 0) { 618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Return immediately to avoid divide-by-zero below. 619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Determine how much space we can actually allocate to tabs. 623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int available_width; 624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (available_width_for_tabs_ < 0) { 625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width = width(); 626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width -= (kNewTabButtonHOffset + newtab_button_bounds_.width()); 627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Interesting corner case: if |available_width_for_tabs_| > the result 629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // of the calculation in the conditional arm above, the strip is in 630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // overflow. We can either use the specified width or the true available 631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // width here; the first preserves the consistent "leave the last tab under 632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the user's mouse so they can close many tabs" behavior at the cost of 633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // prolonging the glitchy appearance of the overflow state, while the second 634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // gets us out of overflow as soon as possible but forces the user to move 635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // their mouse for a few tabs' worth of closing. We choose visual 636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // imperfection over behavioral imperfection and select the first option. 637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width = available_width_for_tabs_; 638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (mini_tab_count > 0) { 641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width -= mini_tab_count * (Tab::GetMiniWidth() + kTabHOffset); 642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_count -= mini_tab_count; 643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_count == 0) { 644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *selected_width = *unselected_width = Tab::GetStandardSize().width(); 645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Account for gap between the last mini-tab and first non-mini-tab. 648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width -= mini_to_non_mini_gap_; 649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Calculate the desired tab widths by dividing the available space into equal 652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // portions. Don't let tabs get larger than the "standard width" or smaller 653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // than the minimum width for each type, respectively. 654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int total_offset = kTabHOffset * (tab_count - 1); 655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const double desired_tab_width = std::min((static_cast<double>( 656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width - total_offset) / static_cast<double>(tab_count)), 657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static_cast<double>(Tab::GetStandardSize().width())); 658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *unselected_width = std::max(desired_tab_width, min_unselected_width); 659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *selected_width = std::max(desired_tab_width, min_selected_width); 660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // When there are multiple tabs, we'll have one selected and some unselected 662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // tabs. If the desired width was between the minimum sizes of these types, 663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // try to shrink the tabs with the smaller minimum. For example, if we have 664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5. If 665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // selected tabs have a minimum width of 4 and unselected tabs have a minimum 666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // width of 1, the above code would set *unselected_width = 2.5, 667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // *selected_width = 4, which results in a total width of 11.5. Instead, we 668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // want to set *unselected_width = 2, *selected_width = 4, for a total width 669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // of 10. 670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_count > 1) { 671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((min_unselected_width < min_selected_width) && 672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (desired_tab_width < min_selected_width)) { 673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Unselected width = (total width - selected width) / (num_tabs - 1) 674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *unselected_width = std::max(static_cast<double>( 675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width - total_offset - min_selected_width) / 676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static_cast<double>(tab_count - 1), min_unselected_width); 677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if ((min_unselected_width > min_selected_width) && 678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (desired_tab_width < min_unselected_width)) { 679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Selected width = (total width - (unselected width * (num_tabs - 1))) 680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *selected_width = std::max(available_width - total_offset - 681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (min_unselected_width * (tab_count - 1)), min_selected_width); 682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::ResizeLayoutTabs() { 687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We've been called back after the TabStrip has been emptied out (probably 688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // just prior to the window being destroyed). We need to do nothing here or 689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // else GetTabAt below will crash. 690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_count() == 0) 691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It is critically important that this is unhooked here, otherwise we will 694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // keep spying on messages forever. 695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RemoveMessageLoopObserver(); 696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_tab_close_ = false; 698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width_for_tabs_ = -1; 699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int mini_tab_count = GetMiniTabCount(); 700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (mini_tab_count == tab_count()) { 701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Only mini-tabs, we know the tab widths won't have changed (all 702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // mini-tabs have the same width), so there is nothing to do. 703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* first_tab = GetTabAtTabDataIndex(mini_tab_count); 706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double unselected, selected; 707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetDesiredTabWidths(tab_count(), mini_tab_count, &unselected, &selected); 708ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO: this is always selected, should it be 'selected : unselected'? 709ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int w = Round(first_tab->IsActive() ? selected : selected); 710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We only want to run the animation if we're not already at the desired 712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // size. 713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (abs(first_tab->width() - w) > 1) 714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StartResizeLayoutAnimation(); 715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::AddMessageLoopObserver() { 7183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!mouse_watcher_.get()) { 7193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick mouse_watcher_.reset( 7203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick new views::MouseWatcher(this, this, 7213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick gfx::Insets(0, 0, kTabStripAnimationVSlop, 0))); 722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 7233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick mouse_watcher_->Start(); 724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::RemoveMessageLoopObserver() { 7273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick mouse_watcher_.reset(NULL); 728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect TabStrip::GetDropBounds(int drop_index, 731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool drop_before, 732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool* is_beneath) { 733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(drop_index != -1); 734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int center_x; 735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (drop_index < tab_count()) { 736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = GetTabAtTabDataIndex(drop_index); 737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (drop_before) 738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch center_x = tab->x() - (kTabHOffset / 2); 739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch center_x = tab->x() + (tab->width() / 2); 741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* last_tab = GetTabAtTabDataIndex(drop_index - 1); 743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch center_x = last_tab->x() + last_tab->width() + (kTabHOffset / 2); 744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Mirror the center point if necessary. 74772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen center_x = GetMirroredXInView(center_x); 748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Determine the screen bounds. 750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Point drop_loc(center_x - drop_indicator_width / 2, 751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch -drop_indicator_height); 752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConvertPointToScreen(this, &drop_loc); 753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width, 754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_indicator_height); 755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the rect doesn't fit on the monitor, push the arrow to the bottom. 757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 75872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen gfx::Rect monitor_bounds = views::GetMonitorBoundsForRect(drop_bounds); 759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *is_beneath = (monitor_bounds.IsEmpty() || 760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !monitor_bounds.Contains(drop_bounds)); 761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else 762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *is_beneath = false; 763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTIMPLEMENTED(); 764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (*is_beneath) 766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_bounds.Offset(0, drop_bounds.height() + height()); 767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return drop_bounds; 769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::UpdateDropIndex(const DropTargetEvent& event) { 772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the UI layout is right-to-left, we need to mirror the mouse 773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // coordinates since we calculate the drop index based on the 774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // original (and therefore non-mirrored) positions of the tabs. 77572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const int x = GetMirroredXInView(event.x()); 776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We don't allow replacing the urls of mini-tabs. 777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = GetMiniTabCount(); i < tab_count(); ++i) { 778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Tab* tab = GetTabAtTabDataIndex(i); 779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int tab_max_x = tab->x() + tab->width(); 780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int hot_width = tab->width() / 3; 781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (x < tab_max_x) { 782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (x < tab->x() + hot_width) 783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetDropIndex(i, true); 784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else if (x >= tab_max_x - hot_width) 785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetDropIndex(i + 1, true); 786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetDropIndex(i, false); 788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 792c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The drop isn't over a tab, add it to the end. 793c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetDropIndex(tab_count(), true); 794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::SetDropIndex(int tab_data_index, bool drop_before) { 797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_data_index == -1) { 798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (drop_info_.get()) 799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_.reset(NULL); 800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (drop_info_.get() && drop_info_->drop_index == tab_data_index && 804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->drop_before == drop_before) { 805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool is_beneath; 809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect drop_bounds = GetDropBounds(tab_data_index, drop_before, 810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &is_beneath); 811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!drop_info_.get()) { 813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_.reset(new DropInfo(tab_data_index, drop_before, !is_beneath)); 814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->drop_index = tab_data_index; 816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->drop_before = drop_before; 817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_beneath == drop_info_->point_down) { 818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->point_down = !is_beneath; 819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->arrow_view->SetImage( 820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetDropArrowImage(drop_info_->point_down)); 821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 824c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Reposition the window. Need to show it too as the window is initially 825c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // hidden. 826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->arrow_window->SetBounds(drop_bounds); 827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_info_->arrow_window->Show(); 828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 830c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::GetDropEffect(const views::DropTargetEvent& event) { 83172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const int source_ops = event.source_operations(); 83272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (source_ops & ui::DragDropTypes::DRAG_COPY) 83372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ui::DragDropTypes::DRAG_COPY; 83472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (source_ops & ui::DragDropTypes::DRAG_LINK) 83572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ui::DragDropTypes::DRAG_LINK; 83672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return ui::DragDropTypes::DRAG_MOVE; 837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 838c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 839c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 840c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochSkBitmap* TabStrip::GetDropArrowImage(bool is_down) { 841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ResourceBundle::GetSharedInstance().GetBitmapNamed( 842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP); 843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip::DropInfo ---------------------------------------------------------- 846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 847c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::DropInfo::DropInfo(int drop_index, bool drop_before, bool point_down) 848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : drop_index(drop_index), 849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drop_before(drop_before), 850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch point_down(point_down) { 851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch arrow_view = new views::ImageView; 852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch arrow_view->SetImage(GetDropArrowImage(point_down)); 853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 854ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); 855ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen params.keep_on_top = true; 856ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen params.transparent = true; 857ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen params.accept_events = false; 858ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen params.can_activate = false; 859ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen arrow_window = views::Widget::CreateWidget(params); 860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch arrow_window->Init( 861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect(0, 0, drop_indicator_width, drop_indicator_height)); 863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch arrow_window->SetContentsView(arrow_view); 864c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 865c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 866c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::DropInfo::~DropInfo() { 867c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Close eventually deletes the window, which deletes arrow_view too. 868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch arrow_window->Close(); 869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 870c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called from: 874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - BasicLayout 875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - Tab insertion/removal 876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - Tab reorder 877c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::GenerateIdealBounds() { 878c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int non_closing_tab_count = 0; 879c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int mini_tab_count = 0; 880c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < tab_count(); ++i) { 881c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BaseTab* tab = base_tab_at_tab_index(i); 882c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!tab->closing()) { 883c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++non_closing_tab_count; 884c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab->data().mini) 885c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch mini_tab_count++; 886c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 887c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 888c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 889c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double unselected, selected; 890c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetDesiredTabWidths(non_closing_tab_count, mini_tab_count, &unselected, 891c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &selected); 892c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 893c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_unselected_width_ = unselected; 894c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_selected_width_ = selected; 895c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 896c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // NOTE: This currently assumes a tab's height doesn't differ based on 897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // selected state or the number of tabs in the strip! 898c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int tab_height = Tab::GetStandardSize().height(); 899c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double tab_x = 0; 900c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool last_was_mini = false; 901c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < tab_count(); ++i) { 902ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Tab* tab = GetTabAtTabDataIndex(i); 903c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!tab->closing()) { 904c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double tab_width = unselected; 905c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab->data().mini) { 906c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_width = Tab::GetMiniWidth(); 907c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 908c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (last_was_mini) { 909c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Give a bigger gap between mini and non-mini tabs. 910c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_x += mini_to_non_mini_gap_; 911c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 912ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (tab->IsActive()) 913c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_width = selected; 914c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 915c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double end_of_tab = tab_x + tab_width; 916c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int rounded_tab_x = Round(tab_x); 917c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch set_ideal_bounds(i, 918c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x, 919c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_height)); 920c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_x = end_of_tab + kTabHOffset; 921c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch last_was_mini = tab->data().mini; 922c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 923c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 924c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 925c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Update bounds of new tab button. 926c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int new_tab_x; 927c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int new_tab_y = browser_defaults::kSizeTabButtonToTopOfTabStrip ? 928c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 0 : kNewTabButtonVOffset; 929c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 && 930c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !in_tab_close_) { 931c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We're shrinking tabs, so we need to anchor the New Tab button to the 932c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // right edge of the TabStrip's bounds, rather than the right edge of the 933c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // right-most Tab, otherwise it'll bounce when animating. 934c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new_tab_x = width() - newtab_button_bounds_.width(); 935c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 936c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new_tab_x = Round(tab_x - kTabHOffset) + kNewTabButtonHOffset; 937c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 938c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y)); 939c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 940c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 941c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartResizeLayoutAnimation() { 942c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PrepareForAnimation(); 943c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GenerateIdealBounds(); 944c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AnimateToIdealBounds(); 945c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 946c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 947c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartMiniTabAnimation() { 948c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_tab_close_ = false; 949c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch available_width_for_tabs_ = -1; 950c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 951c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PrepareForAnimation(); 952c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 953c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GenerateIdealBounds(); 954c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AnimateToIdealBounds(); 955c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 956c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 957c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) { 958c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The user initiated the close. We want to persist the bounds of all the 959c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // existing tabs, so we manually shift ideal_bounds then animate. 960c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int tab_data_index = ModelIndexToTabIndex(model_index); 961c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(tab_data_index != tab_count()); 962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BaseTab* tab_closing = base_tab_at_tab_index(tab_data_index); 963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int delta = tab_closing->width() + kTabHOffset; 964c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_closing->data().mini && model_index + 1 < GetModelCount() && 965c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !GetBaseTabAtModelIndex(model_index + 1)->data().mini) { 966c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delta += mini_to_non_mini_gap_; 967c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 968c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 969c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = tab_data_index + 1; i < tab_count(); ++i) { 970c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BaseTab* tab = base_tab_at_tab_index(i); 971c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!tab->closing()) { 972c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect bounds = ideal_bounds(i); 973c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bounds.set_x(bounds.x() - delta); 974c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch set_ideal_bounds(i, bounds); 975c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 976c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 977c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 978c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab_button_bounds_.set_x(newtab_button_bounds_.x() - delta); 979c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 980c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PrepareForAnimation(); 981c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 982c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Mark the tab as closing. 983c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_closing->set_closing(true); 984c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 985c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AnimateToIdealBounds(); 986c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 987c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect tab_bounds = tab_closing->bounds(); 988c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (type() == HORIZONTAL_TAB_STRIP) 989c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_bounds.set_width(0); 990c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 991c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_bounds.set_height(0); 992c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bounds_animator().AnimateViewTo(tab_closing, tab_bounds); 993c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 994c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Register delegate to do cleanup when done, BoundsAnimator takes 995c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // ownership of RemoveTabDelegate. 996c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bounds_animator().SetAnimationDelegate(tab_closing, 997c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CreateRemoveTabDelegate(tab_closing), 998c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch true); 999c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 1000c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1001c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::GetMiniTabCount() const { 1002c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int mini_count = 0; 1003c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < tab_count(); ++i) { 1004c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (base_tab_at_tab_index(i)->data().mini) 1005c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch mini_count++; 1006c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 1007c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return mini_count; 1008c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 1009c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return mini_count; 1010c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 1011c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1012c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::GetAvailableWidthForTabs(Tab* last_tab) const { 1013c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return last_tab->x() + last_tab->width(); 1014c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 1015c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1016c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStrip::IsPointInTab(Tab* tab, 1017c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const gfx::Point& point_in_tabstrip_coords) { 1018c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Point point_in_tab_coords(point_in_tabstrip_coords); 1019c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch View::ConvertPointToView(this, tab, &point_in_tab_coords); 1020c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return tab->HitTest(point_in_tab_coords); 1021c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 1022