| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/gtk/tabs/tab_renderer_gtk.h" | 5 #include "chrome/browser/ui/gtk/tabs/tab_renderer_gtk.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/browser/defaults.h" | 11 #include "chrome/browser/defaults.h" |
| 12 #include "chrome/browser/extensions/extension_tab_helper.h" | 12 #include "chrome/browser/extensions/extension_tab_helper.h" |
| 13 #include "chrome/browser/favicon/favicon_tab_helper.h" | 13 #include "chrome/browser/favicon/favicon_tab_helper.h" |
| 14 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/browser/ui/browser.h" | 15 #include "chrome/browser/ui/browser.h" |
| 16 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h" | 16 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h" |
| 17 #include "chrome/browser/ui/gtk/cairo_cached_surface.h" |
| 17 #include "chrome/browser/ui/gtk/custom_button.h" | 18 #include "chrome/browser/ui/gtk/custom_button.h" |
| 18 #include "chrome/browser/ui/gtk/gtk_theme_service.h" | 19 #include "chrome/browser/ui/gtk/gtk_theme_service.h" |
| 19 #include "chrome/browser/ui/gtk/gtk_util.h" | 20 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 20 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 21 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 21 #include "chrome/common/chrome_notification_types.h" | 22 #include "chrome/common/chrome_notification_types.h" |
| 22 #include "content/browser/tab_contents/tab_contents.h" | 23 #include "content/browser/tab_contents/tab_contents.h" |
| 23 #include "content/public/browser/notification_source.h" | 24 #include "content/public/browser/notification_source.h" |
| 24 #include "grit/generated_resources.h" | 25 #include "grit/generated_resources.h" |
| 25 #include "grit/theme_resources.h" | 26 #include "grit/theme_resources.h" |
| 26 #include "grit/theme_resources_standard.h" | 27 #include "grit/theme_resources_standard.h" |
| 27 #include "grit/ui_resources.h" | 28 #include "grit/ui_resources.h" |
| 28 #include "ui/base/animation/slide_animation.h" | 29 #include "ui/base/animation/slide_animation.h" |
| 29 #include "ui/base/animation/throb_animation.h" | 30 #include "ui/base/animation/throb_animation.h" |
| 30 #include "ui/base/l10n/l10n_util.h" | 31 #include "ui/base/l10n/l10n_util.h" |
| 31 #include "ui/base/resource/resource_bundle.h" | 32 #include "ui/base/resource/resource_bundle.h" |
| 32 #include "ui/gfx/canvas_skia_paint.h" | 33 #include "ui/gfx/canvas_skia_paint.h" |
| 33 #include "ui/gfx/favicon_size.h" | 34 #include "ui/gfx/favicon_size.h" |
| 35 #include "ui/gfx/gtk_util.h" |
| 36 #include "ui/gfx/pango_util.h" |
| 34 #include "ui/gfx/platform_font_pango.h" | 37 #include "ui/gfx/platform_font_pango.h" |
| 35 #include "ui/gfx/skbitmap_operations.h" | 38 #include "ui/gfx/skbitmap_operations.h" |
| 39 #include "skia/ext/image_operations.h" |
| 36 | 40 |
| 37 #if !GTK_CHECK_VERSION(2, 22, 0) | 41 #if !GTK_CHECK_VERSION(2, 22, 0) |
| 38 #define gtk_button_get_event_window(button) button->event_window | 42 #define gtk_button_get_event_window(button) button->event_window |
| 39 #endif // Gtk+ >= 2.22 | 43 #endif // Gtk+ >= 2.22 |
| 40 | 44 |
| 41 namespace { | 45 namespace { |
| 42 | 46 |
| 43 const int kFontPixelSize = 12; | 47 const int kFontPixelSize = 12; |
| 44 const int kLeftPadding = 16; | 48 const int kLeftPadding = 16; |
| 45 const int kTopPadding = 6; | 49 const int kTopPadding = 6; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 74 const double kMiniTitleChangeThrobOpacity = 0.75; | 78 const double kMiniTitleChangeThrobOpacity = 0.75; |
| 75 | 79 |
| 76 // Duration for when the title of an inactive mini-tab changes. | 80 // Duration for when the title of an inactive mini-tab changes. |
| 77 const int kMiniTitleChangeThrobDuration = 1000; | 81 const int kMiniTitleChangeThrobDuration = 1000; |
| 78 | 82 |
| 79 // The vertical and horizontal offset used to position the close button | 83 // The vertical and horizontal offset used to position the close button |
| 80 // in the tab. TODO(jhawkins): Ask pkasting what the Fuzz is about. | 84 // in the tab. TODO(jhawkins): Ask pkasting what the Fuzz is about. |
| 81 const int kCloseButtonVertFuzz = 0; | 85 const int kCloseButtonVertFuzz = 0; |
| 82 const int kCloseButtonHorzFuzz = 5; | 86 const int kCloseButtonHorzFuzz = 5; |
| 83 | 87 |
| 84 SkBitmap* crashed_favicon = NULL; | |
| 85 | |
| 86 // Gets the bounds of |widget| relative to |parent|. | 88 // Gets the bounds of |widget| relative to |parent|. |
| 87 gfx::Rect GetWidgetBoundsRelativeToParent(GtkWidget* parent, | 89 gfx::Rect GetWidgetBoundsRelativeToParent(GtkWidget* parent, |
| 88 GtkWidget* widget) { | 90 GtkWidget* widget) { |
| 89 gfx::Point parent_pos = gtk_util::GetWidgetScreenPosition(parent); | 91 gfx::Point parent_pos = gtk_util::GetWidgetScreenPosition(parent); |
| 90 gfx::Point widget_pos = gtk_util::GetWidgetScreenPosition(widget); | 92 gfx::Point widget_pos = gtk_util::GetWidgetScreenPosition(widget); |
| 91 return gfx::Rect(widget_pos.x() - parent_pos.x(), | 93 return gfx::Rect(widget_pos.x() - parent_pos.x(), |
| 92 widget_pos.y() - parent_pos.y(), | 94 widget_pos.y() - parent_pos.y(), |
| 93 widget->allocation.width, widget->allocation.height); | 95 widget->allocation.width, widget->allocation.height); |
| 94 } | 96 } |
| 95 | 97 |
| 96 } // namespace | 98 } // namespace |
| 97 | 99 |
| 98 TabRendererGtk::LoadingAnimation::Data::Data( | 100 TabRendererGtk::LoadingAnimation::Data::Data( |
| 99 ThemeService* theme_service) { | 101 GtkThemeService* theme_service) { |
| 100 // The loading animation image is a strip of states. Each state must be | 102 // The loading animation image is a strip of states. Each state must be |
| 101 // square, so the height must divide the width evenly. | 103 // square, so the height must divide the width evenly. |
| 102 loading_animation_frames = theme_service->GetBitmapNamed(IDR_THROBBER); | 104 SkBitmap* loading_animation_frames = |
| 105 theme_service->GetBitmapNamed(IDR_THROBBER); |
| 103 DCHECK(loading_animation_frames); | 106 DCHECK(loading_animation_frames); |
| 104 DCHECK_EQ(loading_animation_frames->width() % | 107 DCHECK_EQ(loading_animation_frames->width() % |
| 105 loading_animation_frames->height(), 0); | 108 loading_animation_frames->height(), 0); |
| 106 loading_animation_frame_count = | 109 loading_animation_frame_count = |
| 107 loading_animation_frames->width() / | 110 loading_animation_frames->width() / |
| 108 loading_animation_frames->height(); | 111 loading_animation_frames->height(); |
| 109 | 112 |
| 110 waiting_animation_frames = | 113 SkBitmap* waiting_animation_frames = |
| 111 theme_service->GetBitmapNamed(IDR_THROBBER_WAITING); | 114 theme_service->GetBitmapNamed(IDR_THROBBER_WAITING); |
| 112 DCHECK(waiting_animation_frames); | 115 DCHECK(waiting_animation_frames); |
| 113 DCHECK_EQ(waiting_animation_frames->width() % | 116 DCHECK_EQ(waiting_animation_frames->width() % |
| 114 waiting_animation_frames->height(), 0); | 117 waiting_animation_frames->height(), 0); |
| 115 waiting_animation_frame_count = | 118 waiting_animation_frame_count = |
| 116 waiting_animation_frames->width() / | 119 waiting_animation_frames->width() / |
| 117 waiting_animation_frames->height(); | 120 waiting_animation_frames->height(); |
| 118 | 121 |
| 119 waiting_to_loading_frame_count_ratio = | 122 waiting_to_loading_frame_count_ratio = |
| 120 waiting_animation_frame_count / | 123 waiting_animation_frame_count / |
| 121 loading_animation_frame_count; | 124 loading_animation_frame_count; |
| 122 // TODO(beng): eventually remove this when we have a proper themeing system. | 125 // TODO(beng): eventually remove this when we have a proper themeing system. |
| 123 // themes not supporting IDR_THROBBER_WAITING are causing this | 126 // themes not supporting IDR_THROBBER_WAITING are causing this |
| 124 // value to be 0 which causes DIV0 crashes. The value of 5 | 127 // value to be 0 which causes DIV0 crashes. The value of 5 |
| 125 // matches the current bitmaps in our source. | 128 // matches the current bitmaps in our source. |
| 126 if (waiting_to_loading_frame_count_ratio == 0) | 129 if (waiting_to_loading_frame_count_ratio == 0) |
| 127 waiting_to_loading_frame_count_ratio = 5; | 130 waiting_to_loading_frame_count_ratio = 5; |
| 128 } | 131 } |
| 129 | 132 |
| 130 TabRendererGtk::LoadingAnimation::Data::Data( | 133 TabRendererGtk::LoadingAnimation::Data::Data( |
| 131 int loading, int waiting, int waiting_to_loading) | 134 int loading, int waiting, int waiting_to_loading) |
| 132 : waiting_animation_frames(NULL), | 135 : loading_animation_frame_count(loading), |
| 133 loading_animation_frames(NULL), | |
| 134 loading_animation_frame_count(loading), | |
| 135 waiting_animation_frame_count(waiting), | 136 waiting_animation_frame_count(waiting), |
| 136 waiting_to_loading_frame_count_ratio(waiting_to_loading) { | 137 waiting_to_loading_frame_count_ratio(waiting_to_loading) { |
| 137 } | 138 } |
| 138 | 139 |
| 139 bool TabRendererGtk::initialized_ = false; | 140 bool TabRendererGtk::initialized_ = false; |
| 140 TabRendererGtk::TabImage TabRendererGtk::tab_active_ = {0}; | 141 int TabRendererGtk::tab_active_l_width_ = 0; |
| 141 TabRendererGtk::TabImage TabRendererGtk::tab_inactive_ = {0}; | 142 int TabRendererGtk::tab_active_l_height_ = 0; |
| 142 TabRendererGtk::TabImage TabRendererGtk::tab_alpha_ = {0}; | 143 int TabRendererGtk::tab_inactive_l_height_ = 0; |
| 143 gfx::Font* TabRendererGtk::title_font_ = NULL; | 144 gfx::Font* TabRendererGtk::title_font_ = NULL; |
| 144 int TabRendererGtk::title_font_height_ = 0; | 145 int TabRendererGtk::title_font_height_ = 0; |
| 145 int TabRendererGtk::close_button_width_ = 0; | 146 int TabRendererGtk::close_button_width_ = 0; |
| 146 int TabRendererGtk::close_button_height_ = 0; | 147 int TabRendererGtk::close_button_height_ = 0; |
| 147 SkColor TabRendererGtk::selected_title_color_ = SK_ColorBLACK; | 148 SkColor TabRendererGtk::selected_title_color_ = SK_ColorBLACK; |
| 148 SkColor TabRendererGtk::unselected_title_color_ = SkColorSetRGB(64, 64, 64); | 149 SkColor TabRendererGtk::unselected_title_color_ = SkColorSetRGB(64, 64, 64); |
| 149 | 150 |
| 150 //////////////////////////////////////////////////////////////////////////////// | 151 //////////////////////////////////////////////////////////////////////////////// |
| 151 // TabRendererGtk::LoadingAnimation, public: | 152 // TabRendererGtk::LoadingAnimation, public: |
| 152 // | 153 // |
| 153 TabRendererGtk::LoadingAnimation::LoadingAnimation( | 154 TabRendererGtk::LoadingAnimation::LoadingAnimation( |
| 154 ThemeService* theme_service) | 155 GtkThemeService* theme_service) |
| 155 : data_(new Data(theme_service)), | 156 : data_(new Data(theme_service)), |
| 156 theme_service_(theme_service), | 157 theme_service_(theme_service), |
| 157 animation_state_(ANIMATION_NONE), | 158 animation_state_(ANIMATION_NONE), |
| 158 animation_frame_(0) { | 159 animation_frame_(0) { |
| 159 registrar_.Add(this, | 160 registrar_.Add(this, |
| 160 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | 161 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, |
| 161 content::Source<ThemeService>(theme_service_)); | 162 content::Source<ThemeService>(theme_service_)); |
| 162 } | 163 } |
| 163 | 164 |
| 164 TabRendererGtk::LoadingAnimation::LoadingAnimation( | 165 TabRendererGtk::LoadingAnimation::LoadingAnimation( |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 } | 202 } |
| 202 | 203 |
| 203 void TabRendererGtk::LoadingAnimation::Observe( | 204 void TabRendererGtk::LoadingAnimation::Observe( |
| 204 int type, | 205 int type, |
| 205 const content::NotificationSource& source, | 206 const content::NotificationSource& source, |
| 206 const content::NotificationDetails& details) { | 207 const content::NotificationDetails& details) { |
| 207 DCHECK(type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED); | 208 DCHECK(type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED); |
| 208 data_.reset(new Data(theme_service_)); | 209 data_.reset(new Data(theme_service_)); |
| 209 } | 210 } |
| 210 | 211 |
| 212 TabRendererGtk::TabData::TabData() |
| 213 : is_default_favicon(false), |
| 214 loading(false), |
| 215 crashed(false), |
| 216 incognito(false), |
| 217 show_icon(true), |
| 218 mini(false), |
| 219 blocked(false), |
| 220 animating_mini_change(false), |
| 221 app(false) { |
| 222 } |
| 223 |
| 224 TabRendererGtk::TabData::~TabData() {} |
| 225 |
| 211 //////////////////////////////////////////////////////////////////////////////// | 226 //////////////////////////////////////////////////////////////////////////////// |
| 212 // FaviconCrashAnimation | 227 // FaviconCrashAnimation |
| 213 // | 228 // |
| 214 // A custom animation subclass to manage the favicon crash animation. | 229 // A custom animation subclass to manage the favicon crash animation. |
| 215 class TabRendererGtk::FaviconCrashAnimation : public ui::LinearAnimation, | 230 class TabRendererGtk::FaviconCrashAnimation : public ui::LinearAnimation, |
| 216 public ui::AnimationDelegate { | 231 public ui::AnimationDelegate { |
| 217 public: | 232 public: |
| 218 explicit FaviconCrashAnimation(TabRendererGtk* target) | 233 explicit FaviconCrashAnimation(TabRendererGtk* target) |
| 219 : ALLOW_THIS_IN_INITIALIZER_LIST(ui::LinearAnimation(1000, 25, this)), | 234 : ALLOW_THIS_IN_INITIALIZER_LIST(ui::LinearAnimation(1000, 25, this)), |
| 220 target_(target) { | 235 target_(target) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 243 | 258 |
| 244 private: | 259 private: |
| 245 TabRendererGtk* target_; | 260 TabRendererGtk* target_; |
| 246 | 261 |
| 247 DISALLOW_COPY_AND_ASSIGN(FaviconCrashAnimation); | 262 DISALLOW_COPY_AND_ASSIGN(FaviconCrashAnimation); |
| 248 }; | 263 }; |
| 249 | 264 |
| 250 //////////////////////////////////////////////////////////////////////////////// | 265 //////////////////////////////////////////////////////////////////////////////// |
| 251 // TabRendererGtk, public: | 266 // TabRendererGtk, public: |
| 252 | 267 |
| 253 TabRendererGtk::TabRendererGtk(ThemeService* theme_service) | 268 TabRendererGtk::TabRendererGtk(GtkThemeService* theme_service) |
| 254 : showing_icon_(false), | 269 : showing_icon_(false), |
| 255 showing_close_button_(false), | 270 showing_close_button_(false), |
| 256 favicon_hiding_offset_(0), | 271 favicon_hiding_offset_(0), |
| 257 should_display_crashed_favicon_(false), | 272 should_display_crashed_favicon_(false), |
| 258 loading_animation_(theme_service), | 273 loading_animation_(theme_service), |
| 259 background_offset_x_(0), | 274 background_offset_x_(0), |
| 260 background_offset_y_(kInactiveTabBackgroundOffsetY), | 275 background_offset_y_(kInactiveTabBackgroundOffsetY), |
| 261 theme_service_(theme_service), | 276 theme_service_(theme_service), |
| 262 close_button_color_(0), | 277 close_button_color_(0), |
| 263 is_active_(false) { | 278 is_active_(false) { |
| 264 InitResources(); | 279 InitResources(); |
| 265 | 280 |
| 266 tab_.Own(gtk_fixed_new()); | 281 tab_.Own(gtk_fixed_new()); |
| 267 gtk_widget_set_app_paintable(tab_.get(), TRUE); | 282 gtk_widget_set_app_paintable(tab_.get(), TRUE); |
| 268 g_signal_connect(tab_.get(), "expose-event", | 283 g_signal_connect(tab_.get(), "expose-event", |
| 269 G_CALLBACK(OnExposeEventThunk), this); | 284 G_CALLBACK(OnExposeEventThunk), this); |
| 270 g_signal_connect(tab_.get(), "size-allocate", | 285 g_signal_connect(tab_.get(), "size-allocate", |
| 271 G_CALLBACK(OnSizeAllocateThunk), this); | 286 G_CALLBACK(OnSizeAllocateThunk), this); |
| 272 close_button_.reset(MakeCloseButton()); | 287 close_button_.reset(MakeCloseButton()); |
| 273 gtk_widget_show(tab_.get()); | 288 gtk_widget_show(tab_.get()); |
| 274 | 289 |
| 275 hover_animation_.reset(new ui::SlideAnimation(this)); | 290 hover_animation_.reset(new ui::SlideAnimation(this)); |
| 276 hover_animation_->SetSlideDuration(kHoverDurationMs); | 291 hover_animation_->SetSlideDuration(kHoverDurationMs); |
| 277 | |
| 278 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
| 279 content::Source<ThemeService>(theme_service_)); | |
| 280 } | 292 } |
| 281 | 293 |
| 282 TabRendererGtk::~TabRendererGtk() { | 294 TabRendererGtk::~TabRendererGtk() { |
| 283 tab_.Destroy(); | 295 tab_.Destroy(); |
| 284 for (BitmapCache::iterator it = cached_bitmaps_.begin(); | |
| 285 it != cached_bitmaps_.end(); ++it) { | |
| 286 delete it->second.bitmap; | |
| 287 } | |
| 288 } | 296 } |
| 289 | 297 |
| 290 void TabRendererGtk::UpdateData(TabContents* contents, | 298 void TabRendererGtk::UpdateData(TabContents* contents, |
| 291 bool app, | 299 bool app, |
| 292 bool loading_only) { | 300 bool loading_only) { |
| 293 DCHECK(contents); | 301 DCHECK(contents); |
| 294 TabContentsWrapper* wrapper = | 302 TabContentsWrapper* wrapper = |
| 295 TabContentsWrapper::GetCurrentWrapperForContents(contents); | 303 TabContentsWrapper::GetCurrentWrapperForContents(contents); |
| 296 | 304 |
| 297 if (!loading_only) { | 305 if (!loading_only) { |
| 298 data_.title = contents->GetTitle(); | 306 data_.title = contents->GetTitle(); |
| 299 data_.incognito = contents->browser_context()->IsOffTheRecord(); | 307 data_.incognito = contents->browser_context()->IsOffTheRecord(); |
| 300 data_.crashed = contents->is_crashed(); | 308 data_.crashed = contents->is_crashed(); |
| 301 | 309 |
| 302 SkBitmap* app_icon = | 310 SkBitmap* app_icon = |
| 303 TabContentsWrapper::GetCurrentWrapperForContents(contents)-> | 311 TabContentsWrapper::GetCurrentWrapperForContents(contents)-> |
| 304 extension_tab_helper()->GetExtensionAppIcon(); | 312 extension_tab_helper()->GetExtensionAppIcon(); |
| 305 if (app_icon) | 313 if (app_icon) { |
| 306 data_.favicon = *app_icon; | 314 data_.favicon = *app_icon; |
| 307 else | 315 } else { |
| 308 data_.favicon = wrapper->favicon_tab_helper()->GetFavicon(); | 316 data_.favicon = wrapper->favicon_tab_helper()->GetFavicon(); |
| 317 } |
| 309 | 318 |
| 310 data_.app = app; | 319 data_.app = app; |
| 320 |
| 321 // Make a cairo cached version of the favicon. |
| 322 if (!data_.favicon.isNull()) { |
| 323 // Instead of resizing the icon during each frame, create our resized |
| 324 // icon resource now, send it to the xserver and use that each frame |
| 325 // instead. |
| 326 |
| 327 // For source images smaller than the favicon square, scale them as if |
| 328 // they were padded to fit the favicon square, so we don't blow up tiny |
| 329 // falcons into larger or nonproportional results. |
| 330 int src_w = data_.favicon.width(); |
| 331 int src_h = data_.favicon.height(); |
| 332 float float_src_w = static_cast<float>(src_w); |
| 333 float float_src_h = static_cast<float>(src_h); |
| 334 float scalable_w, scalable_h; |
| 335 if (src_w <= gfx::kFaviconSize && src_h <= gfx::kFaviconSize) { |
| 336 scalable_w = scalable_h = gfx::kFaviconSize; |
| 337 } else { |
| 338 scalable_w = float_src_w; |
| 339 scalable_h = float_src_h; |
| 340 } |
| 341 |
| 342 // Scale proportionately. |
| 343 float float_size = gfx::kFaviconSize; |
| 344 float scale = std::min(float_size / scalable_w, |
| 345 float_size / scalable_h); |
| 346 int dest_w = static_cast<int>(float_src_w * scale); |
| 347 int dest_h = static_cast<int>(float_src_h * scale); |
| 348 |
| 349 GdkPixbuf* pixbuf; |
| 350 if (dest_w == src_w && dest_h == src_h) { |
| 351 pixbuf = gfx::GdkPixbufFromSkBitmap(&data_.favicon); |
| 352 } else { |
| 353 SkBitmap resized_icon = skia::ImageOperations::Resize( |
| 354 data_.favicon, |
| 355 skia::ImageOperations::RESIZE_BETTER, |
| 356 dest_w, dest_h); |
| 357 pixbuf = gfx::GdkPixbufFromSkBitmap(&resized_icon); |
| 358 } |
| 359 |
| 360 data_.cairo_favicon.UsePixbuf(pixbuf); |
| 361 g_object_unref(pixbuf); |
| 362 } else { |
| 363 data_.cairo_favicon.Reset(); |
| 364 } |
| 365 |
| 311 // This is kind of a hacky way to determine whether our icon is the default | 366 // This is kind of a hacky way to determine whether our icon is the default |
| 312 // favicon. But the plumbing that would be necessary to do it right would | 367 // favicon. But the plumbing that would be necessary to do it right would |
| 313 // be a good bit of work and would sully code for other platforms which | 368 // be a good bit of work and would sully code for other platforms which |
| 314 // don't care to custom-theme the favicon. Hopefully the default favicon | 369 // don't care to custom-theme the favicon. Hopefully the default favicon |
| 315 // will eventually be chromium-themable and this code will go away. | 370 // will eventually be chromium-themable and this code will go away. |
| 316 data_.is_default_favicon = | 371 data_.is_default_favicon = |
| 317 (data_.favicon.pixelRef() == | 372 (data_.favicon.pixelRef() == |
| 318 ResourceBundle::GetSharedInstance().GetBitmapNamed( | 373 ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 319 IDR_DEFAULT_FAVICON)->pixelRef()); | 374 IDR_DEFAULT_FAVICON)->pixelRef()); |
| 320 } | 375 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 370 gtk_widget_show(close_button_->widget()); | 425 gtk_widget_show(close_button_->widget()); |
| 371 } else { | 426 } else { |
| 372 gtk_widget_hide_all(tab_.get()); | 427 gtk_widget_hide_all(tab_.get()); |
| 373 } | 428 } |
| 374 } | 429 } |
| 375 | 430 |
| 376 bool TabRendererGtk::ValidateLoadingAnimation(AnimationState animation_state) { | 431 bool TabRendererGtk::ValidateLoadingAnimation(AnimationState animation_state) { |
| 377 return loading_animation_.ValidateLoadingAnimation(animation_state); | 432 return loading_animation_.ValidateLoadingAnimation(animation_state); |
| 378 } | 433 } |
| 379 | 434 |
| 380 void TabRendererGtk::PaintFaviconArea(GdkEventExpose* event) { | 435 void TabRendererGtk::PaintFaviconArea(GtkWidget* widget, cairo_t* cr) { |
| 381 DCHECK(ShouldShowIcon()); | 436 DCHECK(ShouldShowIcon()); |
| 382 | 437 |
| 383 // The paint area is the favicon bounds, but we're painting into the gdk | 438 cairo_rectangle(cr, |
| 384 // window belonging to the tabstrip. So the coordinates are relative to the | 439 x() + favicon_bounds_.x(), |
| 385 // top left of the tab strip. | 440 y() + favicon_bounds_.y(), |
| 386 event->area.x = x() + favicon_bounds_.x(); | 441 favicon_bounds_.width(), |
| 387 event->area.y = y() + favicon_bounds_.y(); | 442 favicon_bounds_.height()); |
| 388 event->area.width = favicon_bounds_.width(); | 443 cairo_clip(cr); |
| 389 event->area.height = favicon_bounds_.height(); | |
| 390 gfx::CanvasSkiaPaint canvas(event, false); | |
| 391 | 444 |
| 392 // The actual paint methods expect 0, 0 to be the tab top left (see | 445 // The tab is rendered into a windowless widget whose offset is at the |
| 393 // PaintTab). | 446 // coordinate event->area. Translate by these offsets so we can render at |
| 394 canvas.Translate(bounds_.origin()); | 447 // (0,0) to match Windows' rendering metrics. |
| 448 cairo_matrix_t cairo_matrix; |
| 449 cairo_matrix_init_translate(&cairo_matrix, x(), y()); |
| 450 cairo_set_matrix(cr, &cairo_matrix); |
| 395 | 451 |
| 396 // Paint the background behind the favicon. | 452 // Which background should we be painting? |
| 397 int theme_id; | 453 int theme_id; |
| 398 int offset_y = 0; | 454 int offset_y = 0; |
| 399 if (IsActive()) { | 455 if (IsActive()) { |
| 400 theme_id = IDR_THEME_TOOLBAR; | 456 theme_id = IDR_THEME_TOOLBAR; |
| 401 } else { | 457 } else { |
| 402 if (!data_.incognito) { | 458 theme_id = data_.incognito ? IDR_THEME_TAB_BACKGROUND_INCOGNITO : |
| 403 theme_id = IDR_THEME_TAB_BACKGROUND; | 459 IDR_THEME_TAB_BACKGROUND; |
| 404 } else { | 460 |
| 405 theme_id = IDR_THEME_TAB_BACKGROUND_INCOGNITO; | |
| 406 } | |
| 407 if (!theme_service_->HasCustomImage(theme_id)) | 461 if (!theme_service_->HasCustomImage(theme_id)) |
| 408 offset_y = background_offset_y_; | 462 offset_y = background_offset_y_; |
| 409 } | 463 } |
| 410 SkBitmap* tab_bg = theme_service_->GetBitmapNamed(theme_id); | 464 |
| 411 canvas.TileImageInt(*tab_bg, | 465 // Paint the background behind the favicon. |
| 412 x() + favicon_bounds_.x(), offset_y + favicon_bounds_.y(), | 466 CairoCachedSurface* tab_bg = |
| 413 favicon_bounds_.x(), favicon_bounds_.y(), | 467 theme_service_->GetSurfaceNamed(theme_id, widget); |
| 414 favicon_bounds_.width(), favicon_bounds_.height()); | 468 tab_bg->SetSource(cr, -x(), -offset_y); |
| 469 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); |
| 470 cairo_rectangle(cr, |
| 471 favicon_bounds_.x(), favicon_bounds_.y(), |
| 472 favicon_bounds_.width(), favicon_bounds_.height()); |
| 473 cairo_fill(cr); |
| 415 | 474 |
| 416 if (!IsActive()) { | 475 if (!IsActive()) { |
| 417 double throb_value = GetThrobValue(); | 476 double throb_value = GetThrobValue(); |
| 418 if (throb_value > 0) { | 477 if (throb_value > 0) { |
| 419 SkRect bounds; | 478 cairo_push_group(cr); |
| 420 bounds.set(favicon_bounds_.x(), favicon_bounds_.y(), | 479 CairoCachedSurface* active_bg = theme_service_->GetSurfaceNamed( |
| 421 favicon_bounds_.right(), favicon_bounds_.bottom()); | 480 IDR_THEME_TOOLBAR, widget); |
| 422 canvas.sk_canvas()->saveLayerAlpha( | 481 active_bg->SetSource(cr, -x(), 0); |
| 423 &bounds, static_cast<int>(throb_value * 0xff), | 482 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); |
| 424 SkCanvas::kARGB_ClipLayer_SaveFlag); | 483 |
| 425 canvas.sk_canvas()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); | 484 cairo_rectangle(cr, |
| 426 SkBitmap* active_bg = theme_service_->GetBitmapNamed(IDR_THEME_TOOLBAR); | 485 favicon_bounds_.x(), favicon_bounds_.y(), |
| 427 canvas.TileImageInt(*active_bg, | 486 favicon_bounds_.width(), favicon_bounds_.height()); |
| 428 x() + favicon_bounds_.x(), favicon_bounds_.y(), | 487 cairo_fill(cr); |
| 429 favicon_bounds_.x(), favicon_bounds_.y(), | 488 |
| 430 favicon_bounds_.width(), favicon_bounds_.height()); | 489 cairo_pop_group_to_source(cr); |
| 431 canvas.sk_canvas()->restore(); | 490 cairo_paint_with_alpha(cr, throb_value); |
| 432 } | 491 } |
| 433 } | 492 } |
| 434 | 493 |
| 435 // Now paint the icon. | 494 PaintIcon(widget, cr); |
| 436 PaintIcon(&canvas); | |
| 437 } | 495 } |
| 438 | 496 |
| 439 bool TabRendererGtk::ShouldShowIcon() const { | 497 bool TabRendererGtk::ShouldShowIcon() const { |
| 440 if (mini() && height() >= GetMinimumUnselectedSize().height()) { | 498 if (mini() && height() >= GetMinimumUnselectedSize().height()) { |
| 441 return true; | 499 return true; |
| 442 } else if (!data_.show_icon) { | 500 } else if (!data_.show_icon) { |
| 443 return false; | 501 return false; |
| 444 } else if (IsActive()) { | 502 } else if (IsActive()) { |
| 445 // The active tab clips favicon before close button. | 503 // The active tab clips favicon before close button. |
| 446 return IconCapacity() >= 2; | 504 return IconCapacity() >= 2; |
| 447 } | 505 } |
| 448 // Non-selected tabs clip close button before favicon. | 506 // Non-selected tabs clip close button before favicon. |
| 449 return IconCapacity() >= 1; | 507 return IconCapacity() >= 1; |
| 450 } | 508 } |
| 451 | 509 |
| 452 // static | 510 // static |
| 453 gfx::Size TabRendererGtk::GetMinimumUnselectedSize() { | 511 gfx::Size TabRendererGtk::GetMinimumUnselectedSize() { |
| 454 InitResources(); | 512 InitResources(); |
| 455 | 513 |
| 456 gfx::Size minimum_size; | 514 gfx::Size minimum_size; |
| 457 minimum_size.set_width(kLeftPadding + kRightPadding); | 515 minimum_size.set_width(kLeftPadding + kRightPadding); |
| 458 // Since we use bitmap images, the real minimum height of the image is | 516 // Since we use bitmap images, the real minimum height of the image is |
| 459 // defined most accurately by the height of the end cap images. | 517 // defined most accurately by the height of the end cap images. |
| 460 minimum_size.set_height(tab_active_.image_l->height() - kToolbarOverlap); | 518 minimum_size.set_height(tab_active_l_height_ - kToolbarOverlap); |
| 461 return minimum_size; | 519 return minimum_size; |
| 462 } | 520 } |
| 463 | 521 |
| 464 // static | 522 // static |
| 465 gfx::Size TabRendererGtk::GetMinimumSelectedSize() { | 523 gfx::Size TabRendererGtk::GetMinimumSelectedSize() { |
| 466 gfx::Size minimum_size = GetMinimumUnselectedSize(); | 524 gfx::Size minimum_size = GetMinimumUnselectedSize(); |
| 467 minimum_size.set_width(kLeftPadding + gfx::kFaviconSize + kRightPadding); | 525 minimum_size.set_width(kLeftPadding + gfx::kFaviconSize + kRightPadding); |
| 468 return minimum_size; | 526 return minimum_size; |
| 469 } | 527 } |
| 470 | 528 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 482 | 540 |
| 483 // static | 541 // static |
| 484 int TabRendererGtk::GetContentHeight() { | 542 int TabRendererGtk::GetContentHeight() { |
| 485 // The height of the content of the Tab is the largest of the favicon, | 543 // The height of the content of the Tab is the largest of the favicon, |
| 486 // the title text and the close button graphic. | 544 // the title text and the close button graphic. |
| 487 int content_height = std::max(gfx::kFaviconSize, title_font_height_); | 545 int content_height = std::max(gfx::kFaviconSize, title_font_height_); |
| 488 return std::max(content_height, close_button_height_); | 546 return std::max(content_height, close_button_height_); |
| 489 } | 547 } |
| 490 | 548 |
| 491 // static | 549 // static |
| 492 void TabRendererGtk::LoadTabImages() { | |
| 493 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 494 | |
| 495 tab_alpha_.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_LEFT); | |
| 496 tab_alpha_.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_RIGHT); | |
| 497 | |
| 498 tab_active_.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT); | |
| 499 tab_active_.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER); | |
| 500 tab_active_.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT); | |
| 501 tab_active_.l_width = tab_active_.image_l->width(); | |
| 502 tab_active_.r_width = tab_active_.image_r->width(); | |
| 503 | |
| 504 tab_inactive_.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT); | |
| 505 tab_inactive_.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER); | |
| 506 tab_inactive_.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT); | |
| 507 tab_inactive_.l_width = tab_inactive_.image_l->width(); | |
| 508 tab_inactive_.r_width = tab_inactive_.image_r->width(); | |
| 509 | |
| 510 close_button_width_ = rb.GetBitmapNamed(IDR_TAB_CLOSE)->width(); | |
| 511 close_button_height_ = rb.GetBitmapNamed(IDR_TAB_CLOSE)->height(); | |
| 512 } | |
| 513 | |
| 514 // static | |
| 515 void TabRendererGtk::SetSelectedTitleColor(SkColor color) { | 550 void TabRendererGtk::SetSelectedTitleColor(SkColor color) { |
| 516 selected_title_color_ = color; | 551 selected_title_color_ = color; |
| 517 } | 552 } |
| 518 | 553 |
| 519 // static | 554 // static |
| 520 void TabRendererGtk::SetUnselectedTitleColor(SkColor color) { | 555 void TabRendererGtk::SetUnselectedTitleColor(SkColor color) { |
| 521 unselected_title_color_ = color; | 556 unselected_title_color_ = color; |
| 522 } | 557 } |
| 523 | 558 |
| 524 gfx::Rect TabRendererGtk::GetNonMirroredBounds(GtkWidget* parent) const { | 559 gfx::Rect TabRendererGtk::GetNonMirroredBounds(GtkWidget* parent) const { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 548 void TabRendererGtk::StopMiniTabTitleAnimation() { | 583 void TabRendererGtk::StopMiniTabTitleAnimation() { |
| 549 if (mini_title_animation_.get()) | 584 if (mini_title_animation_.get()) |
| 550 mini_title_animation_->Stop(); | 585 mini_title_animation_->Stop(); |
| 551 } | 586 } |
| 552 | 587 |
| 553 void TabRendererGtk::SetBounds(const gfx::Rect& bounds) { | 588 void TabRendererGtk::SetBounds(const gfx::Rect& bounds) { |
| 554 requisition_ = bounds; | 589 requisition_ = bounds; |
| 555 gtk_widget_set_size_request(tab_.get(), bounds.width(), bounds.height()); | 590 gtk_widget_set_size_request(tab_.get(), bounds.width(), bounds.height()); |
| 556 } | 591 } |
| 557 | 592 |
| 558 void TabRendererGtk::Observe(int type, | |
| 559 const content::NotificationSource& source, | |
| 560 const content::NotificationDetails& details) { | |
| 561 DCHECK(type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED); | |
| 562 | |
| 563 // Clear our cache when we receive a theme change notification because it | |
| 564 // contains cached bitmaps based off the previous theme. | |
| 565 for (BitmapCache::iterator it = cached_bitmaps_.begin(); | |
| 566 it != cached_bitmaps_.end(); ++it) { | |
| 567 delete it->second.bitmap; | |
| 568 } | |
| 569 cached_bitmaps_.clear(); | |
| 570 } | |
| 571 | |
| 572 //////////////////////////////////////////////////////////////////////////////// | 593 //////////////////////////////////////////////////////////////////////////////// |
| 573 // TabRendererGtk, protected: | 594 // TabRendererGtk, protected: |
| 574 | 595 |
| 575 void TabRendererGtk::Raise() const { | 596 void TabRendererGtk::Raise() const { |
| 576 if (gtk_button_get_event_window(GTK_BUTTON(close_button_->widget()))) | 597 if (gtk_button_get_event_window(GTK_BUTTON(close_button_->widget()))) |
| 577 gdk_window_raise(gtk_button_get_event_window( | 598 gdk_window_raise(gtk_button_get_event_window( |
| 578 GTK_BUTTON(close_button_->widget()))); | 599 GTK_BUTTON(close_button_->widget()))); |
| 579 } | 600 } |
| 580 | 601 |
| 581 string16 TabRendererGtk::GetTitle() const { | 602 string16 TabRendererGtk::GetTitle() const { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 623 } | 644 } |
| 624 | 645 |
| 625 void TabRendererGtk::DisplayCrashedFavicon() { | 646 void TabRendererGtk::DisplayCrashedFavicon() { |
| 626 should_display_crashed_favicon_ = true; | 647 should_display_crashed_favicon_ = true; |
| 627 } | 648 } |
| 628 | 649 |
| 629 void TabRendererGtk::ResetCrashedFavicon() { | 650 void TabRendererGtk::ResetCrashedFavicon() { |
| 630 should_display_crashed_favicon_ = false; | 651 should_display_crashed_favicon_ = false; |
| 631 } | 652 } |
| 632 | 653 |
| 633 void TabRendererGtk::Paint(gfx::Canvas* canvas) { | 654 void TabRendererGtk::Paint(GtkWidget* widget, cairo_t* cr) { |
| 634 // Don't paint if we're narrower than we can render correctly. (This should | 655 // Don't paint if we're narrower than we can render correctly. (This should |
| 635 // only happen during animations). | 656 // only happen during animations). |
| 636 if (width() < GetMinimumUnselectedSize().width() && !mini()) | 657 if (width() < GetMinimumUnselectedSize().width() && !mini()) |
| 637 return; | 658 return; |
| 638 | 659 |
| 639 // See if the model changes whether the icons should be painted. | 660 // See if the model changes whether the icons should be painted. |
| 640 const bool show_icon = ShouldShowIcon(); | 661 const bool show_icon = ShouldShowIcon(); |
| 641 const bool show_close_button = ShouldShowCloseBox(); | 662 const bool show_close_button = ShouldShowCloseBox(); |
| 642 if (show_icon != showing_icon_ || | 663 if (show_icon != showing_icon_ || |
| 643 show_close_button != showing_close_button_) | 664 show_close_button != showing_close_button_) |
| 644 Layout(); | 665 Layout(); |
| 645 | 666 |
| 646 PaintTabBackground(canvas); | 667 PaintTabBackground(widget, cr); |
| 647 | 668 |
| 648 if (!mini() || width() > kMiniTabRendererAsNormalTabWidth) | 669 if (!mini() || width() > kMiniTabRendererAsNormalTabWidth) |
| 649 PaintTitle(canvas); | 670 PaintTitle(widget, cr); |
| 650 | 671 |
| 651 if (show_icon) | 672 if (show_icon) |
| 652 PaintIcon(canvas); | 673 PaintIcon(widget, cr); |
| 653 } | 674 } |
| 654 | 675 |
| 655 cairo_surface_t* TabRendererGtk::PaintToSurface() { | 676 cairo_surface_t* TabRendererGtk::PaintToSurface(GtkWidget* widget, |
| 656 gfx::CanvasSkia canvas(width(), height(), false); | 677 cairo_t* cr) { |
| 657 Paint(&canvas); | 678 cairo_surface_t* target = cairo_get_target(cr); |
| 658 return cairo_surface_reference(cairo_get_target( | 679 cairo_surface_t* out_surface = cairo_surface_create_similar( |
| 659 skia::BeginPlatformPaint(canvas.sk_canvas()))); | 680 target, |
| 681 CAIRO_CONTENT_COLOR_ALPHA, |
| 682 width(), height()); |
| 683 |
| 684 cairo_t* out_cr = cairo_create(out_surface); |
| 685 Paint(widget, out_cr); |
| 686 cairo_destroy(out_cr); |
| 687 |
| 688 return out_surface; |
| 660 } | 689 } |
| 661 | 690 |
| 662 void TabRendererGtk::SchedulePaint() { | 691 void TabRendererGtk::SchedulePaint() { |
| 663 gtk_widget_queue_draw(tab_.get()); | 692 gtk_widget_queue_draw(tab_.get()); |
| 664 } | 693 } |
| 665 | 694 |
| 666 gfx::Rect TabRendererGtk::GetLocalBounds() { | 695 gfx::Rect TabRendererGtk::GetLocalBounds() { |
| 667 return gfx::Rect(0, 0, bounds_.width(), bounds_.height()); | 696 return gfx::Rect(0, 0, bounds_.width(), bounds_.height()); |
| 668 } | 697 } |
| 669 | 698 |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 761 void TabRendererGtk::MoveCloseButtonWidget() { | 790 void TabRendererGtk::MoveCloseButtonWidget() { |
| 762 if (!close_button_bounds_.IsEmpty()) { | 791 if (!close_button_bounds_.IsEmpty()) { |
| 763 gtk_fixed_move(GTK_FIXED(tab_.get()), close_button_->widget(), | 792 gtk_fixed_move(GTK_FIXED(tab_.get()), close_button_->widget(), |
| 764 close_button_bounds_.x(), close_button_bounds_.y()); | 793 close_button_bounds_.x(), close_button_bounds_.y()); |
| 765 gtk_widget_show(close_button_->widget()); | 794 gtk_widget_show(close_button_->widget()); |
| 766 } else { | 795 } else { |
| 767 gtk_widget_hide(close_button_->widget()); | 796 gtk_widget_hide(close_button_->widget()); |
| 768 } | 797 } |
| 769 } | 798 } |
| 770 | 799 |
| 771 SkBitmap* TabRendererGtk::GetMaskedBitmap(const SkBitmap* mask, | 800 void TabRendererGtk::PaintTab(GtkWidget* widget, GdkEventExpose* event) { |
| 772 const SkBitmap* background, int bg_offset_x, int bg_offset_y) { | 801 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window)); |
| 773 // We store a bitmap for each mask + background pair (4 total bitmaps). We | 802 gdk_cairo_rectangle(cr, &event->area); |
| 774 // replace the cached image if the tab has moved relative to the background. | 803 cairo_clip(cr); |
| 775 BitmapCache::iterator it = cached_bitmaps_.find(std::make_pair(mask, | |
| 776 background)); | |
| 777 if (it != cached_bitmaps_.end()) { | |
| 778 if (it->second.bg_offset_x == bg_offset_x && | |
| 779 it->second.bg_offset_y == bg_offset_y) { | |
| 780 return it->second.bitmap; | |
| 781 } | |
| 782 // The background offset changed so we should re-render with the new | |
| 783 // offsets. | |
| 784 delete it->second.bitmap; | |
| 785 } | |
| 786 SkBitmap image = SkBitmapOperations::CreateTiledBitmap( | |
| 787 *background, bg_offset_x, bg_offset_y, mask->width(), | |
| 788 height() + kToolbarOverlap); | |
| 789 CachedBitmap bitmap = { | |
| 790 bg_offset_x, | |
| 791 bg_offset_y, | |
| 792 new SkBitmap(SkBitmapOperations::CreateMaskedBitmap(image, *mask)) | |
| 793 }; | |
| 794 cached_bitmaps_[std::make_pair(mask, background)] = bitmap; | |
| 795 return bitmap.bitmap; | |
| 796 } | |
| 797 | |
| 798 void TabRendererGtk::PaintTab(GdkEventExpose* event) { | |
| 799 gfx::CanvasSkiaPaint canvas(event, false); | |
| 800 if (canvas.is_empty()) | |
| 801 return; | |
| 802 | 804 |
| 803 // The tab is rendered into a windowless widget whose offset is at the | 805 // The tab is rendered into a windowless widget whose offset is at the |
| 804 // coordinate event->area. Translate by these offsets so we can render at | 806 // coordinate event->area. Translate by these offsets so we can render at |
| 805 // (0,0) to match Windows' rendering metrics. | 807 // (0,0) to match Windows' rendering metrics. |
| 806 canvas.Translate(gfx::Rect(event->area).origin()); | 808 cairo_matrix_t cairo_matrix; |
| 809 cairo_matrix_init_translate(&cairo_matrix, event->area.x, event->area.y); |
| 810 cairo_set_matrix(cr, &cairo_matrix); |
| 807 | 811 |
| 808 // Save the original x offset so we can position background images properly. | 812 // Save the original x offset so we can position background images properly. |
| 809 background_offset_x_ = event->area.x; | 813 background_offset_x_ = event->area.x; |
| 810 | 814 |
| 811 Paint(&canvas); | 815 Paint(widget, cr); |
| 816 cairo_destroy(cr); |
| 812 } | 817 } |
| 813 | 818 |
| 814 void TabRendererGtk::PaintTitle(gfx::Canvas* canvas) { | 819 void TabRendererGtk::PaintTitle(GtkWidget* widget, cairo_t* cr) { |
| 820 if (title_bounds_.IsEmpty()) |
| 821 return; |
| 822 |
| 815 // Paint the Title. | 823 // Paint the Title. |
| 816 string16 title = data_.title; | 824 string16 title = data_.title; |
| 817 if (title.empty()) { | 825 if (title.empty()) { |
| 818 title = data_.loading ? | 826 title = data_.loading ? |
| 819 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : | 827 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : |
| 820 TabContentsWrapper::GetDefaultTitle(); | 828 TabContentsWrapper::GetDefaultTitle(); |
| 821 } else { | 829 } else { |
| 822 Browser::FormatTitleForDisplay(&title); | 830 Browser::FormatTitleForDisplay(&title); |
| 823 } | 831 } |
| 824 | 832 |
| 825 SkColor title_color = IsSelected() ? selected_title_color_ | 833 SkColor title_color = IsSelected() ? selected_title_color_ |
| 826 : unselected_title_color_; | 834 : unselected_title_color_; |
| 827 canvas->DrawStringInt(title, *title_font_, title_color, | 835 |
| 828 title_bounds_.x(), title_bounds_.y(), | 836 DrawTextOntoCairoSurface(cr, |
| 829 title_bounds_.width(), title_bounds_.height()); | 837 title, |
| 838 *title_font_, |
| 839 title_bounds_, |
| 840 title_bounds_, |
| 841 title_color, |
| 842 base::i18n::IsRTL() ? gfx::Canvas::TEXT_ALIGN_RIGHT : |
| 843 gfx::Canvas::TEXT_ALIGN_LEFT); |
| 830 } | 844 } |
| 831 | 845 |
| 832 void TabRendererGtk::PaintIcon(gfx::Canvas* canvas) { | 846 void TabRendererGtk::PaintIcon(GtkWidget* widget, cairo_t* cr) { |
| 833 if (loading_animation_.animation_state() != ANIMATION_NONE) { | 847 if (loading_animation_.animation_state() != ANIMATION_NONE) { |
| 834 PaintLoadingAnimation(canvas); | 848 PaintLoadingAnimation(widget, cr); |
| 835 } else { | 849 } else { |
| 836 canvas->Save(); | |
| 837 canvas->ClipRect(gfx::Rect(0, 0, width(), height() - kFaviconTitleSpacing)); | |
| 838 if (should_display_crashed_favicon_) { | 850 if (should_display_crashed_favicon_) { |
| 839 canvas->DrawBitmapInt(*crashed_favicon, 0, 0, | 851 theme_service_->GetSurfaceNamed(IDR_SAD_FAVICON, widget)->SetSource( |
| 840 crashed_favicon->width(), | 852 cr, favicon_bounds_.x(), |
| 841 crashed_favicon->height(), | 853 favicon_bounds_.y() + favicon_hiding_offset_); |
| 842 favicon_bounds_.x(), | 854 cairo_paint(cr); |
| 843 favicon_bounds_.y() + favicon_hiding_offset_, | |
| 844 gfx::kFaviconSize, gfx::kFaviconSize, | |
| 845 true); | |
| 846 } else { | 855 } else { |
| 847 if (!data_.favicon.isNull()) { | 856 if (!data_.favicon.isNull()) { |
| 848 if (data_.is_default_favicon && theme_service_->UsingNativeTheme()) { | 857 if (data_.is_default_favicon && theme_service_->UsingNativeTheme()) { |
| 849 GdkPixbuf* favicon = GtkThemeService::GetDefaultFavicon(true); | 858 GdkPixbuf* favicon = GtkThemeService::GetDefaultFavicon(true); |
| 850 canvas->AsCanvasSkia()->DrawGdkPixbuf( | 859 |
| 851 favicon, favicon_bounds_.x(), | 860 // TODO(erg): Get GtkThemeService to hand us a |
| 861 // CairoCachedSurface. Then we can simplify all of this. |
| 862 gdk_cairo_set_source_pixbuf( |
| 863 cr, favicon, favicon_bounds_.x(), |
| 852 favicon_bounds_.y() + favicon_hiding_offset_); | 864 favicon_bounds_.y() + favicon_hiding_offset_); |
| 853 } else { | 865 cairo_paint(cr); |
| 854 // If the favicon is an app icon, it is allowed to be drawn slightly | 866 } else if (data_.cairo_favicon.valid()) { |
| 855 // larger than the standard favicon. | 867 // TODO(erg): We should research whether we still need to draw app |
| 856 int faviconHeightOffset = data_.app ? -2 : 0; | 868 // icons larger. We don't appear to be getting larger icons. |
| 857 int faviconWidthDelta = data_.app ? | 869 data_.cairo_favicon.SetSource( |
| 858 data_.favicon.width() - gfx::kFaviconSize : 0; | 870 cr, |
| 859 int faviconHeightDelta = data_.app ? | 871 favicon_bounds_.x(), |
| 860 data_.favicon.height() - gfx::kFaviconSize : 0; | 872 favicon_bounds_.y() + favicon_hiding_offset_); |
| 861 | 873 cairo_paint(cr); |
| 862 // TODO(pkasting): Use code in tab_icon_view.cc:PaintIcon() (or switch | |
| 863 // to using that class to render the favicon). | |
| 864 canvas->DrawBitmapInt(data_.favicon, 0, 0, | |
| 865 data_.favicon.width(), | |
| 866 data_.favicon.height(), | |
| 867 favicon_bounds_.x() - faviconWidthDelta/2, | |
| 868 favicon_bounds_.y() + faviconHeightOffset | |
| 869 - faviconHeightDelta/2 | |
| 870 + favicon_hiding_offset_, | |
| 871 gfx::kFaviconSize + faviconWidthDelta, | |
| 872 gfx::kFaviconSize + faviconHeightDelta, | |
| 873 true); | |
| 874 } | 874 } |
| 875 } | 875 } |
| 876 } | 876 } |
| 877 canvas->Restore(); | |
| 878 } | |
| 879 } | |
| 880 | |
| 881 void TabRendererGtk::PaintTabBackground(gfx::Canvas* canvas) { | |
| 882 if (IsActive()) { | |
| 883 PaintActiveTabBackground(canvas); | |
| 884 } else { | |
| 885 PaintInactiveTabBackground(canvas); | |
| 886 | |
| 887 double throb_value = GetThrobValue(); | |
| 888 if (throb_value > 0) { | |
| 889 canvas->SaveLayerAlpha(static_cast<int>(throb_value * 0xff), | |
| 890 gfx::Rect(width(), height())); | |
| 891 canvas->GetSkCanvas()->drawARGB(0, 255, 255, 255, | |
| 892 SkXfermode::kClear_Mode); | |
| 893 PaintActiveTabBackground(canvas); | |
| 894 canvas->Restore(); | |
| 895 } | |
| 896 } | 877 } |
| 897 } | 878 } |
| 898 | 879 |
| 899 void TabRendererGtk::PaintInactiveTabBackground(gfx::Canvas* canvas) { | 880 void TabRendererGtk::PaintTabBackground(GtkWidget* widget, cairo_t* cr) { |
| 881 if (IsActive()) { |
| 882 PaintActiveTabBackground(widget, cr); |
| 883 } else { |
| 884 PaintInactiveTabBackground(widget, cr); |
| 900 | 885 |
| 901 // The tab image needs to be lined up with the background image | 886 double throb_value = GetThrobValue(); |
| 902 // so that it feels partially transparent. | 887 if (throb_value > 0) { |
| 903 int offset_x = background_offset_x_; | 888 cairo_push_group(cr); |
| 889 PaintActiveTabBackground(widget, cr); |
| 890 cairo_pop_group_to_source(cr); |
| 891 cairo_paint_with_alpha(cr, throb_value); |
| 892 } |
| 893 } |
| 894 } |
| 904 | 895 |
| 905 int tab_id = data_.incognito ? | 896 void TabRendererGtk::DrawTabBackground( |
| 897 cairo_t* cr, |
| 898 GtkWidget* widget, |
| 899 CairoCachedSurface* tab_bg, |
| 900 int offset_x, |
| 901 int offset_y) { |
| 902 tab_bg->SetSource(cr, -offset_x, -offset_y); |
| 903 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); |
| 904 |
| 905 // Draw left edge |
| 906 CairoCachedSurface* tab_l_mask = |
| 907 theme_service_->GetSurfaceNamed(IDR_TAB_ALPHA_LEFT, widget); |
| 908 tab_l_mask->MaskSource(cr, 0, 0); |
| 909 |
| 910 // Draw center |
| 911 cairo_rectangle(cr, |
| 912 tab_active_l_width_, kDropShadowOffset, |
| 913 width() - (2 * tab_active_l_width_), |
| 914 tab_inactive_l_height_); |
| 915 cairo_fill(cr); |
| 916 |
| 917 // Draw right edge |
| 918 CairoCachedSurface* tab_r_mask = |
| 919 theme_service_->GetSurfaceNamed(IDR_TAB_ALPHA_RIGHT, widget); |
| 920 tab_r_mask->MaskSource(cr, width() - tab_active_l_width_, 0); |
| 921 } |
| 922 |
| 923 void TabRendererGtk::DrawTabShadow( |
| 924 cairo_t* cr, |
| 925 GtkWidget* widget, |
| 926 int left_idr, |
| 927 int center_idr, |
| 928 int right_idr) { |
| 929 // Draw left drop shadow |
| 930 CairoCachedSurface* active_image_l = |
| 931 theme_service_->GetSurfaceNamed(left_idr, widget); |
| 932 active_image_l->SetSource(cr, 0, 0); |
| 933 cairo_paint(cr); |
| 934 |
| 935 // Draw the center shadow |
| 936 CairoCachedSurface* active_image_c = |
| 937 theme_service_->GetSurfaceNamed(center_idr, widget); |
| 938 active_image_c->SetSource(cr, 0, 0); |
| 939 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); |
| 940 cairo_rectangle(cr, tab_active_l_width_, 0, |
| 941 width() - (2 * tab_active_l_width_), |
| 942 height()); |
| 943 cairo_fill(cr); |
| 944 |
| 945 // Draw right drop shadow |
| 946 CairoCachedSurface* active_image_r = |
| 947 theme_service_->GetSurfaceNamed(right_idr, widget); |
| 948 active_image_r->SetSource(cr, width() - active_image_r->Width(), 0); |
| 949 cairo_paint(cr); |
| 950 } |
| 951 |
| 952 void TabRendererGtk::PaintInactiveTabBackground(GtkWidget* widget, |
| 953 cairo_t* cr) { |
| 954 int theme_id = data_.incognito ? |
| 906 IDR_THEME_TAB_BACKGROUND_INCOGNITO : IDR_THEME_TAB_BACKGROUND; | 955 IDR_THEME_TAB_BACKGROUND_INCOGNITO : IDR_THEME_TAB_BACKGROUND; |
| 907 if (IsSelected()) | 956 if (IsSelected()) |
| 908 tab_id = IDR_THEME_TAB_BACKGROUND_V; | 957 theme_id = IDR_THEME_TAB_BACKGROUND_V; |
| 909 | 958 |
| 910 SkBitmap* tab_bg = theme_service_->GetBitmapNamed(tab_id); | 959 CairoCachedSurface* tab_bg = |
| 960 theme_service_->GetSurfaceNamed(theme_id, widget); |
| 911 | 961 |
| 912 // If the theme is providing a custom background image, then its top edge | 962 // If the theme is providing a custom background image, then its top edge |
| 913 // should be at the top of the tab. Otherwise, we assume that the background | 963 // should be at the top of the tab. Otherwise, we assume that the background |
| 914 // image is a composited foreground + frame image. | 964 // image is a composited foreground + frame image. |
| 915 int offset_y = theme_service_->HasCustomImage(tab_id) ? | 965 int offset_y = theme_service_->HasCustomImage(theme_id) ? |
| 916 0 : background_offset_y_; | 966 0 : background_offset_y_; |
| 917 | 967 |
| 918 // Draw left edge. | 968 DrawTabBackground(cr, widget, tab_bg, background_offset_x_, offset_y); |
| 919 SkBitmap* theme_l = GetMaskedBitmap(tab_alpha_.image_l, tab_bg, offset_x, | |
| 920 offset_y); | |
| 921 canvas->DrawBitmapInt(*theme_l, 0, 0); | |
| 922 | 969 |
| 923 // Draw right edge. | 970 DrawTabShadow(cr, widget, IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, |
| 924 SkBitmap* theme_r = GetMaskedBitmap(tab_alpha_.image_r, tab_bg, | 971 IDR_TAB_INACTIVE_RIGHT); |
| 925 offset_x + width() - tab_active_.r_width, offset_y); | |
| 926 | |
| 927 canvas->DrawBitmapInt(*theme_r, width() - theme_r->width(), 0); | |
| 928 | |
| 929 // Draw center. | |
| 930 canvas->TileImageInt(*tab_bg, | |
| 931 offset_x + tab_active_.l_width, kDropShadowOffset + offset_y, | |
| 932 tab_active_.l_width, 2, | |
| 933 width() - tab_active_.l_width - tab_active_.r_width, height() - 2); | |
| 934 | |
| 935 canvas->DrawBitmapInt(*tab_inactive_.image_l, 0, 0); | |
| 936 canvas->TileImageInt(*tab_inactive_.image_c, tab_inactive_.l_width, 0, | |
| 937 width() - tab_inactive_.l_width - tab_inactive_.r_width, height()); | |
| 938 canvas->DrawBitmapInt(*tab_inactive_.image_r, | |
| 939 width() - tab_inactive_.r_width, 0); | |
| 940 } | 972 } |
| 941 | 973 |
| 942 void TabRendererGtk::PaintActiveTabBackground(gfx::Canvas* canvas) { | 974 void TabRendererGtk::PaintActiveTabBackground(GtkWidget* widget, |
| 943 int offset_x = background_offset_x_; | 975 cairo_t* cr) { |
| 976 CairoCachedSurface* tab_bg = |
| 977 theme_service_->GetSurfaceNamed(IDR_THEME_TOOLBAR, widget); |
| 944 | 978 |
| 945 SkBitmap* tab_bg = theme_service_->GetBitmapNamed(IDR_THEME_TOOLBAR); | 979 DrawTabBackground(cr, widget, tab_bg, background_offset_x_, 0); |
| 946 | 980 DrawTabShadow(cr, widget, IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, |
| 947 // Draw left edge. | 981 IDR_TAB_ACTIVE_RIGHT); |
| 948 SkBitmap* theme_l = GetMaskedBitmap(tab_alpha_.image_l, tab_bg, offset_x, 0); | |
| 949 canvas->DrawBitmapInt(*theme_l, 0, 0); | |
| 950 | |
| 951 // Draw right edge. | |
| 952 SkBitmap* theme_r = GetMaskedBitmap(tab_alpha_.image_r, tab_bg, | |
| 953 offset_x + width() - tab_active_.r_width, 0); | |
| 954 canvas->DrawBitmapInt(*theme_r, width() - tab_active_.r_width, 0); | |
| 955 | |
| 956 // Draw center. | |
| 957 canvas->TileImageInt(*tab_bg, | |
| 958 offset_x + tab_active_.l_width, kDropShadowHeight, | |
| 959 tab_active_.l_width, kDropShadowHeight, | |
| 960 width() - tab_active_.l_width - tab_active_.r_width, | |
| 961 height() - kDropShadowHeight); | |
| 962 | |
| 963 canvas->DrawBitmapInt(*tab_active_.image_l, 0, 0); | |
| 964 canvas->TileImageInt(*tab_active_.image_c, tab_active_.l_width, 0, | |
| 965 width() - tab_active_.l_width - tab_active_.r_width, height()); | |
| 966 canvas->DrawBitmapInt(*tab_active_.image_r, width() - tab_active_.r_width, 0); | |
| 967 } | 982 } |
| 968 | 983 |
| 969 void TabRendererGtk::PaintLoadingAnimation(gfx::Canvas* canvas) { | 984 void TabRendererGtk::PaintLoadingAnimation(GtkWidget* widget, |
| 970 const SkBitmap* frames = | 985 cairo_t* cr) { |
| 971 (loading_animation_.animation_state() == ANIMATION_WAITING) ? | 986 int id = loading_animation_.animation_state() == ANIMATION_WAITING ? |
| 972 loading_animation_.waiting_animation_frames() : | 987 IDR_THROBBER_WAITING : IDR_THROBBER; |
| 973 loading_animation_.loading_animation_frames(); | 988 CairoCachedSurface* throbber = theme_service_->GetSurfaceNamed(id, widget); |
| 974 const int image_size = frames->height(); | 989 |
| 990 const int image_size = throbber->Height(); |
| 975 const int image_offset = loading_animation_.animation_frame() * image_size; | 991 const int image_offset = loading_animation_.animation_frame() * image_size; |
| 976 DCHECK(image_size == favicon_bounds_.height()); | 992 DCHECK(image_size == favicon_bounds_.height()); |
| 977 DCHECK(image_size == favicon_bounds_.width()); | 993 DCHECK(image_size == favicon_bounds_.width()); |
| 978 | 994 |
| 979 // NOTE: the clipping is a work around for 69528, it shouldn't be necessary. | 995 throbber->SetSource(cr, favicon_bounds_.x() - image_offset, |
| 980 canvas->Save(); | 996 favicon_bounds_.y()); |
| 981 canvas->ClipRect(gfx::Rect(favicon_bounds_.x(), favicon_bounds_.y(), | 997 cairo_rectangle(cr, favicon_bounds_.x(), favicon_bounds_.y(), |
| 982 image_size, image_size)); | 998 image_size, image_size); |
| 983 canvas->DrawBitmapInt(*frames, image_offset, 0, image_size, image_size, | 999 cairo_fill(cr); |
| 984 favicon_bounds_.x(), favicon_bounds_.y(), image_size, image_size, | |
| 985 false); | |
| 986 canvas->Restore(); | |
| 987 } | 1000 } |
| 988 | 1001 |
| 989 int TabRendererGtk::IconCapacity() const { | 1002 int TabRendererGtk::IconCapacity() const { |
| 990 if (height() < GetMinimumUnselectedSize().height()) | 1003 if (height() < GetMinimumUnselectedSize().height()) |
| 991 return 0; | 1004 return 0; |
| 992 return (width() - kLeftPadding - kRightPadding) / gfx::kFaviconSize; | 1005 return (width() - kLeftPadding - kRightPadding) / gfx::kFaviconSize; |
| 993 } | 1006 } |
| 994 | 1007 |
| 995 bool TabRendererGtk::ShouldShowCloseBox() const { | 1008 bool TabRendererGtk::ShouldShowCloseBox() const { |
| 996 // The selected tab never clips close button. | 1009 // The selected tab never clips close button. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1040 if (event->button == 2) { | 1053 if (event->button == 2) { |
| 1041 CloseButtonClicked(); | 1054 CloseButtonClicked(); |
| 1042 return TRUE; | 1055 return TRUE; |
| 1043 } | 1056 } |
| 1044 | 1057 |
| 1045 return FALSE; | 1058 return FALSE; |
| 1046 } | 1059 } |
| 1047 | 1060 |
| 1048 gboolean TabRendererGtk::OnExposeEvent(GtkWidget* widget, | 1061 gboolean TabRendererGtk::OnExposeEvent(GtkWidget* widget, |
| 1049 GdkEventExpose* event) { | 1062 GdkEventExpose* event) { |
| 1050 PaintTab(event); | 1063 PaintTab(widget, event); |
| 1051 gtk_container_propagate_expose(GTK_CONTAINER(tab_.get()), | 1064 gtk_container_propagate_expose(GTK_CONTAINER(tab_.get()), |
| 1052 close_button_->widget(), event); | 1065 close_button_->widget(), event); |
| 1053 return TRUE; | 1066 return TRUE; |
| 1054 } | 1067 } |
| 1055 | 1068 |
| 1056 void TabRendererGtk::OnSizeAllocate(GtkWidget* widget, | 1069 void TabRendererGtk::OnSizeAllocate(GtkWidget* widget, |
| 1057 GtkAllocation* allocation) { | 1070 GtkAllocation* allocation) { |
| 1058 gfx::Rect bounds = gfx::Rect(allocation->x, allocation->y, | 1071 gfx::Rect bounds = gfx::Rect(allocation->x, allocation->y, |
| 1059 allocation->width, allocation->height); | 1072 allocation->width, allocation->height); |
| 1060 | 1073 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1079 hover_animation_->SetTweenType(ui::Tween::EASE_IN); | 1092 hover_animation_->SetTweenType(ui::Tween::EASE_IN); |
| 1080 hover_animation_->Hide(); | 1093 hover_animation_->Hide(); |
| 1081 return FALSE; | 1094 return FALSE; |
| 1082 } | 1095 } |
| 1083 | 1096 |
| 1084 // static | 1097 // static |
| 1085 void TabRendererGtk::InitResources() { | 1098 void TabRendererGtk::InitResources() { |
| 1086 if (initialized_) | 1099 if (initialized_) |
| 1087 return; | 1100 return; |
| 1088 | 1101 |
| 1089 LoadTabImages(); | 1102 // Grab the pixel sizes of our masking images. |
| 1103 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 1104 SkBitmap* tab_active_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT); |
| 1105 tab_active_l_width_ = tab_active_l->width(); |
| 1106 tab_active_l_height_ = tab_active_l->height(); |
| 1090 | 1107 |
| 1091 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 1108 SkBitmap* tab_inactive_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT); |
| 1109 tab_inactive_l_height_ = tab_inactive_l->height(); |
| 1110 |
| 1111 close_button_width_ = rb.GetBitmapNamed(IDR_TAB_CLOSE)->width(); |
| 1112 close_button_height_ = rb.GetBitmapNamed(IDR_TAB_CLOSE)->height(); |
| 1113 |
| 1092 const gfx::Font& base_font = rb.GetFont(ResourceBundle::BaseFont); | 1114 const gfx::Font& base_font = rb.GetFont(ResourceBundle::BaseFont); |
| 1093 title_font_ = new gfx::Font(base_font.GetFontName(), kFontPixelSize); | 1115 title_font_ = new gfx::Font(base_font.GetFontName(), kFontPixelSize); |
| 1094 title_font_height_ = title_font_->GetHeight(); | 1116 title_font_height_ = title_font_->GetHeight(); |
| 1095 | 1117 |
| 1096 crashed_favicon = rb.GetBitmapNamed(IDR_SAD_FAVICON); | |
| 1097 | |
| 1098 initialized_ = true; | 1118 initialized_ = true; |
| 1099 } | 1119 } |
| OLD | NEW |