OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/tabs/tab.h" | 5 #include "chrome/browser/ui/views/tabs/tab.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/debug/alias.h" | 10 #include "base/debug/alias.h" |
(...skipping 12 matching lines...) Expand all Loading... |
23 #include "chrome/common/chrome_switches.h" | 23 #include "chrome/common/chrome_switches.h" |
24 #include "chrome/grit/generated_resources.h" | 24 #include "chrome/grit/generated_resources.h" |
25 #include "content/public/browser/user_metrics.h" | 25 #include "content/public/browser/user_metrics.h" |
26 #include "grit/theme_resources.h" | 26 #include "grit/theme_resources.h" |
27 #include "third_party/skia/include/effects/SkGradientShader.h" | 27 #include "third_party/skia/include/effects/SkGradientShader.h" |
28 #include "ui/accessibility/ax_view_state.h" | 28 #include "ui/accessibility/ax_view_state.h" |
29 #include "ui/base/l10n/l10n_util.h" | 29 #include "ui/base/l10n/l10n_util.h" |
30 #include "ui/base/models/list_selection_model.h" | 30 #include "ui/base/models/list_selection_model.h" |
31 #include "ui/base/resource/resource_bundle.h" | 31 #include "ui/base/resource/resource_bundle.h" |
32 #include "ui/base/theme_provider.h" | 32 #include "ui/base/theme_provider.h" |
| 33 #include "ui/compositor/layer_animator.h" |
| 34 #include "ui/compositor/paint_recorder.h" |
33 #include "ui/gfx/animation/animation_container.h" | 35 #include "ui/gfx/animation/animation_container.h" |
34 #include "ui/gfx/animation/multi_animation.h" | 36 #include "ui/gfx/animation/multi_animation.h" |
35 #include "ui/gfx/animation/throb_animation.h" | 37 #include "ui/gfx/animation/throb_animation.h" |
36 #include "ui/gfx/canvas.h" | 38 #include "ui/gfx/canvas.h" |
37 #include "ui/gfx/color_analysis.h" | 39 #include "ui/gfx/color_analysis.h" |
38 #include "ui/gfx/favicon_size.h" | 40 #include "ui/gfx/favicon_size.h" |
39 #include "ui/gfx/geometry/rect_conversions.h" | 41 #include "ui/gfx/geometry/rect_conversions.h" |
40 #include "ui/gfx/image/image_skia_operations.h" | 42 #include "ui/gfx/image/image_skia_operations.h" |
41 #include "ui/gfx/paint_vector_icon.h" | 43 #include "ui/gfx/paint_vector_icon.h" |
42 #include "ui/gfx/path.h" | 44 #include "ui/gfx/path.h" |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 // The minimum opacity (out of 1) when a tab (either active or inactive) is | 132 // The minimum opacity (out of 1) when a tab (either active or inactive) is |
131 // throbbing in the immersive mode light strip. | 133 // throbbing in the immersive mode light strip. |
132 const double kImmersiveTabMinThrobOpacity = 0.66; | 134 const double kImmersiveTabMinThrobOpacity = 0.66; |
133 | 135 |
134 // Number of steps in the immersive mode loading animation. | 136 // Number of steps in the immersive mode loading animation. |
135 const int kImmersiveLoadingStepCount = 32; | 137 const int kImmersiveLoadingStepCount = 32; |
136 | 138 |
137 const char kTabCloseButtonName[] = "TabCloseButton"; | 139 const char kTabCloseButtonName[] = "TabCloseButton"; |
138 const int kTabCloseButtonSize = 16; | 140 const int kTabCloseButtonSize = 16; |
139 | 141 |
140 // Layer-backed view for updating a waiting or loading tab spinner. | |
141 class ThrobberView : public views::View { | |
142 public: | |
143 explicit ThrobberView(Tab* owner) : owner_(owner) { | |
144 // Since the throbber animates, paint to a separate layer do reduce repaint | |
145 // overheads. | |
146 SetPaintToLayer(true); | |
147 SetFillsBoundsOpaquely(false); | |
148 } | |
149 | |
150 // views::View: | |
151 bool CanProcessEventsWithinSubtree() const override { return false; } | |
152 | |
153 void OnPaint(gfx::Canvas* canvas) override { | |
154 const TabRendererData::NetworkState state = owner_->data().network_state; | |
155 if (state == TabRendererData::NETWORK_STATE_NONE) | |
156 return; | |
157 | |
158 const gfx::Rect bounds = GetLocalBounds(); | |
159 | |
160 // Paint network activity (aka throbber) animation frame. | |
161 ui::ThemeProvider* tp = GetThemeProvider(); | |
162 if (state == TabRendererData::NETWORK_STATE_WAITING) { | |
163 if (waiting_start_time_ == base::TimeTicks()) | |
164 waiting_start_time_ = base::TimeTicks::Now(); | |
165 | |
166 waiting_state_.elapsed_time = | |
167 base::TimeTicks::Now() - waiting_start_time_; | |
168 gfx::PaintThrobberWaiting( | |
169 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING), | |
170 waiting_state_.elapsed_time); | |
171 } else { | |
172 if (loading_start_time_ == base::TimeTicks()) | |
173 loading_start_time_ = base::TimeTicks::Now(); | |
174 | |
175 waiting_state_.color = | |
176 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING); | |
177 gfx::PaintThrobberSpinningAfterWaiting( | |
178 canvas, bounds, | |
179 tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING), | |
180 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); | |
181 } | |
182 } | |
183 | |
184 private: | |
185 Tab* owner_; // Weak. Owns |this|. | |
186 | |
187 // The point in time when the tab icon was first painted in the waiting state. | |
188 base::TimeTicks waiting_start_time_; | |
189 | |
190 // The point in time when the tab icon was first painted in the loading state. | |
191 base::TimeTicks loading_start_time_; | |
192 | |
193 // Paint state for the throbber after the most recent waiting paint. | |
194 gfx::ThrobberWaitingState waiting_state_; | |
195 | |
196 DISALLOW_COPY_AND_ASSIGN(ThrobberView); | |
197 }; | |
198 | |
199 void DrawIconAtLocation(gfx::Canvas* canvas, | 142 void DrawIconAtLocation(gfx::Canvas* canvas, |
200 const gfx::ImageSkia& image, | 143 const gfx::ImageSkia& image, |
201 int image_offset, | 144 int image_offset, |
202 int dst_x, | 145 int dst_x, |
203 int dst_y, | 146 int dst_y, |
204 int icon_width, | 147 int icon_width, |
205 int icon_height, | 148 int icon_height, |
206 bool filter, | 149 bool filter, |
207 const SkPaint& paint) { | 150 const SkPaint& paint) { |
208 // NOTE: the clipping is a work around for 69528, it shouldn't be necessary. | 151 // NOTE: the clipping is a work around for 69528, it shouldn't be necessary. |
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
444 // ImageCacheEntry | 387 // ImageCacheEntry |
445 | 388 |
446 Tab::ImageCacheEntry::ImageCacheEntry() | 389 Tab::ImageCacheEntry::ImageCacheEntry() |
447 : resource_id(-1), | 390 : resource_id(-1), |
448 scale_factor(ui::SCALE_FACTOR_NONE) { | 391 scale_factor(ui::SCALE_FACTOR_NONE) { |
449 } | 392 } |
450 | 393 |
451 Tab::ImageCacheEntry::~ImageCacheEntry() {} | 394 Tab::ImageCacheEntry::~ImageCacheEntry() {} |
452 | 395 |
453 //////////////////////////////////////////////////////////////////////////////// | 396 //////////////////////////////////////////////////////////////////////////////// |
| 397 // ThrobberView |
| 398 |
| 399 // Layer-backed view for updating a waiting or loading tab spinner. |
| 400 class Tab::ThrobberView : public views::View { |
| 401 public: |
| 402 explicit ThrobberView(Tab* owner) : owner_(owner), waiting_arc_(180) { |
| 403 // Since the throbber animates, paint to a separate layer do reduce repaint |
| 404 // overheads. |
| 405 SetPaintToLayer(true); |
| 406 SetFillsBoundsOpaquely(false); |
| 407 |
| 408 waiting_mask_.SetFillsBoundsOpaquely(false); |
| 409 waiting_mask_.SetMasksToBounds(true); |
| 410 waiting_mask_.Add(waiting_arc_.layer()); |
| 411 } |
| 412 |
| 413 void SchedulePaintIfRequired() { |
| 414 if (NeedsPaint()) |
| 415 SchedulePaint(); |
| 416 } |
| 417 |
| 418 // views::View: |
| 419 bool CanProcessEventsWithinSubtree() const override { return false; } |
| 420 |
| 421 void OnBoundsChanged(const gfx::Rect& previous_bounds) override { |
| 422 gfx::Rect bounds = GetLocalBounds(); |
| 423 waiting_arc_.layer()->SetBounds( |
| 424 gfx::Rect(0, 0, bounds.width() * Arc::kAA, bounds.height() * Arc::kAA)); |
| 425 |
| 426 bounds.set_width(bounds.width() / 2); |
| 427 waiting_mask_.SetBounds(bounds); |
| 428 } |
| 429 |
| 430 void OnPaint(gfx::Canvas* canvas) override { |
| 431 if (state_ == TabRendererData::NETWORK_STATE_NONE) |
| 432 return; |
| 433 |
| 434 const gfx::Rect bounds = GetLocalBounds(); |
| 435 |
| 436 // Paint network activity (aka throbber) animation frame. |
| 437 ui::ThemeProvider* tp = owner_->GetThemeProvider(); |
| 438 if (state_ == TabRendererData::NETWORK_STATE_WAITING) { |
| 439 // Painted by Arc. |
| 440 } else { |
| 441 if (loading_start_time_ == base::TimeTicks()) |
| 442 loading_start_time_ = base::TimeTicks::Now(); |
| 443 |
| 444 waiting_state_.color = |
| 445 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING); |
| 446 gfx::PaintThrobberSpinningAfterWaiting( |
| 447 canvas, bounds, |
| 448 tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING), |
| 449 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); |
| 450 } |
| 451 } |
| 452 |
| 453 private: |
| 454 class Arc : public ui::LayerDelegate { |
| 455 public: |
| 456 // Since the rotation transform mis-aligns the pixel anti-aliasing done by |
| 457 // Skia, perform a kind of FSAA by drawing on a larger canvas and scaling |
| 458 // down as part of the transform. |
| 459 static const int kAA = 1; |
| 460 |
| 461 explicit Arc(SkScalar sweep) |
| 462 : color_(SK_ColorRED), sweep_(sweep), layer_(ui::LAYER_TEXTURED) { |
| 463 layer_.set_delegate(this); |
| 464 layer_.SetFillsBoundsOpaquely(false); |
| 465 } |
| 466 |
| 467 ui::Layer* layer() { return &layer_; } |
| 468 |
| 469 void set_color(SkColor color) { color_ = color; } |
| 470 |
| 471 void SetAngle(SkScalar angle) { |
| 472 const gfx::Size size = layer()->size(); |
| 473 gfx::Transform transform; |
| 474 transform.Translate(size.width() / 2.0 / kAA, size.height() / 2.0 / kAA); |
| 475 transform.Rotate(-angle); |
| 476 transform.Scale(1.0 / kAA, 1.0 / kAA); |
| 477 transform.Translate(-size.width() / 2, -size.height() / 2); |
| 478 layer()->SetTransform(transform); |
| 479 } |
| 480 |
| 481 // LayerDelegate: |
| 482 void OnPaintLayer(const ui::PaintContext& context) override { |
| 483 const gfx::Size size = layer()->size(); |
| 484 ui::PaintRecorder recorder(context, size); |
| 485 gfx::PaintThrobberArc(recorder.canvas(), gfx::Rect(size), color_, -90, |
| 486 sweep_); |
| 487 } |
| 488 |
| 489 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} |
| 490 void OnDeviceScaleFactorChanged(float device_scale_factor) override {} |
| 491 base::Closure PrepareForLayerBoundsChange() override { |
| 492 return base::Closure(); |
| 493 } |
| 494 |
| 495 private: |
| 496 SkColor color_; |
| 497 SkScalar sweep_; |
| 498 ui::Layer layer_; |
| 499 |
| 500 DISALLOW_COPY_AND_ASSIGN(Arc); |
| 501 }; |
| 502 |
| 503 void ApplyWaitingRotation(const base::TimeDelta& elapsed_time) { |
| 504 const base::TimeDelta revolution_time = |
| 505 base::TimeDelta::FromMilliseconds(1320); |
| 506 bool needs_mask = elapsed_time < revolution_time / 2; |
| 507 waiting_mask_.SetMasksToBounds(needs_mask); |
| 508 waiting_arc_.SetAngle(360 * waiting_state_.elapsed_time / revolution_time); |
| 509 waiting_arc_.set_color( |
| 510 GetThemeProvider()->GetColor(ThemeProperties::COLOR_THROBBER_WAITING)); |
| 511 } |
| 512 |
| 513 bool NeedsPaint() { |
| 514 if (bounds().IsEmpty()) |
| 515 return false; |
| 516 |
| 517 TabRendererData::NetworkState new_state = owner_->data().network_state; |
| 518 const bool changing_state = new_state != state_; |
| 519 |
| 520 // Waiting throbber is fully layer-backed. |
| 521 if (new_state == TabRendererData::NETWORK_STATE_WAITING) { |
| 522 if (waiting_start_time_ == base::TimeTicks()) |
| 523 waiting_start_time_ = base::TimeTicks::Now(); |
| 524 |
| 525 state_ = new_state; |
| 526 waiting_state_.elapsed_time = |
| 527 base::TimeTicks::Now() - waiting_start_time_; |
| 528 layer()->Add(&waiting_mask_); |
| 529 ApplyWaitingRotation(waiting_state_.elapsed_time); |
| 530 return changing_state; |
| 531 } |
| 532 |
| 533 if (state_ == TabRendererData::NETWORK_STATE_WAITING) |
| 534 layer()->Remove(&waiting_mask_); |
| 535 |
| 536 state_ = new_state; |
| 537 |
| 538 return true; |
| 539 } |
| 540 |
| 541 Tab* owner_; // Weak. Owns this. |
| 542 |
| 543 TabRendererData::NetworkState state_ = TabRendererData::NETWORK_STATE_NONE; |
| 544 |
| 545 // The point in time when the tab icon was first painted in the waiting state. |
| 546 base::TimeTicks waiting_start_time_; |
| 547 |
| 548 // The point in time when the tab icon was first painted in the loading state. |
| 549 base::TimeTicks loading_start_time_; |
| 550 |
| 551 // Paint state for the throbber after the most recent waiting paint. |
| 552 gfx::ThrobberWaitingState waiting_state_; |
| 553 |
| 554 ui::Layer waiting_mask_; |
| 555 Arc waiting_arc_; |
| 556 |
| 557 DISALLOW_COPY_AND_ASSIGN(ThrobberView); |
| 558 }; |
| 559 |
| 560 //////////////////////////////////////////////////////////////////////////////// |
454 // Tab, statics: | 561 // Tab, statics: |
455 | 562 |
456 // static | 563 // static |
457 const char Tab::kViewClassName[] = "Tab"; | 564 const char Tab::kViewClassName[] = "Tab"; |
458 Tab::TabImage Tab::tab_active_ = {0}; | 565 Tab::TabImage Tab::tab_active_ = {0}; |
459 Tab::TabImage Tab::tab_inactive_ = {0}; | 566 Tab::TabImage Tab::tab_inactive_ = {0}; |
460 Tab::TabImage Tab::tab_alpha_ = {0}; | 567 Tab::TabImage Tab::tab_alpha_ = {0}; |
461 Tab::ImageCache* Tab::image_cache_ = NULL; | 568 Tab::ImageCache* Tab::image_cache_ = NULL; |
462 | 569 |
463 //////////////////////////////////////////////////////////////////////////////// | 570 //////////////////////////////////////////////////////////////////////////////// |
(...skipping 989 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1453 // the left since the shoulder of a dragged tab can still paint over the icon. | 1560 // the left since the shoulder of a dragged tab can still paint over the icon. |
1454 gfx::Rect clip = GetLocalBounds(); | 1561 gfx::Rect clip = GetLocalBounds(); |
1455 const bool needs_throbber = state != TabRendererData::NETWORK_STATE_NONE && | 1562 const bool needs_throbber = state != TabRendererData::NETWORK_STATE_NONE && |
1456 controller_->ShouldPaintTab(this, &clip) && | 1563 controller_->ShouldPaintTab(this, &clip) && |
1457 clip.x() == 0 && clip.Contains(favicon_bounds_); | 1564 clip.x() == 0 && clip.Contains(favicon_bounds_); |
1458 | 1565 |
1459 if (needs_throbber != throbber_->visible()) { | 1566 if (needs_throbber != throbber_->visible()) { |
1460 ScheduleIconPaint(); // Repaint the icon area to update favicon visibility. | 1567 ScheduleIconPaint(); // Repaint the icon area to update favicon visibility. |
1461 throbber_->SetVisible(needs_throbber); | 1568 throbber_->SetVisible(needs_throbber); |
1462 } | 1569 } |
1463 throbber_->SchedulePaint(); | 1570 throbber_->SchedulePaintIfRequired(); |
1464 } | 1571 } |
1465 | 1572 |
1466 int Tab::IconCapacity() const { | 1573 int Tab::IconCapacity() const { |
1467 const gfx::Size min_size(GetMinimumUnselectedSize()); | 1574 const gfx::Size min_size(GetMinimumUnselectedSize()); |
1468 if (height() < min_size.height()) | 1575 if (height() < min_size.height()) |
1469 return 0; | 1576 return 0; |
1470 const int available_width = std::max(0, width() - min_size.width()); | 1577 const int available_width = std::max(0, width() - min_size.width()); |
1471 // All icons are the same size as the favicon. | 1578 // All icons are the same size as the favicon. |
1472 const int icon_width = gfx::kFaviconSize; | 1579 const int icon_width = gfx::kFaviconSize; |
1473 // We need enough space to display the icons flush against each other. | 1580 // We need enough space to display the icons flush against each other. |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1732 const gfx::ImageSkia& image) { | 1839 const gfx::ImageSkia& image) { |
1733 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); | 1840 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); |
1734 ImageCacheEntry entry; | 1841 ImageCacheEntry entry; |
1735 entry.resource_id = resource_id; | 1842 entry.resource_id = resource_id; |
1736 entry.scale_factor = scale_factor; | 1843 entry.scale_factor = scale_factor; |
1737 entry.image = image; | 1844 entry.image = image; |
1738 image_cache_->push_front(entry); | 1845 image_cache_->push_front(entry); |
1739 if (image_cache_->size() > kMaxImageCacheSize) | 1846 if (image_cache_->size() > kMaxImageCacheSize) |
1740 image_cache_->pop_back(); | 1847 image_cache_->pop_back(); |
1741 } | 1848 } |
OLD | NEW |