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