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
18class Tab;
19
20namespace views {
21class ImageView;
22#if defined(OS_LINUX)
23class WidgetGtk;
24#elif defined(OS_WIN)
25class 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///////////////////////////////////////////////////////////////////////////////
42class 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