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
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;
55static const double kHoverSlideOpacity = 0.5;
56
57// Opacity for non-active selected tabs.
58static const double kSelectedTabOpacity = .45;
59
60// Selected (but not active) tabs have their throb value scaled down by this.
61static const double kSelectedTabThrobScale = .5;
62
63Tab::TabImage Tab::tab_alpha_ = {0};
64Tab::TabImage Tab::tab_active_ = {0};
65Tab::TabImage Tab::tab_inactive_ = {0};
66
67// Durations for the various parts of the mini tab title animation.
68static const int kMiniTitleChangeAnimationDuration1MS = 1600;
69static const int kMiniTitleChangeAnimationStart1MS = 0;
70static const int kMiniTitleChangeAnimationEnd1MS = 1900;
71static const int kMiniTitleChangeAnimationDuration2MS = 0;
72static const int kMiniTitleChangeAnimationDuration3MS = 550;
73static const int kMiniTitleChangeAnimationStart3MS = 150;
74static const int kMiniTitleChangeAnimationEnd3MS = 800;
75
76// Offset from the right edge for the start of the mini title change animation.
77static const int kMiniTitleChangeInitialXOffset = 6;
78
79// Radius of the radial gradient used for mini title change animation.
80static const int kMiniTitleChangeGradientRadius = 20;
81
82// Colors of the gradient used during the mini title change animation.
83static const SkColor kMiniTitleChangeGradientColor1 = SK_ColorWHITE;
84static const SkColor kMiniTitleChangeGradientColor2 =
85    SkColorSetARGB(0, 255, 255, 255);
86
87// Hit mask constants.
88static const SkScalar kTabCapWidth = 15;
89static const SkScalar kTabTopCurveWidth = 4;
90static const SkScalar kTabBottomCurveWidth = 3;
91
92// static
93const char Tab::kViewClassName[] = "browser/tabs/Tab";
94
95////////////////////////////////////////////////////////////////////////////////
96// Tab, public:
97
98Tab::Tab(TabController* controller)
99    : BaseTab(controller),
100      showing_icon_(false),
101      showing_close_button_(false),
102      close_button_color_(0) {
103  InitTabResources();
104}
105
106Tab::~Tab() {
107}
108
109void 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
132void Tab::StopMiniTabTitleAnimation() {
133  if (mini_title_animation_.get())
134    mini_title_animation_->Stop();
135}
136
137// static
138gfx::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
150gfx::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
157gfx::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
165int Tab::GetMiniWidth() {
166  return browser_defaults::kMiniTabWidth;
167}
168
169////////////////////////////////////////////////////////////////////////////////
170// Tab, protected:
171
172const gfx::Rect& Tab::GetTitleBounds() const {
173  return title_bounds_;
174}
175
176const gfx::Rect& Tab::GetIconBounds() const {
177  return favicon_bounds_;
178}
179
180void 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
193void 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
228void 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
315void Tab::OnThemeChanged() {
316  LoadTabImages();
317}
318
319std::string Tab::GetClassName() const {
320  return kViewClassName;
321}
322
323bool Tab::HasHitTestMask() const {
324  return true;
325}
326
327void 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
353bool 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
359void 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
369void 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
390void 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
444void 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
532void 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
573SkBitmap 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
613int Tab::IconCapacity() const {
614  if (height() < GetMinimumUnselectedSize().height())
615    return 0;
616  return (width() - kLeftPadding - kRightPadding) / kFaviconSize;
617}
618
619bool 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
632bool Tab::ShouldShowCloseBox() const {
633  // The active tab never clips close button.
634  return !data().mini && IsCloseable() && (IsActive() || IconCapacity() >= 3);
635}
636
637double 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
655void 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
667void 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