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