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_RENDERER_GTK_H_
      6 #define CHROME_BROWSER_UI_GTK_TABS_TAB_RENDERER_GTK_H_
      7 
      8 #include <gtk/gtk.h>
      9 #include <map>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/strings/string16.h"
     15 #include "chrome/browser/ui/tabs/tab_utils.h"
     16 #include "content/public/browser/notification_observer.h"
     17 #include "content/public/browser/notification_registrar.h"
     18 #include "third_party/skia/include/core/SkBitmap.h"
     19 #include "ui/base/gtk/gtk_signal.h"
     20 #include "ui/base/gtk/owned_widget_gtk.h"
     21 #include "ui/gfx/animation/animation_delegate.h"
     22 #include "ui/gfx/canvas.h"
     23 #include "ui/gfx/font.h"
     24 #include "ui/gfx/image/cairo_cached_surface.h"
     25 #include "ui/gfx/rect.h"
     26 
     27 namespace gfx {
     28 class CairoCachedSurface;
     29 class Image;
     30 class Size;
     31 class SlideAnimation;
     32 class ThrobAnimation;
     33 }  // namespace gfx
     34 
     35 class CustomDrawButton;
     36 class GtkThemeService;
     37 
     38 namespace content {
     39 class WebContents;
     40 }
     41 
     42 class TabRendererGtk : public gfx::AnimationDelegate,
     43                        public content::NotificationObserver {
     44  public:
     45   // Possible animation states.
     46   enum AnimationState {
     47     ANIMATION_NONE,
     48     ANIMATION_WAITING,
     49     ANIMATION_LOADING
     50   };
     51 
     52   class LoadingAnimation : public content::NotificationObserver {
     53    public:
     54     struct Data {
     55       explicit Data(GtkThemeService* theme_service);
     56       Data(int loading, int waiting, int waiting_to_loading);
     57 
     58       int loading_animation_frame_count;
     59       int waiting_animation_frame_count;
     60       int waiting_to_loading_frame_count_ratio;
     61     };
     62 
     63     explicit LoadingAnimation(GtkThemeService* theme_service);
     64 
     65     // Used in unit tests to inject specific data.
     66     explicit LoadingAnimation(const LoadingAnimation::Data& data);
     67 
     68     virtual ~LoadingAnimation();
     69 
     70     // Advance the loading animation to the next frame, or hide the animation if
     71     // the tab isn't loading. Returns |true| if the icon area needs to be
     72     // repainted.
     73     bool ValidateLoadingAnimation(AnimationState animation_state);
     74 
     75     AnimationState animation_state() const { return animation_state_; }
     76     int animation_frame() const { return animation_frame_; }
     77 
     78     // Provide content::NotificationObserver implementation.
     79     virtual void Observe(int type,
     80                          const content::NotificationSource& source,
     81                          const content::NotificationDetails& details) OVERRIDE;
     82 
     83    private:
     84     scoped_ptr<Data> data_;
     85 
     86     // Used to listen for theme change notifications.
     87     content::NotificationRegistrar registrar_;
     88 
     89     // Gives us our throbber images.
     90     GtkThemeService* theme_service_;
     91 
     92     // Current state of the animation.
     93     AnimationState animation_state_;
     94 
     95     // The current index into the Animation image strip.
     96     int animation_frame_;
     97 
     98     DISALLOW_COPY_AND_ASSIGN(LoadingAnimation);
     99   };
    100 
    101   explicit TabRendererGtk(GtkThemeService* theme_service);
    102   virtual ~TabRendererGtk();
    103 
    104   // Provide content::NotificationObserver implementation.
    105   virtual void Observe(int type,
    106                        const content::NotificationSource& source,
    107                        const content::NotificationDetails& details) OVERRIDE;
    108 
    109   // WebContents. If only the loading state was updated, the loading_only flag
    110   // should be specified. If other things change, set this flag to false to
    111   // update everything.
    112   virtual void UpdateData(content::WebContents* contents,
    113                           bool app,
    114                           bool loading_only);
    115 
    116   // Sets the blocked state of the tab.
    117   void SetBlocked(bool pinned);
    118   bool is_blocked() const;
    119 
    120   // Sets the mini-state of the tab.
    121   void set_mini(bool mini) { data_.mini = mini; }
    122   bool mini() const { return data_.mini; }
    123 
    124   // Sets the app state of the tab.
    125   void set_app(bool app) { data_.app = app; }
    126   bool app() const { return data_.app; }
    127 
    128   // Are we in the process of animating a mini tab state change on this tab?
    129   void set_animating_mini_change(bool value) {
    130     data_.animating_mini_change = value;
    131   }
    132 
    133   // Updates the display to reflect the contents of this TabRenderer's model.
    134   void UpdateFromModel();
    135 
    136   // Returns true if the Tab is active, false otherwise.
    137   virtual bool IsActive() const;
    138 
    139   // Set |is_active_| property of this tab.
    140   void set_is_active(bool is_active) { is_active_ = is_active; }
    141 
    142   // Returns true if the Tab is selected, false otherwise.
    143   virtual bool IsSelected() const;
    144 
    145   // Returns true if the Tab is visible, false otherwise.
    146   virtual bool IsVisible() const;
    147 
    148   // Sets the visibility of the Tab.
    149   virtual void SetVisible(bool visible) const;
    150 
    151   // Paints the tab using resources from the display that |widget| is on,
    152   // drawing into |cr|.
    153   void Paint(GtkWidget* widget, cairo_t* cr);
    154 
    155   // Paints the tab, and keeps the result server-side. The returned surface must
    156   // be freed with cairo_surface_destroy().
    157   cairo_surface_t* PaintToSurface(GtkWidget* widget, cairo_t* cr);
    158 
    159   // There is no PaintNow available, so the fastest we can do is schedule a
    160   // paint with the windowing system.
    161   void SchedulePaint();
    162 
    163   // Notifies the Tab that the close button has been clicked.
    164   virtual void CloseButtonClicked();
    165 
    166   // Sets the bounds of the tab.
    167   virtual void SetBounds(const gfx::Rect& bounds);
    168 
    169   // Advance the loading animation to the next frame, or hide the animation if
    170   // the tab isn't loading.  Returns |true| if the icon area needs to be
    171   // repainted.
    172   bool ValidateLoadingAnimation(AnimationState animation_state);
    173 
    174   // Repaint only the area of the tab that contains the favicon.
    175   void PaintFaviconArea(GtkWidget* widget, cairo_t* cr);
    176 
    177   // Returns whether the Tab should display a favicon.
    178   bool ShouldShowIcon() const;
    179 
    180   // Invoked from Layout() to adjust the position of the favicon or media
    181   // indicator for mini tabs.
    182   void MaybeAdjustLeftForMiniTab(gfx::Rect* bounds) const;
    183 
    184   // Returns the minimum possible size of a single unselected Tab.
    185   static gfx::Size GetMinimumUnselectedSize();
    186   // Returns the minimum possible size of a selected Tab. Selected tabs must
    187   // always show a close button and have a larger minimum size than unselected
    188   // tabs.
    189   static gfx::Size GetMinimumSelectedSize();
    190   // Returns the preferred size of a single Tab, assuming space is
    191   // available.
    192   static gfx::Size GetStandardSize();
    193 
    194   // Returns the width for mini-tabs. Mini-tabs always have this width.
    195   static int GetMiniWidth();
    196 
    197   static gfx::Font* title_font() { return title_font_; }
    198 
    199   // Returns the bounds of the Tab.
    200   int x() const { return bounds_.x(); }
    201   int y() const { return bounds_.y(); }
    202   int width() const { return bounds_.width(); }
    203   int height() const { return bounds_.height(); }
    204 
    205   gfx::Rect bounds() const { return bounds_; }
    206 
    207   gfx::Rect favicon_bounds() const { return favicon_bounds_; }
    208 
    209   // Returns the non-mirrored (LTR) bounds of this tab.
    210   gfx::Rect GetNonMirroredBounds(GtkWidget* parent) const;
    211 
    212   // Returns the requested bounds of the tab.
    213   gfx::Rect GetRequisition() const;
    214 
    215   GtkWidget* widget() const { return tab_.get(); }
    216 
    217   // Start/stop the mini-tab title animation.
    218   void StartMiniTabTitleAnimation();
    219   void StopMiniTabTitleAnimation();
    220 
    221   void set_vertical_offset(int offset) { background_offset_y_ = offset; }
    222 
    223  protected:
    224   const gfx::Rect& title_bounds() const { return title_bounds_; }
    225   const gfx::Rect& close_button_bounds() const { return close_button_bounds_; }
    226 
    227   // Raise button to top of Z-order.
    228   void Raise() const;
    229 
    230   // Returns the title of the Tab.
    231   base::string16 GetTitle() const;
    232 
    233   // enter-notify-event handler that signals when the mouse enters the tab.
    234   CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnEnterNotifyEvent,
    235                        GdkEventCrossing*);
    236 
    237   // leave-notify-event handler that signals when the mouse enters the tab.
    238   CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnLeaveNotifyEvent,
    239                        GdkEventCrossing*);
    240 
    241  private:
    242   class FaviconCrashAnimation;
    243 
    244   // Model data. We store this here so that we don't need to ask the underlying
    245   // model, which is tricky since instances of this object can outlive the
    246   // corresponding objects in the underlying model.
    247   struct TabData {
    248     TabData();
    249     ~TabData();
    250 
    251     SkBitmap favicon;
    252     gfx::CairoCachedSurface cairo_favicon;
    253     bool is_default_favicon;
    254     base::string16 title;
    255     bool loading;
    256     bool crashed;
    257     bool incognito;
    258     bool show_icon;
    259     bool mini;
    260     bool blocked;
    261     bool animating_mini_change;
    262     bool app;
    263     TabMediaState media_state;
    264     TabMediaState previous_media_state;
    265   };
    266 
    267   // Overridden from gfx::AnimationDelegate:
    268   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
    269   virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE;
    270   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
    271 
    272   // Starts/Stops the crash animation.
    273   void StartCrashAnimation();
    274   void StopCrashAnimation();
    275 
    276   // Return true if the crash animation is currently running.
    277   bool IsPerformingCrashAnimation() const;
    278 
    279   // Starts the media indicator fade-in/out animation. There's no stop method
    280   // because this is not a continuous animation.
    281   void StartMediaIndicatorAnimation();
    282 
    283   // Set the temporary offset for the favicon. This is used during animation.
    284   void SetFaviconHidingOffset(int offset);
    285 
    286   void DisplayCrashedFavicon();
    287   void ResetCrashedFavicon();
    288 
    289   // Generates the bounds for the interior items of the tab.
    290   void Layout();
    291 
    292   // Returns the local bounds of the tab.  This returns the rect
    293   // {0, 0, width(), height()} for now, as we don't yet support borders.
    294   gfx::Rect GetLocalBounds();
    295 
    296   // Moves the close button widget within the GtkFixed container.
    297   void MoveCloseButtonWidget();
    298 
    299   // Returns the largest of the favicon, title text, and the close button.
    300   static int GetContentHeight();
    301 
    302   void PaintTab(GtkWidget* widget, GdkEventExpose* event);
    303 
    304   // Paint various portions of the Tab
    305   void PaintTitle(GtkWidget* widget, cairo_t* cr);
    306   void PaintIcon(GtkWidget* widget, cairo_t* cr);
    307   void PaintMediaIndicator(GtkWidget* widget, cairo_t* cr);
    308   void PaintTabBackground(GtkWidget* widget, cairo_t* cr);
    309   void PaintInactiveTabBackground(GtkWidget* widget, cairo_t* cr);
    310   void PaintActiveTabBackground(GtkWidget* widget, cairo_t* cr);
    311   void PaintLoadingAnimation(GtkWidget* widget, cairo_t* cairo);
    312 
    313   // Draws the given |tab_bg| onto |cr| using the tab shape masks along the
    314   // sides for the rounded tab shape.
    315   void DrawTabBackground(cairo_t* cr,
    316                          GtkWidget* widget,
    317                          const gfx::Image& tab_bg,
    318                          int offset_x,
    319                          int offset_y);
    320 
    321   // Draws the tab shadow using the given idr resources onto |cr|.
    322   void DrawTabShadow(cairo_t* cr,
    323                      GtkWidget* widget,
    324                      int left_idr,
    325                      int center_idr,
    326                      int right_idr);
    327 
    328   // Returns the number of favicon-size elements that can fit in the tab's
    329   // current size.
    330   int IconCapacity() const;
    331 
    332   // Returns whether the Tab should display the media indicator.
    333   bool ShouldShowMediaIndicator() const;
    334 
    335   // Returns whether the Tab should display a close button.
    336   bool ShouldShowCloseBox() const;
    337 
    338   CustomDrawButton* MakeCloseButton();
    339 
    340   // Gets the throb value for the tab. When a tab is not selected the
    341   // active background is drawn at |GetThrobValue()|%. This is used for hover
    342   // and mini-tab title change effects.
    343   double GetThrobValue();
    344 
    345   // Handles the clicked signal for the close button.
    346   CHROMEGTK_CALLBACK_0(TabRendererGtk, void, OnCloseButtonClicked);
    347 
    348   // Handles middle clicking the close button.
    349   CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnCloseButtonMouseRelease,
    350                        GdkEventButton*);
    351 
    352   // expose-event handler that redraws the tab.
    353   CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnExposeEvent,
    354                        GdkEventExpose*);
    355 
    356   // size-allocate handler used to update the current bounds of the tab.
    357   CHROMEGTK_CALLBACK_1(TabRendererGtk, void, OnSizeAllocate, GtkAllocation*);
    358 
    359   // TODO(jhawkins): Move to TabResources.
    360   static void InitResources();
    361   static bool initialized_;
    362 
    363   // The bounds of various sections of the display.
    364   gfx::Rect favicon_bounds_;
    365   gfx::Rect title_bounds_;
    366   gfx::Rect media_indicator_bounds_;
    367   gfx::Rect close_button_bounds_;
    368 
    369   TabData data_;
    370 
    371   static int tab_active_l_width_;
    372   static int tab_active_l_height_;
    373   static int tab_inactive_l_width_;
    374   static int tab_inactive_l_height_;
    375 
    376   static gfx::Font* title_font_;
    377   static int title_font_height_;
    378 
    379   static int close_button_width_;
    380   static int close_button_height_;
    381 
    382   content::NotificationRegistrar registrar_;
    383 
    384   // The GtkDrawingArea we draw the tab on.
    385   ui::OwnedWidgetGtk tab_;
    386 
    387   // Whether we're showing the icon. It is cached so that we can detect when it
    388   // changes and layout appropriately.
    389   bool showing_icon_;
    390 
    391   // Whether we're showing the media indicator. It is cached so that we can
    392   // detect when it changes and layout appropriately.
    393   bool showing_media_indicator_;
    394 
    395   // Whether we are showing the close button. It is cached so that we can
    396   // detect when it changes and layout appropriately.
    397   bool showing_close_button_;
    398 
    399   // The offset used to animate the favicon location.
    400   int favicon_hiding_offset_;
    401 
    402   // The animation object used to swap the favicon with the sad tab icon.
    403   scoped_ptr<FaviconCrashAnimation> crash_animation_;
    404 
    405   // Set when the crashed favicon should be displayed.
    406   bool should_display_crashed_favicon_;
    407 
    408   // The bounds of this Tab.
    409   gfx::Rect bounds_;
    410 
    411   // The requested bounds of this tab.  These bounds are relative to the
    412   // tabstrip.
    413   gfx::Rect requisition_;
    414 
    415   // Hover animation.
    416   scoped_ptr<gfx::SlideAnimation> hover_animation_;
    417 
    418   // Animation used when the title of an inactive mini-tab changes.
    419   scoped_ptr<gfx::ThrobAnimation> mini_title_animation_;
    420 
    421   // Media indicator fade-in/out animation (i.e., only on show/hide, not a
    422   // continuous animation).
    423   scoped_ptr<gfx::Animation> media_indicator_animation_;
    424   TabMediaState animating_media_state_;
    425 
    426   // Contains the loading animation state.
    427   LoadingAnimation loading_animation_;
    428 
    429   // The offset used to paint the tab theme images.
    430   int background_offset_x_;
    431 
    432   // The vertical offset used to paint the tab theme images. Controlled by the
    433   // tabstrip and plumbed here to offset the theme image by the size of the
    434   // alignment in the BrowserTitlebar.
    435   int background_offset_y_;
    436 
    437   GtkThemeService* theme_service_;
    438 
    439   // The close button.
    440   scoped_ptr<CustomDrawButton> close_button_;
    441 
    442   // The current color of the close button.
    443   SkColor close_button_color_;
    444 
    445   // Indicates whether this tab is the active one.
    446   bool is_active_;
    447 
    448   // Color of the title text on the selected tab.
    449   SkColor selected_title_color_;
    450 
    451   // Color of the title text on an unselected tab.
    452   SkColor unselected_title_color_;
    453 
    454   DISALLOW_COPY_AND_ASSIGN(TabRendererGtk);
    455 };
    456 
    457 #endif  // CHROME_BROWSER_UI_GTK_TABS_TAB_RENDERER_GTK_H_
    458