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