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
Tab(TabController * controller)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
~Tab()106 Tab::~Tab() {
107 }
108
StartMiniTabTitleAnimation()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
StopMiniTabTitleAnimation()132 void Tab::StopMiniTabTitleAnimation() {
133 if (mini_title_animation_.get())
134 mini_title_animation_->Stop();
135 }
136
137 // static
GetMinimumUnselectedSize()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
GetMinimumSelectedSize()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
GetStandardSize()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
GetMiniWidth()165 int Tab::GetMiniWidth() {
166 return browser_defaults::kMiniTabWidth;
167 }
168
169 ////////////////////////////////////////////////////////////////////////////////
170 // Tab, protected:
171
GetTitleBounds() const172 const gfx::Rect& Tab::GetTitleBounds() const {
173 return title_bounds_;
174 }
175
GetIconBounds() const176 const gfx::Rect& Tab::GetIconBounds() const {
177 return favicon_bounds_;
178 }
179
DataChanged(const TabRendererData & old)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
OnPaint(gfx::Canvas * canvas)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
Layout()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
OnThemeChanged()315 void Tab::OnThemeChanged() {
316 LoadTabImages();
317 }
318
GetClassName() const319 std::string Tab::GetClassName() const {
320 return kViewClassName;
321 }
322
HasHitTestMask() const323 bool Tab::HasHitTestMask() const {
324 return true;
325 }
326
GetHitTestMask(gfx::Path * path) const327 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
GetTooltipTextOrigin(const gfx::Point & p,gfx::Point * origin)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
OnMouseMoved(const views::MouseEvent & event)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
PaintTabBackground(gfx::Canvas * canvas)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
PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas * canvas)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
PaintInactiveTabBackground(gfx::Canvas * canvas)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
PaintActiveTabBackground(gfx::Canvas * canvas)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
DrawHoverGlowBitmap(int width_input,int height_input)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
IconCapacity() const613 int Tab::IconCapacity() const {
614 if (height() < GetMinimumUnselectedSize().height())
615 return 0;
616 return (width() - kLeftPadding - kRightPadding) / kFaviconSize;
617 }
618
ShouldShowIcon() const619 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
ShouldShowCloseBox() const632 bool Tab::ShouldShowCloseBox() const {
633 // The active tab never clips close button.
634 return !data().mini && IsCloseable() && (IsActive() || IconCapacity() >= 3);
635 }
636
GetThrobValue()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
InitTabResources()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
LoadTabImages()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