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