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