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 animation_state()74 AnimationState animation_state() const { return animation_state_; } animation_frame()75 int animation_frame() const { return animation_frame_; } 76 waiting_animation_frames()77 const SkBitmap* waiting_animation_frames() const { 78 return data_->waiting_animation_frames; 79 } loading_animation_frames()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. set_mini(bool mini)120 void set_mini(bool mini) { data_.mini = mini; } mini()121 bool mini() const { return data_.mini; } 122 123 // Sets the app state of the tab. set_app(bool app)124 void set_app(bool app) { data_.app = 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? set_animating_mini_change(bool value)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 title_font()200 static gfx::Font* title_font() { return title_font_; } 201 202 // Returns the bounds of the Tab. x()203 int x() const { return bounds_.x(); } y()204 int y() const { return bounds_.y(); } width()205 int width() const { return bounds_.width(); } height()206 int height() const { return bounds_.height(); } 207 bounds()208 gfx::Rect bounds() const { return bounds_; } 209 favicon_bounds()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 widget()218 GtkWidget* widget() const { return tab_.get(); } 219 220 // Start/stop the mini-tab title animation. 221 void StartMiniTabTitleAnimation(); 222 void StopMiniTabTitleAnimation(); 223 set_vertical_offset(int offset)224 void set_vertical_offset(int offset) { background_offset_y_ = offset; } 225 226 protected: title_bounds()227 const gfx::Rect& title_bounds() const { return title_bounds_; } close_button_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 { TabDataTabData259 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