| 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 119 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 | 130 // The minimum opacity (out of 1) when a tab (either active or inactive) is |
| 131 // throbbing in the immersive mode light strip. | 131 // throbbing in the immersive mode light strip. |
| 132 const double kImmersiveTabMinThrobOpacity = 0.66; | 132 const double kImmersiveTabMinThrobOpacity = 0.66; |
| 133 | 133 |
| 134 // Number of steps in the immersive mode loading animation. | 134 // Number of steps in the immersive mode loading animation. |
| 135 const int kImmersiveLoadingStepCount = 32; | 135 const int kImmersiveLoadingStepCount = 32; |
| 136 | 136 |
| 137 const char kTabCloseButtonName[] = "TabCloseButton"; | 137 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 138 const int kTabCloseButtonSize = 16; | 138 const int kTabCloseButtonSize = 16; |
| 139 | 139 |
| 140 class ThrobberView : public views::View { |
| 141 public: |
| 142 ThrobberView(Tab* owner, const gfx::Rect& bounds) : owner_(owner) { |
| 143 SetPaintToLayer(true); |
| 144 SetFillsBoundsOpaquely(false); |
| 145 SetBoundsRect(bounds); |
| 146 owner_->AddChildView(this); |
| 147 } |
| 148 |
| 149 // views::View: |
| 150 void OnPaint(gfx::Canvas* canvas) override { |
| 151 const TabRendererData::NetworkState state = owner_->data().network_state; |
| 152 if (state == TabRendererData::NETWORK_STATE_NONE) |
| 153 return; |
| 154 |
| 155 const gfx::Rect bounds = GetLocalBounds(); // Adjust for RTL? |
| 156 |
| 157 // Paint network activity (aka throbber) animation frame. |
| 158 ui::ThemeProvider* tp = owner_->GetThemeProvider(); |
| 159 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
| 160 if (waiting_start_time_ == base::TimeTicks()) |
| 161 waiting_start_time_ = base::TimeTicks::Now(); |
| 162 |
| 163 waiting_state_.elapsed_time = |
| 164 base::TimeTicks::Now() - waiting_start_time_; |
| 165 gfx::PaintThrobberWaiting( |
| 166 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING), |
| 167 waiting_state_.elapsed_time); |
| 168 } else { |
| 169 if (loading_start_time_ == base::TimeTicks()) |
| 170 loading_start_time_ = base::TimeTicks::Now(); |
| 171 |
| 172 waiting_state_.color = |
| 173 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING); |
| 174 gfx::PaintThrobberSpinningAfterWaiting( |
| 175 canvas, bounds, |
| 176 tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING), |
| 177 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); |
| 178 } |
| 179 } |
| 180 |
| 181 private: |
| 182 Tab* owner_; // Weak. Owns this. |
| 183 |
| 184 // The point in time when the tab icon was first painted in the waiting state. |
| 185 base::TimeTicks waiting_start_time_; |
| 186 |
| 187 // The point in time when the tab icon was first painted in the loading state. |
| 188 base::TimeTicks loading_start_time_; |
| 189 |
| 190 // Paint state for the throbber after the most recent waiting paint. |
| 191 gfx::ThrobberWaitingState waiting_state_; |
| 192 |
| 193 DISALLOW_COPY_AND_ASSIGN(ThrobberView); |
| 194 }; |
| 195 |
| 140 void DrawIconAtLocation(gfx::Canvas* canvas, | 196 void DrawIconAtLocation(gfx::Canvas* canvas, |
| 141 const gfx::ImageSkia& image, | 197 const gfx::ImageSkia& image, |
| 142 int image_offset, | 198 int image_offset, |
| 143 int dst_x, | 199 int dst_x, |
| 144 int dst_y, | 200 int dst_y, |
| 145 int icon_width, | 201 int icon_width, |
| 146 int icon_height, | 202 int icon_height, |
| 147 bool filter, | 203 bool filter, |
| 148 const SkPaint& paint) { | 204 const SkPaint& paint) { |
| 149 // NOTE: the clipping is a work around for 69528, it shouldn't be necessary. | 205 // NOTE: the clipping is a work around for 69528, it shouldn't be necessary. |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 405 // Tab, public: | 461 // Tab, public: |
| 406 | 462 |
| 407 Tab::Tab(TabController* controller) | 463 Tab::Tab(TabController* controller) |
| 408 : controller_(controller), | 464 : controller_(controller), |
| 409 closing_(false), | 465 closing_(false), |
| 410 dragging_(false), | 466 dragging_(false), |
| 411 detached_(false), | 467 detached_(false), |
| 412 favicon_hiding_offset_(0), | 468 favicon_hiding_offset_(0), |
| 413 immersive_loading_step_(0), | 469 immersive_loading_step_(0), |
| 414 should_display_crashed_favicon_(false), | 470 should_display_crashed_favicon_(false), |
| 471 throbber_(nullptr), |
| 415 media_indicator_button_(nullptr), | 472 media_indicator_button_(nullptr), |
| 416 close_button_(nullptr), | 473 close_button_(nullptr), |
| 417 title_(new views::Label()), | 474 title_(new views::Label()), |
| 418 tab_activated_with_last_tap_down_(false), | 475 tab_activated_with_last_tap_down_(false), |
| 419 hover_controller_(this), | 476 hover_controller_(this), |
| 420 showing_icon_(false), | 477 showing_icon_(false), |
| 421 showing_media_indicator_(false), | 478 showing_media_indicator_(false), |
| 422 showing_close_button_(false), | 479 showing_close_button_(false), |
| 423 button_color_(SK_ColorTRANSPARENT) { | 480 button_color_(SK_ColorTRANSPARENT) { |
| 424 DCHECK(controller); | 481 DCHECK(controller); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 545 } | 602 } |
| 546 | 603 |
| 547 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { | 604 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { |
| 548 if (state == data_.network_state && | 605 if (state == data_.network_state && |
| 549 state == TabRendererData::NETWORK_STATE_NONE) { | 606 state == TabRendererData::NETWORK_STATE_NONE) { |
| 550 // If the network state is none and hasn't changed, do nothing. Otherwise we | 607 // If the network state is none and hasn't changed, do nothing. Otherwise we |
| 551 // need to advance the animation frame. | 608 // need to advance the animation frame. |
| 552 return; | 609 return; |
| 553 } | 610 } |
| 554 | 611 |
| 555 TabRendererData::NetworkState old_state = data_.network_state; | |
| 556 data_.network_state = state; | 612 data_.network_state = state; |
| 557 AdvanceLoadingAnimation(old_state, state); | 613 AdvanceLoadingAnimation(state); |
| 558 } | 614 } |
| 559 | 615 |
| 560 void Tab::StartPulse() { | 616 void Tab::StartPulse() { |
| 561 pulse_animation_.reset(new gfx::ThrobAnimation(this)); | 617 pulse_animation_.reset(new gfx::ThrobAnimation(this)); |
| 562 pulse_animation_->SetSlideDuration(kPulseDurationMs); | 618 pulse_animation_->SetSlideDuration(kPulseDurationMs); |
| 563 if (animation_container_.get()) | 619 if (animation_container_.get()) |
| 564 pulse_animation_->SetContainer(animation_container_.get()); | 620 pulse_animation_->SetContainer(animation_container_.get()); |
| 565 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); | 621 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); |
| 566 } | 622 } |
| 567 | 623 |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 780 const int extra_padding = | 836 const int extra_padding = |
| 781 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 837 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
| 782 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 838 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
| 783 const int start = lb.x() + extra_padding; | 839 const int start = lb.x() + extra_padding; |
| 784 favicon_bounds_.SetRect(start, lb.y(), 0, 0); | 840 favicon_bounds_.SetRect(start, lb.y(), 0, 0); |
| 785 if (showing_icon_) { | 841 if (showing_icon_) { |
| 786 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); | 842 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); |
| 787 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); | 843 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); |
| 788 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); | 844 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); |
| 789 } | 845 } |
| 846 if (throbber_) |
| 847 throbber_->SetBoundsRect(favicon_bounds_); |
| 790 | 848 |
| 791 showing_close_button_ = ShouldShowCloseBox(); | 849 showing_close_button_ = ShouldShowCloseBox(); |
| 792 if (showing_close_button_) { | 850 if (showing_close_button_) { |
| 793 // If the ratio of the close button size to tab width exceeds the maximum. | 851 // If the ratio of the close button size to tab width exceeds the maximum. |
| 794 // The close button should be as large as possible so that there is a larger | 852 // The close button should be as large as possible so that there is a larger |
| 795 // hit-target for touch events. So the close button bounds extends to the | 853 // hit-target for touch events. So the close button bounds extends to the |
| 796 // edges of the tab. However, the larger hit-target should be active only | 854 // edges of the tab. However, the larger hit-target should be active only |
| 797 // for mouse events, and the close-image should show up in the right place. | 855 // for mouse events, and the close-image should show up in the right place. |
| 798 // So a border is added to the button with necessary padding. The close | 856 // So a border is added to the button with necessary padding. The close |
| 799 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target | 857 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target |
| (...skipping 540 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1340 } | 1398 } |
| 1341 | 1399 |
| 1342 void Tab::PaintIcon(gfx::Canvas* canvas) { | 1400 void Tab::PaintIcon(gfx::Canvas* canvas) { |
| 1343 gfx::Rect bounds = favicon_bounds_; | 1401 gfx::Rect bounds = favicon_bounds_; |
| 1344 if (bounds.IsEmpty()) | 1402 if (bounds.IsEmpty()) |
| 1345 return; | 1403 return; |
| 1346 | 1404 |
| 1347 bounds.set_x(GetMirroredXForRect(bounds)); | 1405 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1348 | 1406 |
| 1349 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { | 1407 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { |
| 1350 // Paint network activity (aka throbber) animation frame. | 1408 // Throbber will do its own painting. |
| 1351 ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1352 if (data().network_state == TabRendererData::NETWORK_STATE_WAITING) { | |
| 1353 if (waiting_start_time_ == base::TimeTicks()) | |
| 1354 waiting_start_time_ = base::TimeTicks::Now(); | |
| 1355 | |
| 1356 waiting_state_.elapsed_time = | |
| 1357 base::TimeTicks::Now() - waiting_start_time_; | |
| 1358 gfx::PaintThrobberWaiting( | |
| 1359 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING), | |
| 1360 waiting_state_.elapsed_time); | |
| 1361 } else { | |
| 1362 if (loading_start_time_ == base::TimeTicks()) | |
| 1363 loading_start_time_ = base::TimeTicks::Now(); | |
| 1364 | |
| 1365 waiting_state_.color = | |
| 1366 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING); | |
| 1367 gfx::PaintThrobberSpinningAfterWaiting( | |
| 1368 canvas, bounds, | |
| 1369 tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING), | |
| 1370 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); | |
| 1371 } | |
| 1372 } else if (should_display_crashed_favicon_) { | 1409 } else if (should_display_crashed_favicon_) { |
| 1373 // Paint crash favicon. | 1410 // Paint crash favicon. |
| 1374 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 1411 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 1375 gfx::ImageSkia crashed_favicon(*rb.GetImageSkiaNamed(IDR_SAD_FAVICON)); | 1412 gfx::ImageSkia crashed_favicon(*rb.GetImageSkiaNamed(IDR_SAD_FAVICON)); |
| 1376 bounds.set_y(bounds.y() + favicon_hiding_offset_); | 1413 bounds.set_y(bounds.y() + favicon_hiding_offset_); |
| 1377 DrawIconCenter(canvas, crashed_favicon, 0, | 1414 DrawIconCenter(canvas, crashed_favicon, 0, |
| 1378 crashed_favicon.width(), | 1415 crashed_favicon.width(), |
| 1379 crashed_favicon.height(), | 1416 crashed_favicon.height(), |
| 1380 bounds, true, SkPaint()); | 1417 bounds, true, SkPaint()); |
| 1381 } else if (!data().favicon.isNull()) { | 1418 } else if (!data().favicon.isNull()) { |
| 1382 // Paint the normal favicon. | 1419 // Paint the normal favicon. |
| 1383 DrawIconCenter(canvas, data().favicon, 0, | 1420 DrawIconCenter(canvas, data().favicon, 0, |
| 1384 data().favicon.width(), | 1421 data().favicon.width(), |
| 1385 data().favicon.height(), | 1422 data().favicon.height(), |
| 1386 bounds, true, SkPaint()); | 1423 bounds, true, SkPaint()); |
| 1387 } | 1424 } |
| 1388 } | 1425 } |
| 1389 | 1426 |
| 1390 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, | 1427 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState state) { |
| 1391 TabRendererData::NetworkState state) { | |
| 1392 if (state == TabRendererData::NETWORK_STATE_WAITING) { | 1428 if (state == TabRendererData::NETWORK_STATE_WAITING) { |
| 1393 // Waiting steps backwards. | 1429 // Waiting steps backwards. |
| 1394 immersive_loading_step_ = | 1430 immersive_loading_step_ = |
| 1395 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % | 1431 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % |
| 1396 kImmersiveLoadingStepCount; | 1432 kImmersiveLoadingStepCount; |
| 1397 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { | 1433 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { |
| 1398 immersive_loading_step_ = (immersive_loading_step_ + 1) % | 1434 immersive_loading_step_ = (immersive_loading_step_ + 1) % |
| 1399 kImmersiveLoadingStepCount; | 1435 kImmersiveLoadingStepCount; |
| 1400 } else { | 1436 } else { |
| 1401 waiting_start_time_ = base::TimeTicks(); | |
| 1402 loading_start_time_ = base::TimeTicks(); | |
| 1403 waiting_state_ = gfx::ThrobberWaitingState(); | |
| 1404 immersive_loading_step_ = 0; | 1437 immersive_loading_step_ = 0; |
| 1405 } | 1438 } |
| 1439 |
| 1406 if (controller_->IsImmersiveStyle()) { | 1440 if (controller_->IsImmersiveStyle()) { |
| 1407 SchedulePaintInRect(GetImmersiveBarRect()); | 1441 SchedulePaintInRect(GetImmersiveBarRect()); |
| 1408 } else { | 1442 return; |
| 1443 } |
| 1444 |
| 1445 const bool needs_throbber = state != TabRendererData::NETWORK_STATE_NONE; |
| 1446 if (needs_throbber && !throbber_) { |
| 1447 throbber_ = new ThrobberView(this, favicon_bounds_); |
| 1448 ScheduleIconPaint(); // Repaint the icon area to not show the icon. |
| 1449 } else if (!needs_throbber) { |
| 1450 delete throbber_; |
| 1451 throbber_ = nullptr; |
| 1409 ScheduleIconPaint(); | 1452 ScheduleIconPaint(); |
| 1410 } | 1453 } |
| 1454 if (throbber_) |
| 1455 throbber_->SchedulePaint(); |
| 1411 } | 1456 } |
| 1412 | 1457 |
| 1413 int Tab::IconCapacity() const { | 1458 int Tab::IconCapacity() const { |
| 1414 const gfx::Size min_size(GetMinimumUnselectedSize()); | 1459 const gfx::Size min_size(GetMinimumUnselectedSize()); |
| 1415 if (height() < min_size.height()) | 1460 if (height() < min_size.height()) |
| 1416 return 0; | 1461 return 0; |
| 1417 const int available_width = std::max(0, width() - min_size.width()); | 1462 const int available_width = std::max(0, width() - min_size.width()); |
| 1418 // All icons are the same size as the favicon. | 1463 // All icons are the same size as the favicon. |
| 1419 const int icon_width = gfx::kFaviconSize; | 1464 const int icon_width = gfx::kFaviconSize; |
| 1420 // We need enough space to display the icons flush against each other. | 1465 // 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... |
| 1679 const gfx::ImageSkia& image) { | 1724 const gfx::ImageSkia& image) { |
| 1680 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); | 1725 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); |
| 1681 ImageCacheEntry entry; | 1726 ImageCacheEntry entry; |
| 1682 entry.resource_id = resource_id; | 1727 entry.resource_id = resource_id; |
| 1683 entry.scale_factor = scale_factor; | 1728 entry.scale_factor = scale_factor; |
| 1684 entry.image = image; | 1729 entry.image = image; |
| 1685 image_cache_->push_front(entry); | 1730 image_cache_->push_front(entry); |
| 1686 if (image_cache_->size() > kMaxImageCacheSize) | 1731 if (image_cache_->size() > kMaxImageCacheSize) |
| 1687 image_cache_->pop_back(); | 1732 image_cache_->pop_back(); |
| 1688 } | 1733 } |
| OLD | NEW |