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

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: Fix clipping on the right, and animations 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 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 335
336 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); 336 return MaskedTargeterDelegate::DoesIntersectRect(target, rect);
337 } 337 }
338 338
339 Tab* tab_; 339 Tab* tab_;
340 340
341 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); 341 DISALLOW_COPY_AND_ASSIGN(TabCloseButton);
342 }; 342 };
343 343
344 //////////////////////////////////////////////////////////////////////////////// 344 ////////////////////////////////////////////////////////////////////////////////
345 // ThrobberView
346 //
347 // A Layer-backed view for updating a waiting or loading tab throbber.
348 class Tab::ThrobberView : public views::View {
349 public:
350 explicit ThrobberView(Tab* owner);
351
352 // Resets the times tracking when the throbber changes state.
353 void ResetStartTimes();
354
355 private:
356 // views::View:
357 bool CanProcessEventsWithinSubtree() const override;
358 void OnPaint(gfx::Canvas* canvas) override;
359
360 Tab* owner_; // Weak. Owns |this|.
361
362 // The point in time when the tab icon was first painted in the waiting state.
363 base::TimeTicks waiting_start_time_;
364
365 // The point in time when the tab icon was first painted in the loading state.
366 base::TimeTicks loading_start_time_;
367
368 // Paint state for the throbber after the most recent waiting paint.
369 gfx::ThrobberWaitingState waiting_state_;
370
371 DISALLOW_COPY_AND_ASSIGN(ThrobberView);
372 };
373
374 Tab::ThrobberView::ThrobberView(Tab* owner) : owner_(owner) {
375 // Since the throbber can animate for a long time, paint to a separate layer
376 // to reduce repaint overhead.
377 SetPaintToLayer(true);
378 SetFillsBoundsOpaquely(false);
379 }
380
381 void Tab::ThrobberView::ResetStartTimes() {
382 waiting_start_time_ = base::TimeTicks();
383 loading_start_time_ = base::TimeTicks();
384 waiting_state_ = gfx::ThrobberWaitingState();
385 }
386
387 bool Tab::ThrobberView::CanProcessEventsWithinSubtree() const {
388 return false;
389 }
390
391 void Tab::ThrobberView::OnPaint(gfx::Canvas* canvas) {
392 const TabRendererData::NetworkState state = owner_->data().network_state;
393 if (state == TabRendererData::NETWORK_STATE_NONE)
394 return;
395
396 ui::ThemeProvider* tp = GetThemeProvider();
397 const gfx::Rect bounds = GetLocalBounds();
398 if (state == TabRendererData::NETWORK_STATE_WAITING) {
399 if (waiting_start_time_ == base::TimeTicks())
400 waiting_start_time_ = base::TimeTicks::Now();
401
402 waiting_state_.elapsed_time = base::TimeTicks::Now() - waiting_start_time_;
403 gfx::PaintThrobberWaiting(
404 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING),
405 waiting_state_.elapsed_time);
406 } else {
407 if (loading_start_time_ == base::TimeTicks())
408 loading_start_time_ = base::TimeTicks::Now();
409
410 waiting_state_.color =
411 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING);
412 gfx::PaintThrobberSpinningAfterWaiting(
413 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING),
414 base::TimeTicks::Now() - loading_start_time_, &waiting_state_);
415 }
416 }
417
418 ////////////////////////////////////////////////////////////////////////////////
345 // ImageCacheEntry 419 // ImageCacheEntry
346 420
347 Tab::ImageCacheEntry::ImageCacheEntry() 421 Tab::ImageCacheEntry::ImageCacheEntry()
348 : resource_id(-1), 422 : resource_id(-1),
349 scale_factor(ui::SCALE_FACTOR_NONE) { 423 scale_factor(ui::SCALE_FACTOR_NONE) {
350 } 424 }
351 425
352 Tab::ImageCacheEntry::~ImageCacheEntry() {} 426 Tab::ImageCacheEntry::~ImageCacheEntry() {}
353 427
354 //////////////////////////////////////////////////////////////////////////////// 428 ////////////////////////////////////////////////////////////////////////////////
(...skipping 10 matching lines...) Expand all
365 // Tab, public: 439 // Tab, public:
366 440
367 Tab::Tab(TabController* controller) 441 Tab::Tab(TabController* controller)
368 : controller_(controller), 442 : controller_(controller),
369 closing_(false), 443 closing_(false),
370 dragging_(false), 444 dragging_(false),
371 detached_(false), 445 detached_(false),
372 favicon_hiding_offset_(0), 446 favicon_hiding_offset_(0),
373 immersive_loading_step_(0), 447 immersive_loading_step_(0),
374 should_display_crashed_favicon_(false), 448 should_display_crashed_favicon_(false),
449 throbber_(nullptr),
375 media_indicator_button_(nullptr), 450 media_indicator_button_(nullptr),
376 close_button_(nullptr), 451 close_button_(nullptr),
377 title_(new views::Label()), 452 title_(new views::Label()),
378 tab_activated_with_last_tap_down_(false), 453 tab_activated_with_last_tap_down_(false),
379 hover_controller_(this), 454 hover_controller_(this),
380 showing_icon_(false), 455 showing_icon_(false),
381 showing_media_indicator_(false), 456 showing_media_indicator_(false),
382 showing_close_button_(false), 457 showing_close_button_(false),
383 button_color_(SK_ColorTRANSPARENT) { 458 button_color_(SK_ColorTRANSPARENT) {
384 DCHECK(controller); 459 DCHECK(controller);
385 InitTabResources(); 460 InitTabResources();
386 461
387 // So we get don't get enter/exit on children and don't prematurely stop the 462 // So we get don't get enter/exit on children and don't prematurely stop the
388 // hover. 463 // hover.
389 set_notify_enter_exit_on_child(true); 464 set_notify_enter_exit_on_child(true);
390 465
391 set_id(VIEW_ID_TAB); 466 set_id(VIEW_ID_TAB);
392 467
393 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); 468 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
394 title_->SetElideBehavior(gfx::FADE_TAIL); 469 title_->SetElideBehavior(gfx::FADE_TAIL);
395 title_->SetHandlesTooltips(false); 470 title_->SetHandlesTooltips(false);
396 title_->SetAutoColorReadabilityEnabled(false); 471 title_->SetAutoColorReadabilityEnabled(false);
397 title_->SetText(CoreTabHelper::GetDefaultTitle()); 472 title_->SetText(CoreTabHelper::GetDefaultTitle());
398 AddChildView(title_); 473 AddChildView(title_);
399 474
400 SetEventTargeter( 475 SetEventTargeter(
401 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); 476 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
402 477
478 throbber_ = new ThrobberView(this);
479 AddChildView(throbber_);
480
403 media_indicator_button_ = new MediaIndicatorButton(this); 481 media_indicator_button_ = new MediaIndicatorButton(this);
404 AddChildView(media_indicator_button_); 482 AddChildView(media_indicator_button_);
405 483
406 close_button_ = new TabCloseButton(this); 484 close_button_ = new TabCloseButton(this);
407 close_button_->SetAccessibleName( 485 close_button_->SetAccessibleName(
408 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); 486 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
409 // The normal image is set by OnButtonColorMaybeChanged() because it depends 487 // The normal image is set by OnButtonColorMaybeChanged() because it depends
410 // on the current theme and active state. The hovered and pressed images 488 // on the current theme and active state. The hovered and pressed images
411 // don't depend on the these, so we can set them here. 489 // don't depend on the these, so we can set them here.
412 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon( 490 const gfx::ImageSkia& hovered = gfx::CreateVectorIcon(
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 return controller_->IsTabSelected(this); 525 return controller_->IsTabSelected(this);
448 } 526 }
449 527
450 void Tab::SetData(const TabRendererData& data) { 528 void Tab::SetData(const TabRendererData& data) {
451 DCHECK(GetWidget()); 529 DCHECK(GetWidget());
452 530
453 if (data_.Equals(data)) 531 if (data_.Equals(data))
454 return; 532 return;
455 533
456 TabRendererData old(data_); 534 TabRendererData old(data_);
535 UpdateLoadingAnimation(data.network_state);
457 data_ = data; 536 data_ = data;
458 537
459 base::string16 title = data_.title; 538 base::string16 title = data_.title;
460 if (title.empty()) { 539 if (title.empty()) {
461 title = data_.loading ? 540 title = data_.loading ?
462 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : 541 l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) :
463 CoreTabHelper::GetDefaultTitle(); 542 CoreTabHelper::GetDefaultTitle();
464 } else { 543 } else {
465 Browser::FormatTitleForDisplay(&title); 544 Browser::FormatTitleForDisplay(&title);
466 } 545 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 } 585 }
507 586
508 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { 587 void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) {
509 if (state == data_.network_state && 588 if (state == data_.network_state &&
510 state == TabRendererData::NETWORK_STATE_NONE) { 589 state == TabRendererData::NETWORK_STATE_NONE) {
511 // If the network state is none and hasn't changed, do nothing. Otherwise we 590 // If the network state is none and hasn't changed, do nothing. Otherwise we
512 // need to advance the animation frame. 591 // need to advance the animation frame.
513 return; 592 return;
514 } 593 }
515 594
516 TabRendererData::NetworkState old_state = data_.network_state;
517 data_.network_state = state; 595 data_.network_state = state;
518 AdvanceLoadingAnimation(old_state, state); 596 AdvanceLoadingAnimation();
519 } 597 }
520 598
521 void Tab::StartPulse() { 599 void Tab::StartPulse() {
522 pulse_animation_.reset(new gfx::ThrobAnimation(this)); 600 pulse_animation_.reset(new gfx::ThrobAnimation(this));
523 pulse_animation_->SetSlideDuration(kPulseDurationMs); 601 pulse_animation_->SetSlideDuration(kPulseDurationMs);
524 if (animation_container_.get()) 602 if (animation_container_.get())
525 pulse_animation_->SetContainer(animation_container_.get()); 603 pulse_animation_->SetContainer(animation_container_.get());
526 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); 604 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max());
527 } 605 }
528 606
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 const int extra_padding = 866 const int extra_padding =
789 (controller_->ShouldHideCloseButtonForInactiveTabs() || 867 (controller_->ShouldHideCloseButtonForInactiveTabs() ||
790 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; 868 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding;
791 const int start = lb.x() + extra_padding; 869 const int start = lb.x() + extra_padding;
792 favicon_bounds_.SetRect(start, lb.y(), 0, 0); 870 favicon_bounds_.SetRect(start, lb.y(), 0, 0);
793 if (showing_icon_) { 871 if (showing_icon_) {
794 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); 872 favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize));
795 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2); 873 favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2);
796 MaybeAdjustLeftForPinnedTab(&favicon_bounds_); 874 MaybeAdjustLeftForPinnedTab(&favicon_bounds_);
797 } 875 }
876 throbber_->SetBoundsRect(favicon_bounds_);
798 877
799 showing_close_button_ = ShouldShowCloseBox(); 878 showing_close_button_ = ShouldShowCloseBox();
800 if (showing_close_button_) { 879 if (showing_close_button_) {
801 // If the ratio of the close button size to tab width exceeds the maximum. 880 // If the ratio of the close button size to tab width exceeds the maximum.
802 // The close button should be as large as possible so that there is a larger 881 // The close button should be as large as possible so that there is a larger
803 // hit-target for touch events. So the close button bounds extends to the 882 // hit-target for touch events. So the close button bounds extends to the
804 // edges of the tab. However, the larger hit-target should be active only 883 // edges of the tab. However, the larger hit-target should be active only
805 // for mouse events, and the close-image should show up in the right place. 884 // for mouse events, and the close-image should show up in the right place.
806 // So a border is added to the button with necessary padding. The close 885 // So a border is added to the button with necessary padding. The close
807 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target 886 // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after
1344 1423
1345 void Tab::PaintIcon(gfx::Canvas* canvas) { 1424 void Tab::PaintIcon(gfx::Canvas* canvas) {
1346 gfx::Rect bounds = favicon_bounds_; 1425 gfx::Rect bounds = favicon_bounds_;
1347 bounds.set_x(GetMirroredXForRect(bounds)); 1426 bounds.set_x(GetMirroredXForRect(bounds));
1348 bounds.Offset(0, favicon_hiding_offset_); 1427 bounds.Offset(0, favicon_hiding_offset_);
1349 bounds.Intersect(GetInteriorBounds()); 1428 bounds.Intersect(GetInteriorBounds());
1350 if (bounds.IsEmpty()) 1429 if (bounds.IsEmpty())
1351 return; 1430 return;
1352 1431
1353 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { 1432 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) {
1354 // Paint network activity (aka throbber) animation frame. 1433 // Throbber will do its own painting.
1355 ui::ThemeProvider* tp = GetThemeProvider();
1356 if (data().network_state == TabRendererData::NETWORK_STATE_WAITING) {
1357 if (waiting_start_time_ == base::TimeTicks())
1358 waiting_start_time_ = base::TimeTicks::Now();
1359
1360 waiting_state_.elapsed_time =
1361 base::TimeTicks::Now() - waiting_start_time_;
1362 gfx::PaintThrobberWaiting(
1363 canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING),
1364 waiting_state_.elapsed_time);
1365 } else {
1366 if (loading_start_time_ == base::TimeTicks())
1367 loading_start_time_ = base::TimeTicks::Now();
1368
1369 waiting_state_.color =
1370 tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING);
1371 gfx::PaintThrobberSpinningAfterWaiting(
1372 canvas, bounds,
1373 tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING),
1374 base::TimeTicks::Now() - loading_start_time_, &waiting_state_);
1375 }
1376 } else { 1434 } else {
1377 const gfx::ImageSkia& favicon = should_display_crashed_favicon_ ? 1435 const gfx::ImageSkia& favicon = should_display_crashed_favicon_ ?
1378 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 1436 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
1379 IDR_CRASH_SAD_FAVICON) : 1437 IDR_CRASH_SAD_FAVICON) :
1380 data().favicon; 1438 data().favicon;
1381 if (!favicon.isNull()) { 1439 if (!favicon.isNull()) {
1382 canvas->DrawImageInt(favicon, 0, 0, bounds.width(), bounds.height(), 1440 canvas->DrawImageInt(favicon, 0, 0, bounds.width(), bounds.height(),
1383 bounds.x(), bounds.y(), bounds.width(), 1441 bounds.x(), bounds.y(), bounds.width(),
1384 bounds.height(), false); 1442 bounds.height(), false);
1385 } 1443 }
1386 } 1444 }
1387 } 1445 }
1388 1446
1389 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, 1447 void Tab::AdvanceLoadingAnimation() {
1390 TabRendererData::NetworkState state) { 1448 const TabRendererData::NetworkState state = data().network_state;
tapted 2015/10/11 10:36:38 I briefly had a `ShouldShowThrobber() const` funct
1391 if (state == TabRendererData::NETWORK_STATE_WAITING) { 1449 if (controller_->IsImmersiveStyle()) {
1392 // Waiting steps backwards. 1450 if (state == TabRendererData::NETWORK_STATE_WAITING) {
1393 immersive_loading_step_ = 1451 // Waiting steps backwards.
1394 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % 1452 immersive_loading_step_ =
1395 kImmersiveLoadingStepCount; 1453 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) %
1396 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { 1454 kImmersiveLoadingStepCount;
1397 immersive_loading_step_ = (immersive_loading_step_ + 1) % 1455 } else if (state == TabRendererData::NETWORK_STATE_LOADING) {
1398 kImmersiveLoadingStepCount; 1456 immersive_loading_step_ =
1399 } else { 1457 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount;
1400 waiting_start_time_ = base::TimeTicks(); 1458 } else {
1401 loading_start_time_ = base::TimeTicks(); 1459 immersive_loading_step_ = 0;
1402 waiting_state_ = gfx::ThrobberWaitingState(); 1460 }
1403 immersive_loading_step_ = 0; 1461
1462 SchedulePaintInRect(GetImmersiveBarRect());
1463 return;
1404 } 1464 }
1405 if (controller_->IsImmersiveStyle()) { 1465
1406 SchedulePaintInRect(GetImmersiveBarRect()); 1466 // To ensure the throbber layer does not paint over stacked or dragged tabs,
1407 } else { 1467 // hide it when the favicon would be clipped out. Check for any clipping on
1408 ScheduleIconPaint(); 1468 // the left since the shoulder of a dragged tab can still paint over the icon.
1469 // To do the same on the right, extend the favicon region by a width equal to
1470 // its padding on the left within the tab. Note that usually the tab will re-
1471 // order itself before clipping happens on the right. The exception is when a
1472 // tab is first dragged into a window (see comments in
1473 // TabDragController::GetInsertionIndexFrom()).
1474 const int min_width = favicon_bounds_.right() + favicon_bounds_.x();
1475 gfx::Rect clip = GetLocalBounds();
1476 const bool needs_throbber = state != TabRendererData::NETWORK_STATE_NONE &&
1477 controller_->ShouldPaintTab(this, &clip) &&
1478 clip.x() == 0 && clip.width() > min_width;
1479
1480 if (needs_throbber != throbber_->visible()) {
1481 throbber_->SetVisible(needs_throbber);
1482 ScheduleIconPaint(); // Repaint the icon area to update favicon visibility.
1409 } 1483 }
1484 if (state == TabRendererData::NETWORK_STATE_NONE)
1485 throbber_->ResetStartTimes();
1486 throbber_->SchedulePaint();
1410 } 1487 }
1411 1488
1412 int Tab::IconCapacity() const { 1489 int Tab::IconCapacity() const {
1413 const gfx::Size min_size(GetMinimumInactiveSize()); 1490 const gfx::Size min_size(GetMinimumInactiveSize());
1414 if (height() < min_size.height()) 1491 if (height() < min_size.height())
1415 return 0; 1492 return 0;
1416 const int available_width = std::max(0, width() - min_size.width()); 1493 const int available_width = std::max(0, width() - min_size.width());
1417 // All icons are the same size as the favicon. 1494 // All icons are the same size as the favicon.
1418 const int icon_width = gfx::kFaviconSize; 1495 const int icon_width = gfx::kFaviconSize;
1419 // We need enough space to display the icons flush against each other. 1496 // We need enough space to display the icons flush against each other.
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
1611 const gfx::ImageSkia& image) { 1688 const gfx::ImageSkia& image) {
1612 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); 1689 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE);
1613 ImageCacheEntry entry; 1690 ImageCacheEntry entry;
1614 entry.resource_id = resource_id; 1691 entry.resource_id = resource_id;
1615 entry.scale_factor = scale_factor; 1692 entry.scale_factor = scale_factor;
1616 entry.image = image; 1693 entry.image = image;
1617 image_cache_->push_front(entry); 1694 image_cache_->push_front(entry);
1618 if (image_cache_->size() > kMaxImageCacheSize) 1695 if (image_cache_->size() > kMaxImageCacheSize)
1619 image_cache_->pop_back(); 1696 image_cache_->pop_back();
1620 } 1697 }
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