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