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