Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: chrome/browser/ui/views/tabs/tab.cc

Issue 1393193002: Paint tab-loading throbbers into a ui::Layer. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: overrides -> private Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/ui/views/tabs/tab.h ('k') | chrome/browser/ui/views/tabs/tab_strip.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 364 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 375
376 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); 376 return MaskedTargeterDelegate::DoesIntersectRect(target, rect);
377 } 377 }
378 378
379 Tab* tab_; 379 Tab* tab_;
380 380
381 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); 381 DISALLOW_COPY_AND_ASSIGN(TabCloseButton);
382 }; 382 };
383 383
384 //////////////////////////////////////////////////////////////////////////////// 384 ////////////////////////////////////////////////////////////////////////////////
385 // ThrobberView
386 //
387 // A Layer-backed view for updating a waiting or loading tab throbber.
388 class Tab::ThrobberView : public views::View {
389 public:
390 explicit ThrobberView(Tab* owner);
391
392 // Resets the times tracking when the throbber changes state.
393 void ResetStartTimes();
394
395 private:
396 // views::View:
397 bool CanProcessEventsWithinSubtree() const override;
398 void OnPaint(gfx::Canvas* canvas) override;
399
400 Tab* owner_; // Weak. Owns |this|.
401
402 // The point in time when the tab icon was first painted in the waiting state.
403 base::TimeTicks waiting_start_time_;
404
405 // The point in time when the tab icon was first painted in the loading state.
406 base::TimeTicks loading_start_time_;
407
408 // Paint state for the throbber after the most recent waiting paint.
409 gfx::ThrobberWaitingState waiting_state_;
410
411 DISALLOW_COPY_AND_ASSIGN(ThrobberView);
412 };
413
414 Tab::ThrobberView::ThrobberView(Tab* owner) : owner_(owner) {
415 // Since the throbber can animate for a long time, paint to a separate layer
416 // to reduce repaint overhead.
417 SetPaintToLayer(true);
418 SetFillsBoundsOpaquely(false);
419 }
420
421 void Tab::ThrobberView::ResetStartTimes() {
422 waiting_start_time_ = base::TimeTicks();
423 loading_start_time_ = base::TimeTicks();
424 waiting_state_ = gfx::ThrobberWaitingState();
425 }
426
427 bool Tab::ThrobberView::CanProcessEventsWithinSubtree() const {
428 return false;
429 }
430
431 void Tab::ThrobberView::OnPaint(gfx::Canvas* canvas) {
432 const TabRendererData::NetworkState state = owner_->data().network_state;
433 if (state == TabRendererData::NETWORK_STATE_NONE)
434 return;
435
436 ui::ThemeProvider* tp = GetThemeProvider();
437 const gfx::Rect bounds = GetLocalBounds();
438 if (state == TabRendererData::NETWORK_STATE_WAITING) {
439 if (waiting_start_time_ == base::TimeTicks())
440 waiting_start_time_ = base::TimeTicks::Now();
441
442 waiting_state_.elapsed_time = base::TimeTicks::Now() - waiting_start_time_;
443 gfx::PaintThrobberWaiting(
444 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING),
445 waiting_state_.elapsed_time);
446 } else {
447 if (loading_start_time_ == base::TimeTicks())
448 loading_start_time_ = base::TimeTicks::Now();
449
450 waiting_state_.color =
451 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING);
452 gfx::PaintThrobberSpinningAfterWaiting(
453 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING),
454 base::TimeTicks::Now() - loading_start_time_, &waiting_state_);
455 }
456 }
457
458 ////////////////////////////////////////////////////////////////////////////////
385 // ImageCacheEntry 459 // ImageCacheEntry
386 460
387 Tab::ImageCacheEntry::ImageCacheEntry() 461 Tab::ImageCacheEntry::ImageCacheEntry()
388 : resource_id(-1), 462 : resource_id(-1),
389 scale_factor(ui::SCALE_FACTOR_NONE) { 463 scale_factor(ui::SCALE_FACTOR_NONE) {
390 } 464 }
391 465
392 Tab::ImageCacheEntry::~ImageCacheEntry() {} 466 Tab::ImageCacheEntry::~ImageCacheEntry() {}
393 467
394 //////////////////////////////////////////////////////////////////////////////// 468 ////////////////////////////////////////////////////////////////////////////////
(...skipping 10 matching lines...) Expand all
405 // Tab, public: 479 // Tab, public:
406 480
407 Tab::Tab(TabController* controller) 481 Tab::Tab(TabController* controller)
408 : controller_(controller), 482 : controller_(controller),
409 closing_(false), 483 closing_(false),
410 dragging_(false), 484 dragging_(false),
411 detached_(false), 485 detached_(false),
412 favicon_hiding_offset_(0), 486 favicon_hiding_offset_(0),
413 immersive_loading_step_(0), 487 immersive_loading_step_(0),
414 should_display_crashed_favicon_(false), 488 should_display_crashed_favicon_(false),
489 throbber_(nullptr),
415 media_indicator_button_(nullptr), 490 media_indicator_button_(nullptr),
416 close_button_(nullptr), 491 close_button_(nullptr),
417 title_(new views::Label()), 492 title_(new views::Label()),
418 tab_activated_with_last_tap_down_(false), 493 tab_activated_with_last_tap_down_(false),
419 hover_controller_(this), 494 hover_controller_(this),
420 showing_icon_(false), 495 showing_icon_(false),
421 showing_media_indicator_(false), 496 showing_media_indicator_(false),
422 showing_close_button_(false), 497 showing_close_button_(false),
423 button_color_(SK_ColorTRANSPARENT) { 498 button_color_(SK_ColorTRANSPARENT) {
424 DCHECK(controller); 499 DCHECK(controller);
425 InitTabResources(); 500 InitTabResources();
426 501
427 // So we get don't get enter/exit on children and don't prematurely stop the 502 // So we get don't get enter/exit on children and don't prematurely stop the
428 // hover. 503 // hover.
429 set_notify_enter_exit_on_child(true); 504 set_notify_enter_exit_on_child(true);
430 505
431 set_id(VIEW_ID_TAB); 506 set_id(VIEW_ID_TAB);
432 507
433 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); 508 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
434 title_->SetElideBehavior(gfx::FADE_TAIL); 509 title_->SetElideBehavior(gfx::FADE_TAIL);
435 title_->SetHandlesTooltips(false); 510 title_->SetHandlesTooltips(false);
436 title_->SetAutoColorReadabilityEnabled(false); 511 title_->SetAutoColorReadabilityEnabled(false);
437 title_->SetText(CoreTabHelper::GetDefaultTitle()); 512 title_->SetText(CoreTabHelper::GetDefaultTitle());
438 AddChildView(title_); 513 AddChildView(title_);
439 514
440 SetEventTargeter( 515 SetEventTargeter(
441 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); 516 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
442 517
518 throbber_ = new ThrobberView(this);
519 AddChildView(throbber_);
520
443 media_indicator_button_ = new MediaIndicatorButton(this); 521 media_indicator_button_ = new MediaIndicatorButton(this);
444 AddChildView(media_indicator_button_); 522 AddChildView(media_indicator_button_);
445 523
446 close_button_ = new TabCloseButton(this); 524 close_button_ = new TabCloseButton(this);
447 close_button_->SetAccessibleName( 525 close_button_->SetAccessibleName(
448 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); 526 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
449 // The normal image is set by OnButtonColorMaybeChanged() because it depends 527 // The normal image is set by OnButtonColorMaybeChanged() because it depends
450 // on the current theme and active state. The hovered and pressed images 528 // on the current theme and active state. The hovered and pressed images
451 // don't depend on the these, so we can set them here. 529 // don't depend on the these, so we can set them here.
452 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon( 530 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon(
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 return controller_->IsTabSelected(this); 565 return controller_->IsTabSelected(this);
488 } 566 }
489 567
490 void Tab::SetData(const TabRendererData& data) { 568 void Tab::SetData(const TabRendererData& data) {
491 DCHECK(GetWidget()); 569 DCHECK(GetWidget());
492 570
493 if (data_.Equals(data)) 571 if (data_.Equals(data))
494 return; 572 return;
495 573
496 TabRendererData old(data_); 574 TabRendererData old(data_);
575 UpdateLoadingAnimation(data.network_state);
497 data_ = data; 576 data_ = data;
498 577
499 base::string16 title = data_.title; 578 base::string16 title = data_.title;
500 if (title.empty()) { 579 if (title.empty()) {
501 title = data_.loading ? 580 title = data_.loading ?
502 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : 581 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) :
503 CoreTabHelper::GetDefaultTitle(); 582 CoreTabHelper::GetDefaultTitle();
504 } else { 583 } else {
505 Browser::FormatTitleForDisplay(&title); 584 Browser::FormatTitleForDisplay(&title);
506 } 585 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
545 } 624 }
546 625
547 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { 626 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) {
548 if (state == data_.network_state && 627 if (state == data_.network_state &&
549 state == TabRendererData::NETWORK_STATE_NONE) { 628 state == TabRendererData::NETWORK_STATE_NONE) {
550 // If the network state is none and hasn't changed, do nothing. Otherwise we 629 // If the network state is none and hasn't changed, do nothing. Otherwise we
551 // need to advance the animation frame. 630 // need to advance the animation frame.
552 return; 631 return;
553 } 632 }
554 633
555 TabRendererData::NetworkState old_state = data_.network_state;
556 data_.network_state = state; 634 data_.network_state = state;
557 AdvanceLoadingAnimation(old_state, state); 635 AdvanceLoadingAnimation(state);
558 } 636 }
559 637
560 void Tab::StartPulse() { 638 void Tab::StartPulse() {
561 pulse_animation_.reset(new gfx::ThrobAnimation(this)); 639 pulse_animation_.reset(new gfx::ThrobAnimation(this));
562 pulse_animation_->SetSlideDuration(kPulseDurationMs); 640 pulse_animation_->SetSlideDuration(kPulseDurationMs);
563 if (animation_container_.get()) 641 if (animation_container_.get())
564 pulse_animation_->SetContainer(animation_container_.get()); 642 pulse_animation_->SetContainer(animation_container_.get());
565 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); 643 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max());
566 } 644 }
567 645
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
780 const int extra_padding = 858 const int extra_padding =
781 (controller_->ShouldHideCloseButtonForInactiveTabs() || 859 (controller_->ShouldHideCloseButtonForInactiveTabs() ||
782 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; 860 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding;
783 const int start = lb.x() + extra_padding; 861 const int start = lb.x() + extra_padding;
784 favicon_bounds_.SetRect(start, lb.y(), 0, 0); 862 favicon_bounds_.SetRect(start, lb.y(), 0, 0);
785 if (showing_icon_) { 863 if (showing_icon_) {
786 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); 864 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize));
787 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); 865 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2);
788 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); 866 MaybeAdjustLeftForPinnedTab(&favicon_bounds_);
789 } 867 }
868 throbber_->SetBoundsRect(favicon_bounds_);
790 869
791 showing_close_button_ = ShouldShowCloseBox(); 870 showing_close_button_ = ShouldShowCloseBox();
792 if (showing_close_button_) { 871 if (showing_close_button_) {
793 // If the ratio of the close button size to tab width exceeds the maximum. 872 // 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 873 // 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 874 // 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 875 // 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. 876 // 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 877 // 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 878 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target
(...skipping 540 matching lines...) Expand 10 before | Expand all | Expand 10 after
1340 } 1419 }
1341 1420
1342 void Tab::PaintIcon(gfx::Canvas* canvas) { 1421 void Tab::PaintIcon(gfx::Canvas* canvas) {
1343 gfx::Rect bounds = favicon_bounds_; 1422 gfx::Rect bounds = favicon_bounds_;
1344 if (bounds.IsEmpty()) 1423 if (bounds.IsEmpty())
1345 return; 1424 return;
1346 1425
1347 bounds.set_x(GetMirroredXForRect(bounds)); 1426 bounds.set_x(GetMirroredXForRect(bounds));
1348 1427
1349 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { 1428 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) {
1350 // Paint network activity (aka throbber) animation frame. 1429 // 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_) { 1430 } else if (should_display_crashed_favicon_) {
1373 // Paint crash favicon. 1431 // Paint crash favicon.
1374 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 1432 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1375 gfx::ImageSkia crashed_favicon(*rb.GetImageSkiaNamed(IDR_SAD_FAVICON)); 1433 gfx::ImageSkia crashed_favicon(*rb.GetImageSkiaNamed(IDR_SAD_FAVICON));
1376 bounds.set_y(bounds.y() + favicon_hiding_offset_); 1434 bounds.set_y(bounds.y() + favicon_hiding_offset_);
1377 DrawIconCenter(canvas, crashed_favicon, 0, 1435 DrawIconCenter(canvas, crashed_favicon, 0,
1378 crashed_favicon.width(), 1436 crashed_favicon.width(),
1379 crashed_favicon.height(), 1437 crashed_favicon.height(),
1380 bounds, true, SkPaint()); 1438 bounds, true, SkPaint());
1381 } else if (!data().favicon.isNull()) { 1439 } else if (!data().favicon.isNull()) {
1382 // Paint the normal favicon. 1440 // Paint the normal favicon.
1383 DrawIconCenter(canvas, data().favicon, 0, 1441 DrawIconCenter(canvas, data().favicon, 0,
1384 data().favicon.width(), 1442 data().favicon.width(),
1385 data().favicon.height(), 1443 data().favicon.height(),
1386 bounds, true, SkPaint()); 1444 bounds, true, SkPaint());
1387 } 1445 }
1388 } 1446 }
1389 1447
1390 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, 1448 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState state) {
1391 TabRendererData::NetworkState state) { 1449 if (controller_->IsImmersiveStyle()) {
1392 if (state == TabRendererData::NETWORK_STATE_WAITING) { 1450 if (state == TabRendererData::NETWORK_STATE_WAITING) {
1393 // Waiting steps backwards. 1451 // Waiting steps backwards.
1394 immersive_loading_step_ = 1452 immersive_loading_step_ =
1395 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % 1453 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) %
1396 kImmersiveLoadingStepCount; 1454 kImmersiveLoadingStepCount;
1397 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { 1455 } else if (state == TabRendererData::NETWORK_STATE_LOADING) {
1398 immersive_loading_step_ = (immersive_loading_step_ + 1) % 1456 immersive_loading_step_ =
1399 kImmersiveLoadingStepCount; 1457 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount;
1400 } else { 1458 } else {
1401 waiting_start_time_ = base::TimeTicks(); 1459 immersive_loading_step_ = 0;
1402 loading_start_time_ = base::TimeTicks(); 1460 }
1403 waiting_state_ = gfx::ThrobberWaitingState(); 1461
1404 immersive_loading_step_ = 0; 1462 SchedulePaintInRect(GetImmersiveBarRect());
1463 return;
1405 } 1464 }
1406 if (controller_->IsImmersiveStyle()) { 1465
1407 SchedulePaintInRect(GetImmersiveBarRect()); 1466 // To ensure the throbber layer does not paint over stacked or dragged tabs,
1408 } else { 1467 // hide it when the favicon would be clipped out. Check for any clipping on
1409 ScheduleIconPaint(); 1468 // the left since the shoulder of a dragged tab can still paint over the icon.
1469 gfx::Rect clip = GetLocalBounds();
1470 const bool needs_throbber = state != TabRendererData::NETWORK_STATE_NONE &&
1471 controller_->ShouldPaintTab(this, &clip) &&
1472 clip.x() == 0 && clip.Contains(favicon_bounds_);
sky 2015/10/09 15:16:38 I don't think this is enough. I can still easily g
tapted 2015/10/11 10:36:38 Thanks for trying it! I added handling for two add
1473
1474 if (needs_throbber != throbber_->visible()) {
1475 throbber_->SetVisible(needs_throbber);
1476 ScheduleIconPaint(); // Repaint the icon area to update favicon visibility.
1410 } 1477 }
1478 if (state == TabRendererData::NETWORK_STATE_NONE)
1479 throbber_->ResetStartTimes();
1480 throbber_->SchedulePaint();
1411 } 1481 }
1412 1482
1413 int Tab::IconCapacity() const { 1483 int Tab::IconCapacity() const {
1414 const gfx::Size min_size(GetMinimumUnselectedSize()); 1484 const gfx::Size min_size(GetMinimumUnselectedSize());
1415 if (height() < min_size.height()) 1485 if (height() < min_size.height())
1416 return 0; 1486 return 0;
1417 const int available_width = std::max(0, width() - min_size.width()); 1487 const int available_width = std::max(0, width() - min_size.width());
1418 // All icons are the same size as the favicon. 1488 // All icons are the same size as the favicon.
1419 const int icon_width = gfx::kFaviconSize; 1489 const int icon_width = gfx::kFaviconSize;
1420 // We need enough space to display the icons flush against each other. 1490 // We need enough space to display the icons flush against each other.
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
1679 const gfx::ImageSkia& image) { 1749 const gfx::ImageSkia& image) {
1680 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); 1750 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE);
1681 ImageCacheEntry entry; 1751 ImageCacheEntry entry;
1682 entry.resource_id = resource_id; 1752 entry.resource_id = resource_id;
1683 entry.scale_factor = scale_factor; 1753 entry.scale_factor = scale_factor;
1684 entry.image = image; 1754 entry.image = image;
1685 image_cache_->push_front(entry); 1755 image_cache_->push_front(entry);
1686 if (image_cache_->size() > kMaxImageCacheSize) 1756 if (image_cache_->size() > kMaxImageCacheSize)
1687 image_cache_->pop_back(); 1757 image_cache_->pop_back();
1688 } 1758 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/tabs/tab.h ('k') | chrome/browser/ui/views/tabs/tab_strip.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698