Home | History | Annotate | Download | only in tabs
      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