1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_H_ 6 #define CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_H_ 7 #pragma once 8 9 #include "base/memory/ref_counted.h" 10 #include "base/timer.h" 11 #include "chrome/browser/ui/views/tabs/base_tab_strip.h" 12 #include "ui/base/animation/animation_container.h" 13 #include "ui/gfx/point.h" 14 #include "ui/gfx/rect.h" 15 #include "views/controls/button/image_button.h" 16 #include "views/mouse_watcher.h" 17 18 class Tab; 19 20 namespace views { 21 class ImageView; 22 #if defined(OS_LINUX) 23 class WidgetGtk; 24 #elif defined(OS_WIN) 25 class WidgetWin; 26 #endif 27 } 28 29 /////////////////////////////////////////////////////////////////////////////// 30 // 31 // TabStrip 32 // 33 // A View that represents the TabStripModel. The TabStrip has the 34 // following responsibilities: 35 // - It implements the TabStripModelObserver interface, and acts as a 36 // container for Tabs, and is also responsible for creating them. 37 // - It takes part in Tab Drag & Drop with Tab, TabDragHelper and 38 // DraggedTab, focusing on tasks that require reshuffling other tabs 39 // in response to dragged tabs. 40 // 41 /////////////////////////////////////////////////////////////////////////////// 42 class TabStrip : public BaseTabStrip, 43 public views::ButtonListener, 44 public views::MouseWatcherListener { 45 public: 46 explicit TabStrip(TabStripController* controller); 47 virtual ~TabStrip(); 48 49 // Creates the new tab button. 50 void InitTabStripButtons(); 51 52 // Returns the bounds of the new tab button. 53 gfx::Rect GetNewTabButtonBounds(); 54 55 // MouseWatcherListener overrides: 56 virtual void MouseMovedOutOfView() OVERRIDE; 57 58 // AbstractTabStripView implementation: 59 virtual bool IsPositionInWindowCaption(const gfx::Point& point) OVERRIDE; 60 virtual void SetBackgroundOffset(const gfx::Point& offset) OVERRIDE; 61 62 // BaseTabStrip implementation: 63 virtual void PrepareForCloseAt(int model_index) OVERRIDE; 64 virtual void RemoveTabAt(int model_index) OVERRIDE; 65 virtual void SelectTabAt(int old_model_index, int new_model_index) OVERRIDE; 66 virtual void TabTitleChangedNotLoading(int model_index) OVERRIDE; 67 virtual void StartHighlight(int model_index) OVERRIDE; 68 virtual void StopAllHighlighting() OVERRIDE; 69 virtual BaseTab* CreateTabForDragging() OVERRIDE; 70 71 // views::View overrides: 72 virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE; 73 virtual const views::View* GetViewByID(int id) const OVERRIDE; 74 virtual gfx::Size GetPreferredSize() OVERRIDE; 75 // NOTE: the drag and drop methods are invoked from FrameView. This is done 76 // to allow for a drop region that extends outside the bounds of the TabStrip. 77 virtual void OnDragEntered(const views::DropTargetEvent& event) OVERRIDE; 78 virtual int OnDragUpdated(const views::DropTargetEvent& event) OVERRIDE; 79 virtual void OnDragExited() OVERRIDE; 80 virtual int OnPerformDrop(const views::DropTargetEvent& event) OVERRIDE; 81 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; 82 virtual views::View* GetEventHandlerForPoint(const gfx::Point& point) 83 OVERRIDE; 84 virtual void OnThemeChanged() OVERRIDE; 85 86 protected: 87 // BaseTabStrip overrides: 88 virtual BaseTab* CreateTab() OVERRIDE; 89 virtual void StartInsertTabAnimation(int model_index) OVERRIDE; 90 virtual void AnimateToIdealBounds() OVERRIDE; 91 virtual bool ShouldHighlightCloseButtonAfterRemove() OVERRIDE; 92 virtual void DoLayout() OVERRIDE; 93 virtual void LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs, 94 BaseTab* active_tab, 95 const gfx::Point& location, 96 bool initial_drag) OVERRIDE; 97 virtual void CalculateBoundsForDraggedTabs( 98 const std::vector<BaseTab*>& tabs, 99 std::vector<gfx::Rect>* bounds) OVERRIDE; 100 virtual int GetSizeNeededForTabs(const std::vector<BaseTab*>& tabs) OVERRIDE; 101 102 // views::View implementation: 103 virtual void ViewHierarchyChanged(bool is_add, 104 views::View* parent, 105 views::View* child) OVERRIDE; 106 107 // views::ButtonListener implementation: 108 virtual void ButtonPressed(views::Button* sender, const views::Event& event) 109 OVERRIDE; 110 111 // Horizontal gap between mini and non-mini-tabs. 112 static const int mini_to_non_mini_gap_; 113 114 private: 115 friend class DraggedTabController; 116 117 // Used during a drop session of a url. Tracks the position of the drop as 118 // well as a window used to highlight where the drop occurs. 119 struct DropInfo { 120 DropInfo(int index, bool drop_before, bool paint_down); 121 ~DropInfo(); 122 123 // Index of the tab to drop on. If drop_before is true, the drop should 124 // occur between the tab at drop_index - 1 and drop_index. 125 // WARNING: if drop_before is true it is possible this will == tab_count, 126 // which indicates the drop should create a new tab at the end of the tabs. 127 int drop_index; 128 bool drop_before; 129 130 // Direction the arrow should point in. If true, the arrow is displayed 131 // above the tab and points down. If false, the arrow is displayed beneath 132 // the tab and points up. 133 bool point_down; 134 135 // Renders the drop indicator. 136 views::Widget* arrow_window; 137 views::ImageView* arrow_view; 138 139 private: 140 DISALLOW_COPY_AND_ASSIGN(DropInfo); 141 }; 142 143 void Init(); 144 145 // Set the images for the new tab button. 146 void LoadNewTabButtonImage(); 147 148 // Retrieves the Tab at the specified index. Remember, the specified index 149 // is in terms of tab_data, *not* the model. 150 Tab* GetTabAtTabDataIndex(int tab_data_index) const; 151 152 // Returns the tab at the specified index. If a remove animation is on going 153 // and the index is >= the index of the tab being removed, the index is 154 // incremented. While a remove operation is on going the indices of the model 155 // do not line up with the indices of the view. This method adjusts the index 156 // accordingly. 157 // 158 // Use this instead of GetTabAtTabDataIndex if the index comes from the model. 159 Tab* GetTabAtModelIndex(int model_index) const; 160 161 // Returns the number of mini-tabs. 162 int GetMiniTabCount() const; 163 164 // -- Tab Resize Layout ----------------------------------------------------- 165 166 // Returns the exact (unrounded) current width of each tab. 167 void GetCurrentTabWidths(double* unselected_width, 168 double* selected_width) const; 169 170 // Returns the exact (unrounded) desired width of each tab, based on the 171 // desired strip width and number of tabs. If 172 // |width_of_tabs_for_mouse_close_| is nonnegative we use that value in 173 // calculating the desired strip width; otherwise we use the current width. 174 // |mini_tab_count| gives the number of mini-tabs and |tab_count| the number 175 // of mini and non-mini-tabs. 176 void GetDesiredTabWidths(int tab_count, 177 int mini_tab_count, 178 double* unselected_width, 179 double* selected_width) const; 180 181 // Perform an animated resize-relayout of the TabStrip immediately. 182 void ResizeLayoutTabs(); 183 184 // Ensure that the message loop observer used for event spying is added and 185 // removed appropriately so we can tell when to resize layout the tab strip. 186 void AddMessageLoopObserver(); 187 void RemoveMessageLoopObserver(); 188 189 // -- Link Drag & Drop ------------------------------------------------------ 190 191 // Returns the bounds to render the drop at, in screen coordinates. Sets 192 // |is_beneath| to indicate whether the arrow is beneath the tab, or above 193 // it. 194 gfx::Rect GetDropBounds(int drop_index, bool drop_before, bool* is_beneath); 195 196 // Updates the location of the drop based on the event. 197 void UpdateDropIndex(const views::DropTargetEvent& event); 198 199 // Sets the location of the drop, repainting as necessary. 200 void SetDropIndex(int tab_data_index, bool drop_before); 201 202 // Returns the drop effect for dropping a URL on the tab strip. This does 203 // not query the data in anyway, it only looks at the source operations. 204 int GetDropEffect(const views::DropTargetEvent& event); 205 206 // Returns the image to use for indicating a drop on a tab. If is_down is 207 // true, this returns an arrow pointing down. 208 static SkBitmap* GetDropArrowImage(bool is_down); 209 210 // -- Animations ------------------------------------------------------------ 211 212 // Generates the ideal bounds of the TabStrip when all Tabs have finished 213 // animating to their desired position/bounds. This is used by the standard 214 // Layout method and other callers like the DraggedTabController that need 215 // stable representations of Tab positions. 216 virtual void GenerateIdealBounds(); 217 218 // Starts various types of TabStrip animations. 219 void StartResizeLayoutAnimation(); 220 virtual void StartMiniTabAnimation(); 221 void StartMouseInitiatedRemoveTabAnimation(int model_index); 222 223 // Calculates the available width for tabs, assuming a Tab is to be closed. 224 int GetAvailableWidthForTabs(Tab* last_tab) const; 225 226 // Returns true if the specified point in TabStrip coords is within the 227 // hit-test region of the specified Tab. 228 bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords); 229 230 // -- Member Variables ------------------------------------------------------ 231 232 // The "New Tab" button. 233 views::ImageButton* newtab_button_; 234 235 // Ideal bounds of the new tab button. 236 gfx::Rect newtab_button_bounds_; 237 238 // The current widths of various types of tabs. We save these so that, as 239 // users close tabs while we're holding them at the same size, we can lay out 240 // tabs exactly and eliminate the "pixel jitter" we'd get from just leaving 241 // them all at their existing, rounded widths. 242 double current_unselected_width_; 243 double current_selected_width_; 244 245 // If this value is nonnegative, it is used in GetDesiredTabWidths() to 246 // calculate how much space in the tab strip to use for tabs. Most of the 247 // time this will be -1, but while we're handling closing a tab via the mouse, 248 // we'll set this to the edge of the last tab before closing, so that if we 249 // are closing the last tab and need to resize immediately, we'll resize only 250 // back to this width, thus once again placing the last tab under the mouse 251 // cursor. 252 int available_width_for_tabs_; 253 254 // True if PrepareForCloseAt has been invoked. When true remove animations 255 // preserve current tab bounds. 256 bool in_tab_close_; 257 258 // The size of the new tab button must be hardcoded because we need to be 259 // able to lay it out before we are able to get its image from the 260 // ui::ThemeProvider. It also makes sense to do this, because the size of the 261 // new tab button should not need to be calculated dynamically. 262 static const int kNewTabButtonWidth = 28; 263 static const int kNewTabButtonHeight = 18; 264 265 // Valid for the lifetime of a drag over us. 266 scoped_ptr<DropInfo> drop_info_; 267 268 // To ensure all tabs pulse at the same time they share the same animation 269 // container. This is that animation container. 270 scoped_refptr<ui::AnimationContainer> animation_container_; 271 272 // Used for stage 1 of new tab animation. 273 base::OneShotTimer<TabStrip> new_tab_timer_; 274 275 scoped_ptr<views::MouseWatcher> mouse_watcher_; 276 277 DISALLOW_COPY_AND_ASSIGN(TabStrip); 278 }; 279 280 #endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_H_ 281