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
25namespace gfx {
26class Size;
27}  // namespace gfx
28
29class CustomDrawButton;
30class GtkThemeService;
31class TabContents;
32
33namespace ui {
34class SlideAnimation;
35class ThemeProvider;
36class ThrobAnimation;
37}
38
39class 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