1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/tabs/tab.h" 6 7 #include <limits> 8 9 #include "base/utf_string_conversions.h" 10 #include "chrome/browser/defaults.h" 11 #include "chrome/browser/themes/theme_service.h" 12 #include "grit/app_resources.h" 13 #include "grit/generated_resources.h" 14 #include "grit/theme_resources.h" 15 #include "third_party/skia/include/effects/SkGradientShader.h" 16 #include "ui/base/animation/multi_animation.h" 17 #include "ui/base/animation/slide_animation.h" 18 #include "ui/base/animation/throb_animation.h" 19 #include "ui/base/resource/resource_bundle.h" 20 #include "ui/gfx/canvas_skia.h" 21 #include "ui/gfx/favicon_size.h" 22 #include "ui/gfx/font.h" 23 #include "ui/gfx/path.h" 24 #include "ui/gfx/skbitmap_operations.h" 25 #include "views/controls/button/image_button.h" 26 #include "views/widget/tooltip_manager.h" 27 #include "views/widget/widget.h" 28 #include "views/window/non_client_view.h" 29 #include "views/window/window.h" 30 31 static const int kLeftPadding = 16; 32 static const int kTopPadding = 6; 33 static const int kRightPadding = 15; 34 static const int kBottomPadding = 5; 35 static const int kDropShadowHeight = 2; 36 static const int kToolbarOverlap = 1; 37 static const int kFaviconTitleSpacing = 4; 38 static const int kTitleCloseButtonSpacing = 5; 39 static const int kStandardTitleWidth = 175; 40 static const int kCloseButtonVertFuzz = 0; 41 static const int kCloseButtonHorzFuzz = 5; 42 43 // Vertical adjustment to the favicon when the tab has a large icon. 44 static const int kAppTapFaviconVerticalAdjustment = 2; 45 46 // When a non-mini-tab becomes a mini-tab the width of the tab animates. If 47 // the width of a mini-tab is >= kMiniTabRendererAsNormalTabWidth then the tab 48 // is rendered as a normal tab. This is done to avoid having the title 49 // immediately disappear when transitioning a tab from normal to mini-tab. 50 static const int kMiniTabRendererAsNormalTabWidth = 51 browser_defaults::kMiniTabWidth + 30; 52 53 // How opaque to make the hover state (out of 1). 54 static const double kHoverOpacity = 0.33; 55 static const double kHoverSlideOpacity = 0.5; 56 57 // Opacity for non-active selected tabs. 58 static const double kSelectedTabOpacity = .45; 59 60 // Selected (but not active) tabs have their throb value scaled down by this. 61 static const double kSelectedTabThrobScale = .5; 62 63 Tab::TabImage Tab::tab_alpha_ = {0}; 64 Tab::TabImage Tab::tab_active_ = {0}; 65 Tab::TabImage Tab::tab_inactive_ = {0}; 66 67 // Durations for the various parts of the mini tab title animation. 68 static const int kMiniTitleChangeAnimationDuration1MS = 1600; 69 static const int kMiniTitleChangeAnimationStart1MS = 0; 70 static const int kMiniTitleChangeAnimationEnd1MS = 1900; 71 static const int kMiniTitleChangeAnimationDuration2MS = 0; 72 static const int kMiniTitleChangeAnimationDuration3MS = 550; 73 static const int kMiniTitleChangeAnimationStart3MS = 150; 74 static const int kMiniTitleChangeAnimationEnd3MS = 800; 75 76 // Offset from the right edge for the start of the mini title change animation. 77 static const int kMiniTitleChangeInitialXOffset = 6; 78 79 // Radius of the radial gradient used for mini title change animation. 80 static const int kMiniTitleChangeGradientRadius = 20; 81 82 // Colors of the gradient used during the mini title change animation. 83 static const SkColor kMiniTitleChangeGradientColor1 = SK_ColorWHITE; 84 static const SkColor kMiniTitleChangeGradientColor2 = 85 SkColorSetARGB(0, 255, 255, 255); 86 87 // Hit mask constants. 88 static const SkScalar kTabCapWidth = 15; 89 static const SkScalar kTabTopCurveWidth = 4; 90 static const SkScalar kTabBottomCurveWidth = 3; 91 92 // static 93 const char Tab::kViewClassName[] = "browser/tabs/Tab"; 94 95 //////////////////////////////////////////////////////////////////////////////// 96 // Tab, public: 97 98 Tab::Tab(TabController* controller) 99 : BaseTab(controller), 100 showing_icon_(false), 101 showing_close_button_(false), 102 close_button_color_(0) { 103 InitTabResources(); 104 } 105 106 Tab::~Tab() { 107 } 108 109 void Tab::StartMiniTabTitleAnimation() { 110 if (!mini_title_animation_.get()) { 111 ui::MultiAnimation::Parts parts; 112 parts.push_back( 113 ui::MultiAnimation::Part(kMiniTitleChangeAnimationDuration1MS, 114 ui::Tween::EASE_OUT)); 115 parts.push_back( 116 ui::MultiAnimation::Part(kMiniTitleChangeAnimationDuration2MS, 117 ui::Tween::ZERO)); 118 parts.push_back( 119 ui::MultiAnimation::Part(kMiniTitleChangeAnimationDuration3MS, 120 ui::Tween::EASE_IN)); 121 parts[0].start_time_ms = kMiniTitleChangeAnimationStart1MS; 122 parts[0].end_time_ms = kMiniTitleChangeAnimationEnd1MS; 123 parts[2].start_time_ms = kMiniTitleChangeAnimationStart3MS; 124 parts[2].end_time_ms = kMiniTitleChangeAnimationEnd3MS; 125 mini_title_animation_.reset(new ui::MultiAnimation(parts)); 126 mini_title_animation_->SetContainer(animation_container()); 127 mini_title_animation_->set_delegate(this); 128 } 129 mini_title_animation_->Start(); 130 } 131 132 void Tab::StopMiniTabTitleAnimation() { 133 if (mini_title_animation_.get()) 134 mini_title_animation_->Stop(); 135 } 136 137 // static 138 gfx::Size Tab::GetMinimumUnselectedSize() { 139 InitTabResources(); 140 141 gfx::Size minimum_size; 142 minimum_size.set_width(kLeftPadding + kRightPadding); 143 // Since we use bitmap images, the real minimum height of the image is 144 // defined most accurately by the height of the end cap images. 145 minimum_size.set_height(tab_active_.image_l->height()); 146 return minimum_size; 147 } 148 149 // static 150 gfx::Size Tab::GetMinimumSelectedSize() { 151 gfx::Size minimum_size = GetMinimumUnselectedSize(); 152 minimum_size.set_width(kLeftPadding + kFaviconSize + kRightPadding); 153 return minimum_size; 154 } 155 156 // static 157 gfx::Size Tab::GetStandardSize() { 158 gfx::Size standard_size = GetMinimumUnselectedSize(); 159 standard_size.set_width( 160 standard_size.width() + kFaviconTitleSpacing + kStandardTitleWidth); 161 return standard_size; 162 } 163 164 // static 165 int Tab::GetMiniWidth() { 166 return browser_defaults::kMiniTabWidth; 167 } 168 169 //////////////////////////////////////////////////////////////////////////////// 170 // Tab, protected: 171 172 const gfx::Rect& Tab::GetTitleBounds() const { 173 return title_bounds_; 174 } 175 176 const gfx::Rect& Tab::GetIconBounds() const { 177 return favicon_bounds_; 178 } 179 180 void Tab::DataChanged(const TabRendererData& old) { 181 if (data().blocked == old.blocked) 182 return; 183 184 if (data().blocked) 185 StartPulse(); 186 else 187 StopPulse(); 188 } 189 190 //////////////////////////////////////////////////////////////////////////////// 191 // Tab, views::View overrides: 192 193 void Tab::OnPaint(gfx::Canvas* canvas) { 194 // Don't paint if we're narrower than we can render correctly. (This should 195 // only happen during animations). 196 if (width() < GetMinimumUnselectedSize().width() && !data().mini) 197 return; 198 199 // See if the model changes whether the icons should be painted. 200 const bool show_icon = ShouldShowIcon(); 201 const bool show_close_button = ShouldShowCloseBox(); 202 if (show_icon != showing_icon_ || show_close_button != showing_close_button_) 203 Layout(); 204 205 PaintTabBackground(canvas); 206 207 SkColor title_color = GetThemeProvider()-> 208 GetColor(IsSelected() ? 209 ThemeService::COLOR_TAB_TEXT : 210 ThemeService::COLOR_BACKGROUND_TAB_TEXT); 211 212 if (!data().mini || width() > kMiniTabRendererAsNormalTabWidth) 213 PaintTitle(canvas, title_color); 214 215 if (show_icon) 216 PaintIcon(canvas); 217 218 // If the close button color has changed, generate a new one. 219 if (!close_button_color_ || title_color != close_button_color_) { 220 close_button_color_ = title_color; 221 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 222 close_button()->SetBackground(close_button_color_, 223 rb.GetBitmapNamed(IDR_TAB_CLOSE), 224 rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK)); 225 } 226 } 227 228 void Tab::Layout() { 229 gfx::Rect lb = GetContentsBounds(); 230 if (lb.IsEmpty()) 231 return; 232 lb.Inset(kLeftPadding, kTopPadding, kRightPadding, kBottomPadding); 233 234 // The height of the content of the Tab is the largest of the favicon, 235 // the title text and the close button graphic. 236 int content_height = std::max(kFaviconSize, font_height()); 237 gfx::Size close_button_size(close_button()->GetPreferredSize()); 238 content_height = std::max(content_height, close_button_size.height()); 239 240 // Size the Favicon. 241 showing_icon_ = ShouldShowIcon(); 242 if (showing_icon_) { 243 // Use the size of the favicon as apps use a bigger favicon size. 244 int favicon_top = kTopPadding + content_height / 2 - kFaviconSize / 2; 245 int favicon_left = lb.x(); 246 favicon_bounds_.SetRect(favicon_left, favicon_top, 247 kFaviconSize, kFaviconSize); 248 if (data().mini && width() < kMiniTabRendererAsNormalTabWidth) { 249 // Adjust the location of the favicon when transitioning from a normal 250 // tab to a mini-tab. 251 int mini_delta = kMiniTabRendererAsNormalTabWidth - GetMiniWidth(); 252 int ideal_delta = width() - GetMiniWidth(); 253 if (ideal_delta < mini_delta) { 254 int ideal_x = (GetMiniWidth() - kFaviconSize) / 2; 255 int x = favicon_bounds_.x() + static_cast<int>( 256 (1 - static_cast<float>(ideal_delta) / 257 static_cast<float>(mini_delta)) * 258 (ideal_x - favicon_bounds_.x())); 259 favicon_bounds_.set_x(x); 260 } 261 } 262 } else { 263 favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0); 264 } 265 266 // Size the Close button. 267 showing_close_button_ = ShouldShowCloseBox(); 268 if (showing_close_button_) { 269 int close_button_top = kTopPadding + kCloseButtonVertFuzz + 270 (content_height - close_button_size.height()) / 2; 271 // If the ratio of the close button size to tab width exceeds the maximum. 272 close_button()->SetBounds(lb.width() + kCloseButtonHorzFuzz, 273 close_button_top, close_button_size.width(), 274 close_button_size.height()); 275 close_button()->SetVisible(true); 276 } else { 277 close_button()->SetBounds(0, 0, 0, 0); 278 close_button()->SetVisible(false); 279 } 280 281 int title_left = favicon_bounds_.right() + kFaviconTitleSpacing; 282 int title_top = kTopPadding + (content_height - font_height()) / 2; 283 // Size the Title text to fill the remaining space. 284 if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) { 285 // If the user has big fonts, the title will appear rendered too far down 286 // on the y-axis if we use the regular top padding, so we need to adjust it 287 // so that the text appears centered. 288 gfx::Size minimum_size = GetMinimumUnselectedSize(); 289 int text_height = title_top + font_height() + kBottomPadding; 290 if (text_height > minimum_size.height()) 291 title_top -= (text_height - minimum_size.height()) / 2; 292 293 int title_width; 294 if (close_button()->IsVisible()) { 295 title_width = std::max(close_button()->x() - 296 kTitleCloseButtonSpacing - title_left, 0); 297 } else { 298 title_width = std::max(lb.width() - title_left, 0); 299 } 300 title_bounds_.SetRect(title_left, title_top, title_width, font_height()); 301 } else { 302 title_bounds_.SetRect(title_left, title_top, 0, 0); 303 } 304 305 // Certain UI elements within the Tab (the favicon, etc.) are not represented 306 // as child Views (which is the preferred method). Instead, these UI elements 307 // are drawn directly on the canvas from within Tab::OnPaint(). The Tab's 308 // child Views (for example, the Tab's close button which is a views::Button 309 // instance) are automatically mirrored by the mirroring infrastructure in 310 // views. The elements Tab draws directly on the canvas need to be manually 311 // mirrored if the View's layout is right-to-left. 312 title_bounds_.set_x(GetMirroredXForRect(title_bounds_)); 313 } 314 315 void Tab::OnThemeChanged() { 316 LoadTabImages(); 317 } 318 319 std::string Tab::GetClassName() const { 320 return kViewClassName; 321 } 322 323 bool Tab::HasHitTestMask() const { 324 return true; 325 } 326 327 void Tab::GetHitTestMask(gfx::Path* path) const { 328 DCHECK(path); 329 330 SkScalar h = SkIntToScalar(height()); 331 SkScalar w = SkIntToScalar(width()); 332 333 path->moveTo(0, h); 334 335 // Left end cap. 336 path->lineTo(kTabBottomCurveWidth, h - kTabBottomCurveWidth); 337 path->lineTo(kTabCapWidth - kTabTopCurveWidth, kTabTopCurveWidth); 338 path->lineTo(kTabCapWidth, 0); 339 340 // Connect to the right cap. 341 path->lineTo(w - kTabCapWidth, 0); 342 343 // Right end cap. 344 path->lineTo(w - kTabCapWidth + kTabTopCurveWidth, kTabTopCurveWidth); 345 path->lineTo(w - kTabBottomCurveWidth, h - kTabBottomCurveWidth); 346 path->lineTo(w, h); 347 348 // Close out the path. 349 path->lineTo(0, h); 350 path->close(); 351 } 352 353 bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) { 354 origin->set_x(title_bounds_.x() + 10); 355 origin->set_y(-views::TooltipManager::GetTooltipHeight() - 4); 356 return true; 357 } 358 359 void Tab::OnMouseMoved(const views::MouseEvent& event) { 360 hover_point_ = event.location(); 361 // We need to redraw here because otherwise the hover glow does not update 362 // and follow the new mouse position. 363 SchedulePaint(); 364 } 365 366 //////////////////////////////////////////////////////////////////////////////// 367 // Tab, private 368 369 void Tab::PaintTabBackground(gfx::Canvas* canvas) { 370 if (IsActive()) { 371 PaintActiveTabBackground(canvas); 372 } else { 373 if (mini_title_animation_.get() && mini_title_animation_->is_animating()) 374 PaintInactiveTabBackgroundWithTitleChange(canvas); 375 else 376 PaintInactiveTabBackground(canvas); 377 378 double throb_value = GetThrobValue(); 379 if (throb_value > 0) { 380 canvas->SaveLayerAlpha(static_cast<int>(throb_value * 0xff), 381 gfx::Rect(width(), height())); 382 canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, 383 SkXfermode::kClear_Mode); 384 PaintActiveTabBackground(canvas); 385 canvas->Restore(); 386 } 387 } 388 } 389 390 void Tab::PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas) { 391 // Render the inactive tab background. We'll use this for clipping. 392 gfx::CanvasSkia background_canvas(width(), height(), false); 393 PaintInactiveTabBackground(&background_canvas); 394 395 SkBitmap background_image = background_canvas.ExtractBitmap(); 396 397 // Draw a radial gradient to hover_canvas. 398 gfx::CanvasSkia hover_canvas(width(), height(), false); 399 int radius = kMiniTitleChangeGradientRadius; 400 int x0 = width() + radius - kMiniTitleChangeInitialXOffset; 401 int x1 = radius; 402 int x2 = -radius; 403 int x; 404 if (mini_title_animation_->current_part_index() == 0) { 405 x = mini_title_animation_->CurrentValueBetween(x0, x1); 406 } else if (mini_title_animation_->current_part_index() == 1) { 407 x = x1; 408 } else { 409 x = mini_title_animation_->CurrentValueBetween(x1, x2); 410 } 411 SkPaint paint; 412 SkPoint loc = { SkIntToScalar(x), SkIntToScalar(0) }; 413 SkColor colors[2]; 414 colors[0] = kMiniTitleChangeGradientColor1; 415 colors[1] = kMiniTitleChangeGradientColor2; 416 SkShader* shader = SkGradientShader::CreateRadial( 417 loc, 418 SkIntToScalar(radius), 419 colors, 420 NULL, 421 2, 422 SkShader::kClamp_TileMode); 423 paint.setShader(shader); 424 shader->unref(); 425 hover_canvas.DrawRectInt(x - radius, -radius, radius * 2, radius * 2, paint); 426 427 // Draw the radial gradient clipped to the background into hover_image. 428 SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap( 429 hover_canvas.ExtractBitmap(), background_image); 430 431 // Draw the tab background to the canvas. 432 canvas->DrawBitmapInt(background_image, 0, 0); 433 434 // And then the gradient on top of that. 435 if (mini_title_animation_->current_part_index() == 2) { 436 canvas->SaveLayerAlpha(mini_title_animation_->CurrentValueBetween(255, 0)); 437 canvas->DrawBitmapInt(hover_image, 0, 0); 438 canvas->Restore(); 439 } else { 440 canvas->DrawBitmapInt(hover_image, 0, 0); 441 } 442 } 443 444 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) { 445 // The tab image needs to be lined up with the background image 446 // so that it feels partially transparent. These offsets represent the tab 447 // position within the frame background image. 448 int offset = GetMirroredX() + background_offset_.x(); 449 450 int tab_id; 451 if (GetWidget() && 452 GetWidget()->GetWindow()->non_client_view()->UseNativeFrame()) { 453 tab_id = IDR_THEME_TAB_BACKGROUND_V; 454 } else { 455 tab_id = data().incognito ? IDR_THEME_TAB_BACKGROUND_INCOGNITO : 456 IDR_THEME_TAB_BACKGROUND; 457 } 458 459 SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id); 460 461 TabImage* tab_image = &tab_active_; 462 TabImage* tab_inactive_image = &tab_inactive_; 463 TabImage* alpha = &tab_alpha_; 464 465 // If the theme is providing a custom background image, then its top edge 466 // should be at the top of the tab. Otherwise, we assume that the background 467 // image is a composited foreground + frame image. 468 int bg_offset_y = GetThemeProvider()->HasCustomImage(tab_id) ? 469 0 : background_offset_.y(); 470 471 // We need a CanvasSkia object to be able to extract the bitmap from. 472 // We draw everything to this canvas and then output it to the canvas 473 // parameter in addition to using it to mask the hover glow if needed. 474 gfx::CanvasSkia background_canvas(width(), height(), false); 475 476 // Draw left edge. Don't draw over the toolbar, as we're not the foreground 477 // tab. 478 SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( 479 *tab_bg, offset, bg_offset_y, tab_image->l_width, height()); 480 SkBitmap theme_l = 481 SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l); 482 background_canvas.DrawBitmapInt(theme_l, 483 0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap, 484 0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap, 485 false); 486 487 // Draw right edge. Again, don't draw over the toolbar. 488 SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg, 489 offset + width() - tab_image->r_width, bg_offset_y, 490 tab_image->r_width, height()); 491 SkBitmap theme_r = 492 SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r); 493 background_canvas.DrawBitmapInt(theme_r, 494 0, 0, theme_r.width(), theme_r.height() - kToolbarOverlap, 495 width() - theme_r.width(), 0, theme_r.width(), 496 theme_r.height() - kToolbarOverlap, false); 497 498 // Draw center. Instead of masking out the top portion we simply skip over 499 // it by incrementing by kDropShadowHeight, since it's a simple rectangle. 500 // And again, don't draw over the toolbar. 501 background_canvas.TileImageInt(*tab_bg, 502 offset + tab_image->l_width, 503 bg_offset_y + kDropShadowHeight + tab_image->y_offset, 504 tab_image->l_width, 505 kDropShadowHeight + tab_image->y_offset, 506 width() - tab_image->l_width - tab_image->r_width, 507 height() - kDropShadowHeight - kToolbarOverlap - tab_image->y_offset); 508 509 canvas->DrawBitmapInt(background_canvas.ExtractBitmap(), 0, 0); 510 511 if (!GetThemeProvider()->HasCustomImage(tab_id) && 512 hover_animation() && 513 (hover_animation()->IsShowing() || hover_animation()->is_animating())) { 514 SkBitmap hover_glow = DrawHoverGlowBitmap(width(), height()); 515 // Draw the hover glow clipped to the background into hover_image. 516 SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap( 517 hover_glow, background_canvas.ExtractBitmap()); 518 canvas->DrawBitmapInt(hover_image, 0, 0); 519 } 520 521 // Now draw the highlights/shadows around the tab edge. 522 canvas->DrawBitmapInt(*tab_inactive_image->image_l, 0, 0); 523 canvas->TileImageInt(*tab_inactive_image->image_c, 524 tab_inactive_image->l_width, 0, 525 width() - tab_inactive_image->l_width - 526 tab_inactive_image->r_width, 527 height()); 528 canvas->DrawBitmapInt(*tab_inactive_image->image_r, 529 width() - tab_inactive_image->r_width, 0); 530 } 531 532 void Tab::PaintActiveTabBackground(gfx::Canvas* canvas) { 533 int offset = GetMirroredX() + background_offset_.x(); 534 ui::ThemeProvider* tp = GetThemeProvider(); 535 DCHECK(tp) << "Unable to get theme provider"; 536 537 SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(IDR_THEME_TOOLBAR); 538 539 TabImage* tab_image = &tab_active_; 540 TabImage* alpha = &tab_alpha_; 541 542 // Draw left edge. 543 SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( 544 *tab_bg, offset, 0, tab_image->l_width, height()); 545 SkBitmap theme_l = 546 SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l); 547 canvas->DrawBitmapInt(theme_l, 0, 0); 548 549 // Draw right edge. 550 SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg, 551 offset + width() - tab_image->r_width, 0, tab_image->r_width, height()); 552 SkBitmap theme_r = 553 SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r); 554 canvas->DrawBitmapInt(theme_r, width() - tab_image->r_width, 0); 555 556 // Draw center. Instead of masking out the top portion we simply skip over it 557 // by incrementing by kDropShadowHeight, since it's a simple rectangle. 558 canvas->TileImageInt(*tab_bg, 559 offset + tab_image->l_width, 560 kDropShadowHeight + tab_image->y_offset, 561 tab_image->l_width, 562 kDropShadowHeight + tab_image->y_offset, 563 width() - tab_image->l_width - tab_image->r_width, 564 height() - kDropShadowHeight - tab_image->y_offset); 565 566 // Now draw the highlights/shadows around the tab edge. 567 canvas->DrawBitmapInt(*tab_image->image_l, 0, 0); 568 canvas->TileImageInt(*tab_image->image_c, tab_image->l_width, 0, 569 width() - tab_image->l_width - tab_image->r_width, height()); 570 canvas->DrawBitmapInt(*tab_image->image_r, width() - tab_image->r_width, 0); 571 } 572 573 SkBitmap Tab::DrawHoverGlowBitmap(int width_input, int height_input) { 574 // Draw a radial gradient to hover_canvas so we can export the bitmap. 575 gfx::CanvasSkia hover_canvas(width_input, height_input, false); 576 577 // Draw a radial gradient to hover_canvas. 578 int radius = width() / 3; 579 580 SkPaint paint; 581 paint.setStyle(SkPaint::kFill_Style); 582 paint.setFlags(SkPaint::kAntiAlias_Flag); 583 SkPoint loc = { SkIntToScalar(hover_point_.x()), 584 SkIntToScalar(hover_point_.y()) }; 585 SkColor colors[2]; 586 const ui::SlideAnimation* hover_slide = hover_animation(); 587 int hover_alpha = 0; 588 if (hover_slide) { 589 hover_alpha = static_cast<int>(255 * kHoverSlideOpacity * 590 hover_slide->GetCurrentValue()); 591 } 592 colors[0] = SkColorSetARGB(hover_alpha, 255, 255, 255); 593 colors[1] = SkColorSetARGB(0, 255, 255, 255); 594 SkShader* shader = SkGradientShader::CreateRadial( 595 loc, 596 SkIntToScalar(radius), 597 colors, 598 NULL, 599 2, 600 SkShader::kClamp_TileMode); 601 // Shader can end up null when radius = 0. 602 // If so, this results in default full tab glow behavior. 603 if (shader) { 604 paint.setShader(shader); 605 shader->unref(); 606 hover_canvas.DrawRectInt(hover_point_.x() - radius, 607 hover_point_.y() - radius, 608 radius * 2, radius * 2, paint); 609 } 610 return hover_canvas.ExtractBitmap(); 611 } 612 613 int Tab::IconCapacity() const { 614 if (height() < GetMinimumUnselectedSize().height()) 615 return 0; 616 return (width() - kLeftPadding - kRightPadding) / kFaviconSize; 617 } 618 619 bool Tab::ShouldShowIcon() const { 620 if (data().mini && height() >= GetMinimumUnselectedSize().height()) 621 return true; 622 if (!data().show_icon) { 623 return false; 624 } else if (IsActive()) { 625 // The active tab clips favicon before close button. 626 return IconCapacity() >= 2; 627 } 628 // Non-selected tabs clip close button before favicon. 629 return IconCapacity() >= 1; 630 } 631 632 bool Tab::ShouldShowCloseBox() const { 633 // The active tab never clips close button. 634 return !data().mini && IsCloseable() && (IsActive() || IconCapacity() >= 3); 635 } 636 637 double Tab::GetThrobValue() { 638 bool is_selected = IsSelected(); 639 double min = is_selected ? kSelectedTabOpacity : 0; 640 double scale = is_selected ? kSelectedTabThrobScale : 1; 641 642 if (pulse_animation() && pulse_animation()->is_animating()) 643 return pulse_animation()->GetCurrentValue() * kHoverOpacity * scale + min; 644 645 if (hover_animation()) 646 return kHoverOpacity * hover_animation()->GetCurrentValue() * scale + min; 647 648 return is_selected ? kSelectedTabOpacity : 0; 649 } 650 651 //////////////////////////////////////////////////////////////////////////////// 652 // Tab, private: 653 654 // static 655 void Tab::InitTabResources() { 656 static bool initialized = false; 657 if (initialized) 658 return; 659 660 initialized = true; 661 662 // Load the tab images once now, and maybe again later if the theme changes. 663 LoadTabImages(); 664 } 665 666 // static 667 void Tab::LoadTabImages() { 668 // We're not letting people override tab images just yet. 669 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 670 671 tab_alpha_.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_LEFT); 672 tab_alpha_.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_RIGHT); 673 674 tab_active_.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT); 675 tab_active_.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER); 676 tab_active_.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT); 677 tab_active_.l_width = tab_active_.image_l->width(); 678 tab_active_.r_width = tab_active_.image_r->width(); 679 680 tab_inactive_.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT); 681 tab_inactive_.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER); 682 tab_inactive_.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT); 683 tab_inactive_.l_width = tab_inactive_.image_l->width(); 684 tab_inactive_.r_width = tab_inactive_.image_r->width(); 685 } 686