Home | History | Annotate | Download | only in tabs
      1 // Copyright (c) 2012 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_GTK_TABS_TAB_STRIP_GTK_H_
      6 #define CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_
      7 
      8 #include <gtk/gtk.h>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/memory/weak_ptr.h"
     14 #include "base/message_loop/message_loop.h"
     15 #include "chrome/browser/ui/gtk/tabs/tab_gtk.h"
     16 #include "chrome/browser/ui/gtk/tabstrip_origin_provider.h"
     17 #include "chrome/browser/ui/gtk/view_id_util.h"
     18 #include "chrome/browser/ui/tabs/hover_tab_selector.h"
     19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     20 #include "content/public/browser/notification_observer.h"
     21 #include "content/public/browser/notification_registrar.h"
     22 #include "ui/base/gtk/gtk_signal.h"
     23 #include "ui/base/gtk/owned_widget_gtk.h"
     24 #include "ui/gfx/rect.h"
     25 
     26 class BrowserWindowGtk;
     27 class CustomDrawButton;
     28 class DraggedTabControllerGtk;
     29 class GtkThemeService;
     30 
     31 namespace gfx {
     32 class Image;
     33 }
     34 
     35 class TabStripGtk : public TabStripModelObserver,
     36                     public TabGtk::TabDelegate,
     37                     public base::MessageLoopForUI::Observer,
     38                     public content::NotificationObserver,
     39                     public TabstripOriginProvider,
     40                     public ViewIDUtil::Delegate {
     41  public:
     42   class TabAnimation;
     43 
     44   TabStripGtk(TabStripModel* model, BrowserWindowGtk* window);
     45   virtual ~TabStripGtk();
     46 
     47   // Initialize and load the TabStrip into a container.
     48   // TODO(tc): Pass in theme provider so we can properly theme the tabs.
     49   void Init();
     50 
     51   void Show();
     52   void Hide();
     53 
     54   TabStripModel* model() const { return model_; }
     55 
     56   BrowserWindowGtk* window() const { return window_; }
     57 
     58   GtkWidget* widget() const { return tabstrip_.get(); }
     59 
     60   // Returns true if there is an active drag session.
     61   bool IsDragSessionActive() const { return drag_controller_.get() != NULL; }
     62 
     63   // Returns true if a tab is being dragged into this tabstrip.
     64   bool IsActiveDropTarget() const;
     65 
     66   // Sets the bounds of the tabs.
     67   void Layout();
     68 
     69   // Queues a draw for the tabstrip widget.
     70   void SchedulePaint();
     71 
     72   // Sets the bounds of the tabstrip.
     73   void SetBounds(const gfx::Rect& bounds);
     74 
     75   // Returns the bounds of the tabstrip.
     76   const gfx::Rect& bounds() const { return bounds_; }
     77 
     78   // Updates loading animations for the TabStrip.
     79   void UpdateLoadingAnimations();
     80 
     81   // Return true if this tab strip is compatible with the provided tab strip.
     82   // Compatible tab strips can transfer tabs during drag and drop.
     83   bool IsCompatibleWith(TabStripGtk* other);
     84 
     85   // Returns true if Tabs in this TabStrip are currently changing size or
     86   // position.
     87   bool IsAnimating() const;
     88 
     89   // Destroys the active drag controller.
     90   void DestroyDragController();
     91 
     92   // Removes |dragged_tab| from this tabstrip, and deletes it.
     93   void DestroyDraggedTab(TabGtk* dragged_tab);
     94 
     95   // Retrieve the ideal bounds for the Tab at the specified index.
     96   gfx::Rect GetIdealBounds(int index);
     97 
     98   // Sets the vertical offset that each tab will use to offset against the
     99   // background image. Passed in from the titlebar and based on the size of the
    100   // alignment that sits above the tabstrip.
    101   void SetVerticalOffset(int offset);
    102 
    103   // TabstripOriginProvider implementation -------------------------------------
    104   virtual gfx::Point GetTabStripOriginForWidget(GtkWidget* widget) OVERRIDE;
    105 
    106   // ViewIDUtil::Delegate implementation ---------------------------------------
    107   virtual GtkWidget* GetWidgetForViewID(ViewID id) OVERRIDE;
    108 
    109  protected:
    110   // TabStripModelObserver implementation:
    111   virtual void TabInsertedAt(content::WebContents* contents,
    112                              int index,
    113                              bool foreground) OVERRIDE;
    114   virtual void TabDetachedAt(content::WebContents* contents,
    115                              int index) OVERRIDE;
    116   virtual void TabMoved(content::WebContents* contents,
    117                         int from_index,
    118                         int to_index) OVERRIDE;
    119   virtual void ActiveTabChanged(content::WebContents* old_contents,
    120                                 content::WebContents* new_contents,
    121                                 int index,
    122                                 int reason) OVERRIDE;
    123   virtual void TabSelectionChanged(
    124       TabStripModel* tab_strip_model,
    125       const ui::ListSelectionModel& old_model) OVERRIDE;
    126   virtual void TabChangedAt(content::WebContents* contents,
    127                             int index,
    128                             TabChangeType change_type) OVERRIDE;
    129   virtual void TabReplacedAt(TabStripModel* tab_strip_model,
    130                              content::WebContents* old_contents,
    131                              content::WebContents* new_contents,
    132                              int index) OVERRIDE;
    133   virtual void TabMiniStateChanged(content::WebContents* contents,
    134                                    int index) OVERRIDE;
    135   virtual void TabBlockedStateChanged(content::WebContents* contents,
    136                                       int index) OVERRIDE;
    137 
    138   // TabGtk::TabDelegate implementation:
    139   virtual bool IsTabActive(const TabGtk* tab) const OVERRIDE;
    140   virtual bool IsTabSelected(const TabGtk* tab) const OVERRIDE;
    141   virtual bool IsTabPinned(const TabGtk* tab) const OVERRIDE;
    142   virtual bool IsTabDetached(const TabGtk* tab) const OVERRIDE;
    143   virtual void ActivateTab(TabGtk* tab) OVERRIDE;
    144   virtual void ToggleTabSelection(TabGtk* tab) OVERRIDE;
    145   virtual void ExtendTabSelection(TabGtk* tab) OVERRIDE;
    146   virtual void CloseTab(TabGtk* tab) OVERRIDE;
    147   virtual bool IsCommandEnabledForTab(
    148       TabStripModel::ContextMenuCommand command_id,
    149       const TabGtk* tab) const OVERRIDE;
    150   virtual void ExecuteCommandForTab(
    151       TabStripModel::ContextMenuCommand command_id, TabGtk* tab) OVERRIDE;
    152   virtual void StartHighlightTabsForCommand(
    153       TabStripModel::ContextMenuCommand command_id, TabGtk* tab) OVERRIDE;
    154   virtual void StopHighlightTabsForCommand(
    155       TabStripModel::ContextMenuCommand command_id, TabGtk* tab) OVERRIDE;
    156   virtual void StopAllHighlighting() OVERRIDE;
    157   virtual void MaybeStartDrag(TabGtk* tab, const gfx::Point& point) OVERRIDE;
    158   virtual void ContinueDrag(GdkDragContext* context) OVERRIDE;
    159   virtual bool EndDrag(bool canceled) OVERRIDE;
    160   virtual bool HasAvailableDragActions() const OVERRIDE;
    161   virtual GtkThemeService* GetThemeProvider() OVERRIDE;
    162   virtual TabStripMenuController* GetTabStripMenuControllerForTab(
    163       TabGtk* tab) OVERRIDE;
    164 
    165   // MessageLoop::Observer implementation:
    166   virtual void WillProcessEvent(GdkEvent* event) OVERRIDE;
    167   virtual void DidProcessEvent(GdkEvent* event) OVERRIDE;
    168 
    169   // Overridden from content::NotificationObserver:
    170   virtual void Observe(int type,
    171                        const content::NotificationSource& source,
    172                        const content::NotificationDetails& details) OVERRIDE;
    173 
    174   // Horizontal gap between mini-tabs and normal tabs.
    175   static const int mini_to_non_mini_gap_;
    176 
    177  private:
    178   friend class BrowserWindowGtk;
    179   friend class DraggedTabControllerGtk;
    180   friend class InsertTabAnimation;
    181   friend class MiniMoveAnimation;
    182   friend class MiniTabAnimation;
    183   friend class MoveTabAnimation;
    184   friend class RemoveTabAnimation;
    185   friend class ResizeLayoutAnimation;
    186   friend class TabAnimation;
    187 
    188   struct TabData {
    189     TabGtk* tab;
    190     gfx::Rect ideal_bounds;
    191   };
    192 
    193   // Used during a drop session of a url. Tracks the position of the drop as
    194   // well as a window used to highlight where the drop occurs.
    195   class DropInfo {
    196    public:
    197     DropInfo(int index, bool drop_before, bool point_down);
    198     virtual ~DropInfo();
    199 
    200     // TODO(jhawkins): Factor out this code into a TransparentContainer class.
    201 
    202     // expose-event handler that redraws the drop indicator.
    203     CHROMEGTK_CALLBACK_1(DropInfo, gboolean, OnExposeEvent, GdkEventExpose*);
    204 
    205     // Sets the color map of the container window to allow the window to be
    206     // transparent.
    207     void SetContainerColorMap();
    208 
    209     // Sets full transparency for the container window.  This is used if
    210     // compositing is available for the screen.
    211     void SetContainerTransparency();
    212 
    213     // Sets the shape mask for the container window to emulate a transparent
    214     // container window.  This is used if compositing is not available for the
    215     // screen.
    216     void SetContainerShapeMask();
    217 
    218     // Creates the container widget.
    219     void CreateContainer();
    220 
    221     // Destroys the container widget.
    222     void DestroyContainer();
    223 
    224     // Index of the tab to drop on. If drop_before is true, the drop should
    225     // occur between the tab at drop_index - 1 and drop_index.
    226     // WARNING: if drop_before is true it is possible this will == tab_count,
    227     // which indicates the drop should create a new tab at the end of the tabs.
    228     int drop_index;
    229     bool drop_before;
    230 
    231     // Direction the arrow should point in. If true, the arrow is displayed
    232     // above the tab and points down. If false, the arrow is displayed beneath
    233     // the tab and points up.
    234     bool point_down;
    235 
    236     // Transparent container window used to render the drop indicator over the
    237     // tabstrip and toolbar.
    238     GtkWidget* container;
    239 
    240     // The drop indicator image.
    241     gfx::Image* drop_arrow;
    242 
    243    private:
    244     DISALLOW_COPY_AND_ASSIGN(DropInfo);
    245   };
    246 
    247   // Map signal handler that sets initial z-ordering. The widgets need to be
    248   // realized before we can set the stacking. We use the "map" signal since the
    249   // "realize" signal is called before the child widgets get realized.
    250   CHROMEGTK_CALLBACK_0(TabStripGtk, void, OnMap);
    251 
    252   // expose-event handler that redraws the tabstrip
    253   CHROMEGTK_CALLBACK_1(TabStripGtk, gboolean, OnExpose, GdkEventExpose*);
    254 
    255   // size-allocate handler that gets the new bounds of the tabstrip.
    256   CHROMEGTK_CALLBACK_1(TabStripGtk, void, OnSizeAllocate, GtkAllocation*);
    257 
    258   // drag-motion handler that is signaled when the user performs a drag in the
    259   // tabstrip bounds.
    260   CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragMotion, GdkDragContext*,
    261                        gint, gint, guint);
    262 
    263   // drag-drop handler that is notified when the user finishes a drag.
    264   CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragDrop, GdkDragContext*,
    265                        gint, gint, guint);
    266 
    267   // drag-leave handler that is signaled when the mouse leaves the tabstrip
    268   // during a drag.
    269   CHROMEGTK_CALLBACK_2(TabStripGtk, gboolean, OnDragLeave, GdkDragContext*,
    270                        guint);
    271 
    272   // drag-data-received handler that receives the data associated with the drag.
    273   CHROMEGTK_CALLBACK_6(TabStripGtk, gboolean, OnDragDataReceived,
    274                        GdkDragContext*, gint, gint, GtkSelectionData*,
    275                        guint, guint);
    276 
    277   // Handles the clicked signal from the new tab button.
    278   CHROMEGTK_CALLBACK_0(TabStripGtk, void, OnNewTabClicked);
    279 
    280   // Sets the bounds of the tab and moves the tab widget to those bounds.
    281   void SetTabBounds(TabGtk* tab, const gfx::Rect& bounds);
    282 
    283   // Returns true if |rects| are all areas that match up with tab favicons.
    284   // |rects| must be sorted from left to right.  |tabs_to_paint| are the tab
    285   // positions that match the rects.
    286   bool CanPaintOnlyFavicons(const GdkRectangle* rects,
    287                             int num_rects,
    288                             std::vector<int>* tabs_to_paint);
    289 
    290   // Paints the tab favicon areas for tabs in |tabs_to_paint|.
    291   void PaintOnlyFavicons(GdkEventExpose* event,
    292                          const std::vector<int>& tabs_to_paint);
    293 
    294   // Initializes the new tab button.
    295   CustomDrawButton* MakeNewTabButton();
    296 
    297   // Sets the theme specific background on the new tab button.
    298   void SetNewTabButtonBackground();
    299 
    300   // Gets the number of Tabs in the collection.
    301   int GetTabCount() const;
    302 
    303   // Returns the number of mini-tabs.
    304   int GetMiniTabCount() const;
    305 
    306   // Retrieves the Tab at the specified index. Take care in using this, you may
    307   // need to use GetTabAtAdjustForAnimation.
    308   TabGtk* GetTabAt(int index) const;
    309 
    310   // Returns the tab at the specified index. If a remove animation is on going
    311   // and the index is >= the index of the tab being removed, the index is
    312   // incremented. While a remove operation is on going the indices of the model
    313   // do not line up with the indices of the view. This method adjusts the index
    314   // accordingly.
    315   //
    316   // Use this instead of GetTabAt if the index comes from the model.
    317   TabGtk* GetTabAtAdjustForAnimation(int index) const;
    318 
    319   // Returns the exact (unrounded) current width of each tab.
    320   void GetCurrentTabWidths(double* unselected_width,
    321                            double* selected_width) const;
    322 
    323   // Returns the exact (unrounded) desired width of each tab, based on the
    324   // desired strip width and number of tabs.  If
    325   // |width_of_tabs_for_mouse_close_| is nonnegative we use that value in
    326   // calculating the desired strip width; otherwise we use the current width.
    327   // |mini_tab_count| gives the number of mini-tabs, and |tab_count| the
    328   // number of mini and non-mini-tabs.
    329   void GetDesiredTabWidths(int tab_count,
    330                            int mini_tab_count,
    331                            double* unselected_width,
    332                            double* selected_width) const;
    333 
    334   // Returns the horizontal offset before the tab at |tab_index|.
    335   int GetTabHOffset(int tab_index);
    336 
    337   // Returns the x-coordinate tabs start from.
    338   int tab_start_x() const;
    339 
    340   // Perform an animated resize-relayout of the TabStrip immediately.
    341   void ResizeLayoutTabs();
    342 
    343   // Returns whether or not the cursor is currently in the "tab strip zone"
    344   // which is defined as the region above the TabStrip and a bit below it.
    345   bool IsCursorInTabStripZone() const;
    346 
    347   // Reset the Z-ordering of tabs.
    348   void ReStack();
    349 
    350   // Ensure that the message loop observer used for event spying is added and
    351   // removed appropriately so we can tell when to resize layout the tab strip.
    352   void AddMessageLoopObserver();
    353   void RemoveMessageLoopObserver();
    354 
    355   // Calculates the available width for tabs, assuming a Tab is to be closed.
    356   int GetAvailableWidthForTabs(TabGtk* last_tab) const;
    357 
    358   // Finds the index of the WebContents corresponding to |tab| in our
    359   // associated TabStripModel, or -1 if there is none (e.g. the specified |tab|
    360   // is being animated closed).
    361   int GetIndexOfTab(const TabGtk* tab) const;
    362 
    363   // Cleans up the tab from the TabStrip at the specified |index|.
    364   void RemoveTabAt(int index);
    365 
    366   // Called from the message loop observer when a mouse movement has occurred
    367   // anywhere over our containing window.
    368   void HandleGlobalMouseMoveEvent();
    369 
    370   // Generates the ideal bounds of the TabStrip when all Tabs have finished
    371   // animating to their desired position/bounds. This is used by the standard
    372   // Layout method and other callers like the DraggedTabController that need
    373   // stable representations of Tab positions.
    374   void GenerateIdealBounds();
    375 
    376   // Lays out the New Tab button, assuming the right edge of the last Tab on
    377   // the TabStrip at |last_tab_right|.  |unselected_width| is the width of
    378   // unselected tabs at the moment this function is called.  The value changes
    379   // during animations, so we can't use current_unselected_width_.
    380   void LayoutNewTabButton(double last_tab_right, double unselected_width);
    381 
    382   // -- Link Drag & Drop ------------------------------------------------------
    383 
    384   // Returns the bounds to render the drop at, in screen coordinates. Sets
    385   // |is_beneath| to indicate whether the arrow is beneath the tab, or above
    386   // it.
    387   gfx::Rect GetDropBounds(int drop_index, bool drop_before, bool* is_beneath);
    388 
    389   // Updates the location of the drop based on the event.
    390   void UpdateDropIndex(GdkDragContext* context, gint x, gint y);
    391 
    392   // Sets the location of the drop, repainting as necessary.
    393   void SetDropIndex(int index, bool drop_before);
    394 
    395   // Determines whether the data is acceptable by the tabstrip and opens a new
    396   // tab with the data as URL if it is.  Returns true if the drop was
    397   // successful.
    398   bool CompleteDrop(const guchar* data, bool is_plain_text);
    399 
    400   // Returns the image to use for indicating a drop on a tab. If is_down is
    401   // true, this returns an arrow pointing down.
    402   static gfx::Image* GetDropArrowImage(bool is_down);
    403 
    404   // -- Animations -------------------------------------------------------------
    405 
    406   // Stops the current animation.
    407   void StopAnimation();
    408 
    409   // A generic Layout method for various classes of TabStrip animations,
    410   // including Insert, Remove and Resize Layout cases.
    411   void AnimationLayout(double unselected_width);
    412 
    413   // Starts various types of TabStrip animations.
    414   void StartInsertTabAnimation(int index);
    415   void StartRemoveTabAnimation(int index, content::WebContents* contents);
    416   void StartMoveTabAnimation(int from_index, int to_index);
    417   void StartMiniTabAnimation(int index);
    418   void StartMiniMoveTabAnimation(int from_index,
    419                                  int to_index,
    420                                  const gfx::Rect& start_bounds);
    421   void StartResizeLayoutAnimation();
    422 
    423   // Notifies the TabStrip that the specified TabAnimation has completed.
    424   // Optionally a full Layout will be performed, specified by |layout|.
    425   void FinishAnimation(TabAnimation* animation, bool layout);
    426 
    427   // The Tabs we contain, and their last generated "good" bounds.
    428   std::vector<TabData> tab_data_;
    429 
    430   // The current widths of various types of tabs.  We save these so that, as
    431   // users close tabs while we're holding them at the same size, we can lay out
    432   // tabs exactly and eliminate the "pixel jitter" we'd get from just leaving
    433   // them all at their existing, rounded widths.
    434   double current_unselected_width_;
    435   double current_selected_width_;
    436 
    437   // If this value is nonnegative, it is used in GetDesiredTabWidths() to
    438   // calculate how much space in the tab strip to use for tabs.  Most of the
    439   // time this will be -1, but while we're handling closing a tab via the mouse,
    440   // we'll set this to the edge of the last tab before closing, so that if we
    441   // are closing the last tab and need to resize immediately, we'll resize only
    442   // back to this width, thus once again placing the last tab under the mouse
    443   // cursor.
    444   int available_width_for_tabs_;
    445 
    446   // True if a resize layout animation should be run a short delay after the
    447   // mouse exits the TabStrip.
    448   bool needs_resize_layout_;
    449 
    450   // The GtkFixed widget.
    451   ui::OwnedWidgetGtk tabstrip_;
    452 
    453   // The bounds of the tabstrip.
    454   gfx::Rect bounds_;
    455 
    456   // The amount to offset tab backgrounds when we are using an autogenerated
    457   // tab background image.
    458   int tab_vertical_offset_;
    459 
    460   // Our model.
    461   TabStripModel* model_;
    462 
    463   // The BrowserWindowGtk containing this tab strip.
    464   BrowserWindowGtk* window_;
    465 
    466   // Theme resources.
    467   GtkThemeService* theme_service_;
    468 
    469   // The currently running animation.
    470   scoped_ptr<TabAnimation> active_animation_;
    471 
    472   // The New Tab button.
    473   scoped_ptr<CustomDrawButton> newtab_button_;
    474 
    475   // The bounds of the bitmap surface used to paint the New Tab button.
    476   gfx::Rect newtab_surface_bounds_;
    477 
    478   // Valid for the lifetime of a drag over us.
    479   scoped_ptr<DropInfo> drop_info_;
    480 
    481   // The controller for a drag initiated from a Tab. Valid for the lifetime of
    482   // the drag session.
    483   scoped_ptr<DraggedTabControllerGtk> drag_controller_;
    484 
    485   // A factory that is used to construct a delayed callback to the
    486   // ResizeLayoutTabsNow method.
    487   base::WeakPtrFactory<TabStripGtk> weak_factory_;
    488 
    489   // A different factory for calls to Layout().
    490   base::WeakPtrFactory<TabStripGtk> layout_factory_;
    491 
    492   // True if the tabstrip has already been added as a MessageLoop observer.
    493   bool added_as_message_loop_observer_;
    494 
    495   // Helper for performing tab selection as a result of dragging over a tab.
    496   HoverTabSelector hover_tab_selector_;
    497 
    498   content::NotificationRegistrar registrar_;
    499 
    500   DISALLOW_COPY_AND_ASSIGN(TabStripGtk);
    501 };
    502 
    503 #endif  // CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_
    504