Chromium Code Reviews| 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" |
| 11 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "chrome/browser/defaults.h" | 12 #include "chrome/browser/defaults.h" |
| 13 #include "chrome/browser/themes/theme_properties.h" | 13 #include "chrome/browser/themes/theme_properties.h" |
| 14 #include "chrome/browser/ui/browser.h" | 14 #include "chrome/browser/ui/browser.h" |
| 15 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 15 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" |
| 16 #include "chrome/browser/ui/tabs/tab_resources.h" | 16 #include "chrome/browser/ui/tabs/tab_resources.h" |
| 17 #include "chrome/browser/ui/tabs/tab_utils.h" | 17 #include "chrome/browser/ui/tabs/tab_utils.h" |
| 18 #include "chrome/browser/ui/view_ids.h" | 18 #include "chrome/browser/ui/view_ids.h" |
| 19 #include "chrome/browser/ui/views/tabs/tab_controller.h" | 19 #include "chrome/browser/ui/views/tabs/tab_controller.h" |
| 20 #include "chrome/browser/ui/views/theme_image_mapper.h" | 20 #include "chrome/browser/ui/views/theme_image_mapper.h" |
| 21 #include "chrome/browser/ui/views/touch_uma/touch_uma.h" | 21 #include "chrome/browser/ui/views/touch_uma/touch_uma.h" |
| 22 #include "chrome/common/chrome_switches.h" | 22 #include "chrome/common/chrome_switches.h" |
| 23 #include "chrome/grit/generated_resources.h" | 23 #include "chrome/grit/generated_resources.h" |
| 24 #include "content/public/browser/user_metrics.h" | |
| 24 #include "grit/theme_resources.h" | 25 #include "grit/theme_resources.h" |
| 25 #include "third_party/skia/include/effects/SkGradientShader.h" | 26 #include "third_party/skia/include/effects/SkGradientShader.h" |
| 26 #include "ui/accessibility/ax_view_state.h" | 27 #include "ui/accessibility/ax_view_state.h" |
| 27 #include "ui/aura/env.h" | 28 #include "ui/aura/env.h" |
| 28 #include "ui/base/l10n/l10n_util.h" | 29 #include "ui/base/l10n/l10n_util.h" |
| 29 #include "ui/base/models/list_selection_model.h" | 30 #include "ui/base/models/list_selection_model.h" |
| 30 #include "ui/base/resource/resource_bundle.h" | 31 #include "ui/base/resource/resource_bundle.h" |
| 31 #include "ui/base/theme_provider.h" | 32 #include "ui/base/theme_provider.h" |
| 32 #include "ui/gfx/animation/animation_container.h" | 33 #include "ui/gfx/animation/animation_container.h" |
| 33 #include "ui/gfx/animation/multi_animation.h" | 34 #include "ui/gfx/animation/multi_animation.h" |
| 34 #include "ui/gfx/animation/throb_animation.h" | 35 #include "ui/gfx/animation/throb_animation.h" |
| 35 #include "ui/gfx/canvas.h" | 36 #include "ui/gfx/canvas.h" |
| 36 #include "ui/gfx/color_analysis.h" | 37 #include "ui/gfx/color_analysis.h" |
| 37 #include "ui/gfx/favicon_size.h" | 38 #include "ui/gfx/favicon_size.h" |
| 38 #include "ui/gfx/image/image_skia_operations.h" | 39 #include "ui/gfx/image/image_skia_operations.h" |
| 39 #include "ui/gfx/path.h" | 40 #include "ui/gfx/path.h" |
| 40 #include "ui/gfx/rect_conversions.h" | 41 #include "ui/gfx/rect_conversions.h" |
| 41 #include "ui/gfx/skia_util.h" | 42 #include "ui/gfx/skia_util.h" |
| 42 #include "ui/resources/grit/ui_resources.h" | 43 #include "ui/resources/grit/ui_resources.h" |
| 43 #include "ui/views/border.h" | 44 #include "ui/views/border.h" |
| 44 #include "ui/views/controls/button/image_button.h" | 45 #include "ui/views/controls/button/image_button.h" |
| 45 #include "ui/views/controls/label.h" | 46 #include "ui/views/controls/label.h" |
| 46 #include "ui/views/rect_based_targeting_utils.h" | 47 #include "ui/views/rect_based_targeting_utils.h" |
| 47 #include "ui/views/view_targeter.h" | 48 #include "ui/views/view_targeter.h" |
| 48 #include "ui/views/widget/tooltip_manager.h" | 49 #include "ui/views/widget/tooltip_manager.h" |
| 49 #include "ui/views/widget/widget.h" | 50 #include "ui/views/widget/widget.h" |
| 50 #include "ui/views/window/non_client_view.h" | 51 #include "ui/views/window/non_client_view.h" |
| 51 | 52 |
| 53 using base::UserMetricsAction; | |
| 54 | |
| 52 namespace { | 55 namespace { |
| 53 | 56 |
| 54 // Padding around the "content" of a tab, occupied by the tab border graphics. | 57 // Padding around the "content" of a tab, occupied by the tab border graphics. |
| 55 const int kLeftPadding = 22; | 58 const int kLeftPadding = 22; |
| 56 const int kTopPadding = 4; | 59 const int kTopPadding = 4; |
| 57 const int kRightPadding = 17; | 60 const int kRightPadding = 17; |
| 58 const int kBottomPadding = 2; | 61 const int kBottomPadding = 2; |
| 59 | 62 |
| 60 // Height of the shadow at the top of the tab image assets. | 63 // Height of the shadow at the top of the tab image assets. |
| 61 const int kDropShadowHeight = 4; | 64 const int kDropShadowHeight = 4; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 const SkColor kImmersiveInactiveTabColor = SkColorSetRGB(190, 190, 190); | 128 const SkColor kImmersiveInactiveTabColor = SkColorSetRGB(190, 190, 190); |
| 126 | 129 |
| 127 // 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 |
| 128 // throbbing in the immersive mode light strip. | 131 // throbbing in the immersive mode light strip. |
| 129 const double kImmersiveTabMinThrobOpacity = 0.66; | 132 const double kImmersiveTabMinThrobOpacity = 0.66; |
| 130 | 133 |
| 131 // Number of steps in the immersive mode loading animation. | 134 // Number of steps in the immersive mode loading animation. |
| 132 const int kImmersiveLoadingStepCount = 32; | 135 const int kImmersiveLoadingStepCount = 32; |
| 133 | 136 |
| 134 const char kTabCloseButtonName[] = "TabCloseButton"; | 137 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 138 const char kMediaIndicatorButtonName[] = "MediaIndicatorButton"; | |
| 135 | 139 |
| 136 void DrawIconAtLocation(gfx::Canvas* canvas, | 140 void DrawIconAtLocation(gfx::Canvas* canvas, |
| 137 const gfx::ImageSkia& image, | 141 const gfx::ImageSkia& image, |
| 138 int image_offset, | 142 int image_offset, |
| 139 int dst_x, | 143 int dst_x, |
| 140 int dst_y, | 144 int dst_y, |
| 141 int icon_width, | 145 int icon_width, |
| 142 int icon_height, | 146 int icon_height, |
| 143 bool filter, | 147 bool filter, |
| 144 const SkPaint& paint) { | 148 const SkPaint& paint) { |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 371 | 375 |
| 372 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); | 376 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); |
| 373 } | 377 } |
| 374 | 378 |
| 375 Tab* tab_; | 379 Tab* tab_; |
| 376 | 380 |
| 377 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); | 381 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); |
| 378 }; | 382 }; |
| 379 | 383 |
| 380 //////////////////////////////////////////////////////////////////////////////// | 384 //////////////////////////////////////////////////////////////////////////////// |
| 385 // MediaIndicatorButton | |
| 386 // | |
| 387 // This is a Button subclass that serves as both the media indicator icon | |
| 388 // (audio, tab capture, etc.), and as a mute button. When the indicator is | |
| 389 // transitioned to the audio playing or muting state, the button functionality | |
| 390 // is enabled and begins handling mouse events. Otherwise, this view behaves | |
| 391 // like an image and all mouse events will be handled by the Tab (its parent | |
| 392 // View). | |
| 393 class Tab::MediaIndicatorButton : public views::ImageButton, | |
|
sky
2014/09/23 22:58:18
Move this into its own .h/.cc.
miu
2014/09/24 22:34:16
Done.
| |
| 394 public views::ViewTargeterDelegate { | |
| 395 public: | |
| 396 explicit MediaIndicatorButton(Tab* tab) | |
|
sky
2014/09/23 22:58:18
You don't seem to use tab at all here.
miu
2014/09/24 22:34:16
Done. Yep, I can downcast from View::parent() whe
| |
| 397 : views::ImageButton(NULL), | |
| 398 media_state_(TAB_MEDIA_STATE_NONE), | |
| 399 showing_media_state_(TAB_MEDIA_STATE_NONE) { | |
| 400 SetEventTargeter( | |
| 401 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); | |
| 402 } | |
| 403 | |
| 404 virtual ~MediaIndicatorButton() {} | |
| 405 | |
| 406 // Returns the current TabMediaState except, while the indicator image is | |
| 407 // fading out, returns the prior TabMediaState. | |
| 408 TabMediaState showing_media_state() const { | |
| 409 return showing_media_state_; | |
| 410 } | |
| 411 | |
| 412 // Updates ImageButton images, starts fade animations, and | |
| 413 // activates/deactivates button functionality as appropriate. | |
| 414 void TransitionToMediaState(TabMediaState next_state) { | |
| 415 if (next_state == media_state_) | |
| 416 return; | |
| 417 | |
| 418 if (next_state != TAB_MEDIA_STATE_NONE) { | |
| 419 const gfx::ImageSkia* const indicator_image = | |
| 420 chrome::GetTabMediaIndicatorImage(next_state).ToImageSkia(); | |
| 421 SetImage(views::CustomButton::STATE_NORMAL, indicator_image); | |
| 422 SetImage(views::CustomButton::STATE_DISABLED, indicator_image); | |
| 423 const gfx::ImageSkia* const affordance_image = | |
| 424 chrome::GetTabMediaIndicatorAffordanceImage(next_state).ToImageSkia(); | |
| 425 SetImage(views::CustomButton::STATE_HOVERED, affordance_image); | |
| 426 SetImage(views::CustomButton::STATE_PRESSED, affordance_image); | |
| 427 } | |
| 428 | |
| 429 if ((media_state_ == TAB_MEDIA_STATE_AUDIO_PLAYING && | |
| 430 next_state == TAB_MEDIA_STATE_AUDIO_MUTING) || | |
| 431 (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING && | |
| 432 next_state == TAB_MEDIA_STATE_AUDIO_PLAYING) || | |
| 433 (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING && | |
| 434 next_state == TAB_MEDIA_STATE_NONE)) { | |
| 435 // Instant user feedback: No fade animation. | |
| 436 showing_media_state_ = next_state; | |
| 437 media_indicator_animation_.reset(); | |
| 438 } else { | |
| 439 if (next_state == TAB_MEDIA_STATE_NONE) | |
| 440 showing_media_state_ = media_state_; // Fading-out indicator. | |
| 441 else | |
| 442 showing_media_state_ = next_state; // Fading-in to next indicator. | |
| 443 media_indicator_animation_ = | |
| 444 chrome::CreateTabMediaIndicatorFadeAnimation(next_state); | |
| 445 media_indicator_animation_->set_delegate(this); | |
| 446 media_indicator_animation_->Start(); | |
| 447 } | |
| 448 | |
| 449 SetEnabled(chrome::IsTabAudioMutingFeatureEnabled() && | |
| 450 (next_state == TAB_MEDIA_STATE_AUDIO_PLAYING || | |
| 451 next_state == TAB_MEDIA_STATE_AUDIO_MUTING)); | |
| 452 | |
| 453 // An indicator state change should be made visible immediately, instead of | |
| 454 // the user being surprised when their mouse leaves the button. | |
| 455 if (state() == views::CustomButton::STATE_HOVERED) { | |
| 456 SetState(enabled() ? views::CustomButton::STATE_NORMAL : | |
| 457 views::CustomButton::STATE_DISABLED); | |
| 458 } | |
| 459 | |
| 460 media_state_ = next_state; | |
| 461 } | |
| 462 | |
| 463 protected: | |
| 464 // views::View: | |
| 465 virtual const char* GetClassName() const OVERRIDE { | |
| 466 return kMediaIndicatorButtonName; | |
| 467 } | |
| 468 | |
| 469 virtual View* GetTooltipHandlerForPoint(const gfx::Point& point) OVERRIDE { | |
| 470 return NULL; // Tab (the parent View) provides the tooltip. | |
| 471 } | |
| 472 | |
| 473 virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE { | |
| 474 const ButtonState previous_state = state(); | |
| 475 const bool ret = ImageButton::OnMouseDragged(event); | |
| 476 if (previous_state != views::CustomButton::STATE_NORMAL && | |
| 477 state() == views::CustomButton::STATE_NORMAL) | |
| 478 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Dragged")); | |
| 479 return ret; | |
| 480 } | |
| 481 | |
| 482 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
| 483 double opaqueness = media_indicator_animation_ ? | |
| 484 media_indicator_animation_->GetCurrentValue() : 1.0; | |
| 485 if (media_state_ == TAB_MEDIA_STATE_NONE) | |
| 486 opaqueness = 1.0 - opaqueness; // Fading out, not in. | |
|
sky
2014/09/23 22:58:18
Can't you make the animation return the right valu
miu
2014/09/24 22:34:16
I think it goes against the intentions of gfx::Ani
sky
2014/09/25 19:25:04
Well, it depends upon the animation. In theory you
| |
| 487 if (opaqueness < 1.0) | |
| 488 canvas->SaveLayerAlpha(opaqueness * SK_AlphaOPAQUE); | |
| 489 ImageButton::OnPaint(canvas); | |
| 490 if (opaqueness < 1.0) | |
| 491 canvas->Restore(); | |
| 492 } | |
| 493 | |
| 494 // views::ViewTargeterDelegate | |
| 495 virtual bool DoesIntersectRect(const View* target, | |
| 496 const gfx::Rect& rect) const OVERRIDE { | |
| 497 // If this button is not enabled, Tab (the parent View) handles all mouse | |
| 498 // events. | |
| 499 return enabled() && | |
| 500 views::ViewTargeterDelegate::DoesIntersectRect(target, rect); | |
| 501 } | |
| 502 | |
| 503 // views::Button: | |
| 504 virtual void NotifyClick(const ui::Event& event) OVERRIDE { | |
|
sky
2014/09/23 22:58:17
Is it going to be annoying that the behavior of th
miu
2014/09/24 22:34:16
(I assume you're referring to the code/comment at
sky
2014/09/25 19:25:04
Just in general because the state may change at an
| |
| 505 if (media_state_ == TAB_MEDIA_STATE_AUDIO_PLAYING) | |
| 506 content::RecordAction(UserMetricsAction("MuteTab")); | |
| 507 else if (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING) | |
| 508 content::RecordAction(UserMetricsAction("UnmuteTab")); | |
| 509 else | |
| 510 NOTREACHED(); | |
| 511 | |
| 512 if (Tab* const tab = static_cast<Tab*>(parent())) | |
|
sky
2014/09/23 22:58:18
Can the parent really be NULL? I would expect this
miu
2014/09/24 22:34:16
Done.
| |
| 513 tab->controller_->ToggleTabAudioMute(tab); | |
| 514 } | |
| 515 | |
| 516 // gfx::AnimationDelegate | |
| 517 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE { | |
|
sky
2014/09/23 22:58:18
Having ImageButton be the delegate for two animati
miu
2014/09/24 22:34:16
Done.
| |
| 518 showing_media_state_ = media_state_; | |
| 519 ImageButton::AnimationCanceled(animation); | |
| 520 SchedulePaint(); | |
| 521 } | |
| 522 | |
| 523 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE { | |
| 524 showing_media_state_ = media_state_; | |
| 525 ImageButton::AnimationEnded(animation); | |
| 526 SchedulePaint(); | |
| 527 } | |
| 528 | |
| 529 private: | |
| 530 TabMediaState media_state_; | |
| 531 | |
| 532 // Media indicator fade-in/out animation (i.e., only on show/hide, not a | |
| 533 // continuous animation). | |
| 534 scoped_ptr<gfx::Animation> media_indicator_animation_; | |
| 535 TabMediaState showing_media_state_; | |
| 536 | |
| 537 DISALLOW_COPY_AND_ASSIGN(MediaIndicatorButton); | |
| 538 }; | |
| 539 | |
| 540 //////////////////////////////////////////////////////////////////////////////// | |
| 381 // ImageCacheEntry | 541 // ImageCacheEntry |
| 382 | 542 |
| 383 Tab::ImageCacheEntry::ImageCacheEntry() | 543 Tab::ImageCacheEntry::ImageCacheEntry() |
| 384 : resource_id(-1), | 544 : resource_id(-1), |
| 385 scale_factor(ui::SCALE_FACTOR_NONE) { | 545 scale_factor(ui::SCALE_FACTOR_NONE) { |
| 386 } | 546 } |
| 387 | 547 |
| 388 Tab::ImageCacheEntry::~ImageCacheEntry() {} | 548 Tab::ImageCacheEntry::~ImageCacheEntry() {} |
| 389 | 549 |
| 390 //////////////////////////////////////////////////////////////////////////////// | 550 //////////////////////////////////////////////////////////////////////////////// |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 402 | 562 |
| 403 Tab::Tab(TabController* controller) | 563 Tab::Tab(TabController* controller) |
| 404 : controller_(controller), | 564 : controller_(controller), |
| 405 closing_(false), | 565 closing_(false), |
| 406 dragging_(false), | 566 dragging_(false), |
| 407 detached_(false), | 567 detached_(false), |
| 408 favicon_hiding_offset_(0), | 568 favicon_hiding_offset_(0), |
| 409 loading_animation_frame_(0), | 569 loading_animation_frame_(0), |
| 410 immersive_loading_step_(0), | 570 immersive_loading_step_(0), |
| 411 should_display_crashed_favicon_(false), | 571 should_display_crashed_favicon_(false), |
| 412 animating_media_state_(TAB_MEDIA_STATE_NONE), | |
| 413 close_button_(NULL), | 572 close_button_(NULL), |
| 573 media_indicator_button_(NULL), | |
| 414 title_(new views::Label()), | 574 title_(new views::Label()), |
| 415 tab_activated_with_last_tap_down_(false), | 575 tab_activated_with_last_tap_down_(false), |
| 416 hover_controller_(this), | 576 hover_controller_(this), |
| 417 showing_icon_(false), | 577 showing_icon_(false), |
| 418 showing_media_indicator_(false), | 578 showing_media_indicator_(false), |
| 419 showing_close_button_(false), | 579 showing_close_button_(false), |
| 420 close_button_color_(0) { | 580 close_button_color_(0) { |
| 421 DCHECK(controller); | 581 DCHECK(controller); |
| 422 InitTabResources(); | 582 InitTabResources(); |
| 423 | 583 |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 502 #else | 662 #else |
| 503 StartCrashAnimation(); | 663 StartCrashAnimation(); |
| 504 #endif | 664 #endif |
| 505 } | 665 } |
| 506 } else { | 666 } else { |
| 507 if (IsPerformingCrashAnimation()) | 667 if (IsPerformingCrashAnimation()) |
| 508 StopCrashAnimation(); | 668 StopCrashAnimation(); |
| 509 ResetCrashedFavicon(); | 669 ResetCrashedFavicon(); |
| 510 } | 670 } |
| 511 | 671 |
| 512 if (data_.media_state != old.media_state) { | 672 if (data_.media_state != old.media_state) |
| 513 if (data_.media_state != TAB_MEDIA_STATE_NONE) | 673 LazyGetMediaIndicatorButton()->TransitionToMediaState(data_.media_state); |
| 514 animating_media_state_ = data_.media_state; | |
| 515 StartMediaIndicatorAnimation(); | |
| 516 } | |
| 517 | 674 |
| 518 if (old.mini != data_.mini) { | 675 if (old.mini != data_.mini) { |
| 519 StopAndDeleteAnimation( | 676 StopAndDeleteAnimation( |
| 520 mini_title_change_animation_.PassAs<gfx::Animation>()); | 677 mini_title_change_animation_.PassAs<gfx::Animation>()); |
| 521 } | 678 } |
| 522 | 679 |
| 523 DataChanged(old); | 680 DataChanged(old); |
| 524 | 681 |
| 525 Layout(); | 682 Layout(); |
| 526 SchedulePaint(); | 683 SchedulePaint(); |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 635 | 792 |
| 636 void Tab::AnimationProgressed(const gfx::Animation* animation) { | 793 void Tab::AnimationProgressed(const gfx::Animation* animation) { |
| 637 // Ignore if the pulse animation is being performed on active tab because | 794 // Ignore if the pulse animation is being performed on active tab because |
| 638 // it repaints the same image. See |Tab::PaintTabBackground()|. | 795 // it repaints the same image. See |Tab::PaintTabBackground()|. |
| 639 if (animation == pulse_animation_.get() && IsActive()) | 796 if (animation == pulse_animation_.get() && IsActive()) |
| 640 return; | 797 return; |
| 641 SchedulePaint(); | 798 SchedulePaint(); |
| 642 } | 799 } |
| 643 | 800 |
| 644 void Tab::AnimationCanceled(const gfx::Animation* animation) { | 801 void Tab::AnimationCanceled(const gfx::Animation* animation) { |
| 645 if (media_indicator_animation_ == animation) | |
| 646 animating_media_state_ = data_.media_state; | |
| 647 SchedulePaint(); | 802 SchedulePaint(); |
| 648 } | 803 } |
| 649 | 804 |
| 650 void Tab::AnimationEnded(const gfx::Animation* animation) { | 805 void Tab::AnimationEnded(const gfx::Animation* animation) { |
| 651 if (media_indicator_animation_ == animation) | |
| 652 animating_media_state_ = data_.media_state; | |
| 653 SchedulePaint(); | 806 SchedulePaint(); |
| 654 } | 807 } |
| 655 | 808 |
| 656 //////////////////////////////////////////////////////////////////////////////// | 809 //////////////////////////////////////////////////////////////////////////////// |
| 657 // Tab, views::ButtonListener overrides: | 810 // Tab, views::ButtonListener overrides: |
| 658 | 811 |
| 659 void Tab::ButtonPressed(views::Button* sender, const ui::Event& event) { | 812 void Tab::ButtonPressed(views::Button* sender, const ui::Event& event) { |
| 813 if (media_indicator_button_ && media_indicator_button_->visible()) { | |
| 814 if (media_indicator_button_->enabled()) | |
|
Mark P
2014/09/23 18:56:15
Are you planning to do sequence analysis? If not,
| |
| 815 content::RecordAction(UserMetricsAction("CloseTab_MuteToggleAvailable")); | |
| 816 else if (data_.media_state == TAB_MEDIA_STATE_AUDIO_PLAYING) | |
| 817 content::RecordAction(UserMetricsAction("CloseTab_AudioIndicator")); | |
| 818 else | |
| 819 content::RecordAction(UserMetricsAction("CloseTab_CaptureIndicator")); | |
|
Mark P
2014/09/23 19:49:56
Can you please give this a better name?
miu
2014/09/24 22:34:16
Done. Named it RecordingIndicator, per offline IM
| |
| 820 } else { | |
| 821 content::RecordAction(UserMetricsAction("CloseTab_NoMediaIndicator")); | |
| 822 } | |
| 823 | |
| 660 const CloseTabSource source = | 824 const CloseTabSource source = |
| 661 (event.type() == ui::ET_MOUSE_RELEASED && | 825 (event.type() == ui::ET_MOUSE_RELEASED && |
| 662 (event.flags() & ui::EF_FROM_TOUCH) == 0) ? CLOSE_TAB_FROM_MOUSE : | 826 (event.flags() & ui::EF_FROM_TOUCH) == 0) ? CLOSE_TAB_FROM_MOUSE : |
| 663 CLOSE_TAB_FROM_TOUCH; | 827 CLOSE_TAB_FROM_TOUCH; |
| 664 DCHECK_EQ(close_button_, sender); | 828 DCHECK_EQ(close_button_, sender); |
| 665 controller_->CloseTab(this, source); | 829 controller_->CloseTab(this, source); |
| 666 if (event.type() == ui::ET_GESTURE_TAP) | 830 if (event.type() == ui::ET_GESTURE_TAP) |
| 667 TouchUMA::RecordGestureAction(TouchUMA::GESTURE_TABCLOSE_TAP); | 831 TouchUMA::RecordGestureAction(TouchUMA::GESTURE_TABCLOSE_TAP); |
| 668 } | 832 } |
| 669 | 833 |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 763 const int left = kViewSpacing; | 927 const int left = kViewSpacing; |
| 764 const int right = width() - (lb.width() + close_button_size.width() + left); | 928 const int right = width() - (lb.width() + close_button_size.width() + left); |
| 765 close_button_->SetBorder( | 929 close_button_->SetBorder( |
| 766 views::Border::CreateEmptyBorder(top, left, bottom, right)); | 930 views::Border::CreateEmptyBorder(top, left, bottom, right)); |
| 767 close_button_->SetPosition(gfx::Point(lb.width(), 0)); | 931 close_button_->SetPosition(gfx::Point(lb.width(), 0)); |
| 768 close_button_->SizeToPreferredSize(); | 932 close_button_->SizeToPreferredSize(); |
| 769 } | 933 } |
| 770 close_button_->SetVisible(showing_close_button_); | 934 close_button_->SetVisible(showing_close_button_); |
| 771 | 935 |
| 772 showing_media_indicator_ = ShouldShowMediaIndicator(); | 936 showing_media_indicator_ = ShouldShowMediaIndicator(); |
| 773 media_indicator_bounds_.SetRect(lb.x(), lb.y(), 0, 0); | |
| 774 if (showing_media_indicator_) { | 937 if (showing_media_indicator_) { |
| 775 const gfx::Image& media_indicator_image = | 938 views::ImageButton* const button = LazyGetMediaIndicatorButton(); |
| 776 chrome::GetTabMediaIndicatorImage(animating_media_state_); | 939 const gfx::Size image_size(button->GetPreferredSize()); |
| 777 media_indicator_bounds_.set_width(media_indicator_image.Width()); | |
| 778 media_indicator_bounds_.set_height(media_indicator_image.Height()); | |
| 779 media_indicator_bounds_.set_y( | |
| 780 lb.y() + (lb.height() - media_indicator_bounds_.height() + 1) / 2); | |
| 781 const int right = showing_close_button_ ? | 940 const int right = showing_close_button_ ? |
| 782 close_button_->x() + close_button_->GetInsets().left() : lb.right(); | 941 close_button_->x() + close_button_->GetInsets().left() : lb.right(); |
| 783 media_indicator_bounds_.set_x( | 942 gfx::Rect bounds( |
| 784 std::max(lb.x(), right - media_indicator_bounds_.width())); | 943 std::max(lb.x(), right - image_size.width()), |
| 785 MaybeAdjustLeftForMiniTab(&media_indicator_bounds_); | 944 lb.y() + (lb.height() - image_size.height() + 1) / 2, |
| 945 image_size.width(), | |
| 946 image_size.height()); | |
| 947 MaybeAdjustLeftForMiniTab(&bounds); | |
| 948 button->SetBoundsRect(bounds); | |
|
sky
2014/09/23 22:58:18
Did you make sure this does the right thing when r
miu
2014/09/24 22:34:15
Good thing you had me check. When I first impleme
| |
| 949 button->SetVisible(true); | |
| 950 } else if (media_indicator_button_) { | |
| 951 media_indicator_button_->SetVisible(false); | |
| 786 } | 952 } |
| 787 | 953 |
| 788 // Size the title to fill the remaining width and use all available height. | 954 // Size the title to fill the remaining width and use all available height. |
| 789 bool show_title = !data().mini || width() >= kMiniTabRendererAsNormalTabWidth; | 955 bool show_title = !data().mini || width() >= kMiniTabRendererAsNormalTabWidth; |
| 790 if (show_title) { | 956 if (show_title) { |
| 791 int title_left = favicon_bounds_.right() + kFaviconTitleSpacing; | 957 int title_left = favicon_bounds_.right() + kFaviconTitleSpacing; |
| 792 int title_width = lb.width() - title_left; | 958 int title_width = lb.width() - title_left; |
| 793 if (showing_media_indicator_) { | 959 if (showing_media_indicator_) { |
| 794 title_width = media_indicator_bounds_.x() - kViewSpacing - title_left; | 960 title_width = media_indicator_button_->x() - kViewSpacing - title_left; |
| 795 } else if (close_button_->visible()) { | 961 } else if (close_button_->visible()) { |
| 796 // Allow the title to overlay the close button's empty border padding. | 962 // Allow the title to overlay the close button's empty border padding. |
| 797 title_width = close_button_->x() + close_button_->GetInsets().left() - | 963 title_width = close_button_->x() + close_button_->GetInsets().left() - |
| 798 kViewSpacing - title_left; | 964 kViewSpacing - title_left; |
| 799 } | 965 } |
| 800 gfx::Rect rect(title_left, lb.y(), std::max(title_width, 0), lb.height()); | 966 gfx::Rect rect(title_left, lb.y(), std::max(title_width, 0), lb.height()); |
| 801 const int title_height = title_->GetPreferredSize().height(); | 967 const int title_height = title_->GetPreferredSize().height(); |
| 802 if (title_height > rect.height()) { | 968 if (title_height > rect.height()) { |
| 803 rect.set_y(lb.y() - (title_height - rect.height()) / 2); | 969 rect.set_y(lb.y() - (title_height - rect.height()) / 2); |
| 804 rect.set_height(title_height); | 970 rect.set_height(title_height); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 897 Tab* closest_tab = controller_->GetTabAt(this, event.location()); | 1063 Tab* closest_tab = controller_->GetTabAt(this, event.location()); |
| 898 if (closest_tab) | 1064 if (closest_tab) |
| 899 controller_->CloseTab(closest_tab, CLOSE_TAB_FROM_MOUSE); | 1065 controller_->CloseTab(closest_tab, CLOSE_TAB_FROM_MOUSE); |
| 900 } | 1066 } |
| 901 } else if (event.IsOnlyLeftMouseButton() && !event.IsShiftDown() && | 1067 } else if (event.IsOnlyLeftMouseButton() && !event.IsShiftDown() && |
| 902 !event.IsControlDown()) { | 1068 !event.IsControlDown()) { |
| 903 // If the tab was already selected mouse pressed doesn't change the | 1069 // If the tab was already selected mouse pressed doesn't change the |
| 904 // selection. Reset it now to handle the case where multiple tabs were | 1070 // selection. Reset it now to handle the case where multiple tabs were |
| 905 // selected. | 1071 // selected. |
| 906 controller_->SelectTab(this); | 1072 controller_->SelectTab(this); |
| 1073 | |
| 1074 if (media_indicator_button_ && media_indicator_button_->visible() && | |
| 1075 media_indicator_button_->bounds().Contains(event.location())) { | |
| 1076 content::RecordAction(UserMetricsAction("TabMediaIndicator_Clicked")); | |
| 1077 } | |
| 907 } | 1078 } |
| 908 } | 1079 } |
| 909 | 1080 |
| 910 void Tab::OnMouseCaptureLost() { | 1081 void Tab::OnMouseCaptureLost() { |
| 911 controller_->EndDrag(END_DRAG_CAPTURE_LOST); | 1082 controller_->EndDrag(END_DRAG_CAPTURE_LOST); |
| 912 } | 1083 } |
| 913 | 1084 |
| 914 void Tab::OnMouseEntered(const ui::MouseEvent& event) { | 1085 void Tab::OnMouseEntered(const ui::MouseEvent& event) { |
| 915 hover_controller_.Show(views::GlowHoverController::SUBTLE); | 1086 hover_controller_.Show(views::GlowHoverController::SUBTLE); |
| 916 } | 1087 } |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1013 const SkColor title_color = GetThemeProvider()->GetColor(IsSelected() ? | 1184 const SkColor title_color = GetThemeProvider()->GetColor(IsSelected() ? |
| 1014 ThemeProperties::COLOR_TAB_TEXT : | 1185 ThemeProperties::COLOR_TAB_TEXT : |
| 1015 ThemeProperties::COLOR_BACKGROUND_TAB_TEXT); | 1186 ThemeProperties::COLOR_BACKGROUND_TAB_TEXT); |
| 1016 title_->SetVisible(!data().mini || | 1187 title_->SetVisible(!data().mini || |
| 1017 width() > kMiniTabRendererAsNormalTabWidth); | 1188 width() > kMiniTabRendererAsNormalTabWidth); |
| 1018 title_->SetEnabledColor(title_color); | 1189 title_->SetEnabledColor(title_color); |
| 1019 | 1190 |
| 1020 if (show_icon) | 1191 if (show_icon) |
| 1021 PaintIcon(canvas); | 1192 PaintIcon(canvas); |
| 1022 | 1193 |
| 1023 if (show_media_indicator) | |
| 1024 PaintMediaIndicator(canvas); | |
| 1025 | |
| 1026 // If the close button color has changed, generate a new one. | 1194 // If the close button color has changed, generate a new one. |
| 1027 if (!close_button_color_ || title_color != close_button_color_) { | 1195 if (!close_button_color_ || title_color != close_button_color_) { |
| 1028 close_button_color_ = title_color; | 1196 close_button_color_ = title_color; |
| 1029 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 1197 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 1030 close_button_->SetBackground(close_button_color_, | 1198 close_button_->SetBackground(close_button_color_, |
| 1031 rb.GetImageSkiaNamed(IDR_CLOSE_1), | 1199 rb.GetImageSkiaNamed(IDR_CLOSE_1), |
| 1032 rb.GetImageSkiaNamed(IDR_CLOSE_1_MASK)); | 1200 rb.GetImageSkiaNamed(IDR_CLOSE_1_MASK)); |
| 1033 } | 1201 } |
| 1034 } | 1202 } |
| 1035 | 1203 |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1334 bounds, true, SkPaint()); | 1502 bounds, true, SkPaint()); |
| 1335 } else if (!data().favicon.isNull()) { | 1503 } else if (!data().favicon.isNull()) { |
| 1336 // Paint the normal favicon. | 1504 // Paint the normal favicon. |
| 1337 DrawIconCenter(canvas, data().favicon, 0, | 1505 DrawIconCenter(canvas, data().favicon, 0, |
| 1338 data().favicon.width(), | 1506 data().favicon.width(), |
| 1339 data().favicon.height(), | 1507 data().favicon.height(), |
| 1340 bounds, true, SkPaint()); | 1508 bounds, true, SkPaint()); |
| 1341 } | 1509 } |
| 1342 } | 1510 } |
| 1343 | 1511 |
| 1344 void Tab::PaintMediaIndicator(gfx::Canvas* canvas) { | |
| 1345 if (media_indicator_bounds_.IsEmpty() || !media_indicator_animation_) | |
| 1346 return; | |
| 1347 | |
| 1348 gfx::Rect bounds = media_indicator_bounds_; | |
| 1349 bounds.set_x(GetMirroredXForRect(bounds)); | |
| 1350 | |
| 1351 SkPaint paint; | |
| 1352 paint.setAntiAlias(true); | |
| 1353 double opaqueness = media_indicator_animation_->GetCurrentValue(); | |
| 1354 if (data_.media_state == TAB_MEDIA_STATE_NONE) | |
| 1355 opaqueness = 1.0 - opaqueness; // Fading out, not in. | |
| 1356 paint.setAlpha(opaqueness * SK_AlphaOPAQUE); | |
| 1357 | |
| 1358 const gfx::ImageSkia& media_indicator_image = | |
| 1359 *(chrome::GetTabMediaIndicatorImage(animating_media_state_). | |
| 1360 ToImageSkia()); | |
| 1361 DrawIconAtLocation(canvas, media_indicator_image, 0, | |
| 1362 bounds.x(), bounds.y(), media_indicator_image.width(), | |
| 1363 media_indicator_image.height(), true, paint); | |
| 1364 } | |
| 1365 | |
| 1366 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, | 1512 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, |
| 1367 TabRendererData::NetworkState state) { | 1513 TabRendererData::NetworkState state) { |
| 1368 static bool initialized = false; | 1514 static bool initialized = false; |
| 1369 static int loading_animation_frame_count = 0; | 1515 static int loading_animation_frame_count = 0; |
| 1370 static int waiting_animation_frame_count = 0; | 1516 static int waiting_animation_frame_count = 0; |
| 1371 static int waiting_to_loading_frame_count_ratio = 0; | 1517 static int waiting_to_loading_frame_count_ratio = 0; |
| 1372 if (!initialized) { | 1518 if (!initialized) { |
| 1373 initialized = true; | 1519 initialized = true; |
| 1374 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 1520 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 1375 gfx::ImageSkia loading_animation(*rb.GetImageSkiaNamed(IDR_THROBBER)); | 1521 gfx::ImageSkia loading_animation(*rb.GetImageSkiaNamed(IDR_THROBBER)); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1432 if (available_width >= width_per_icon && | 1578 if (available_width >= width_per_icon && |
| 1433 available_width < (width_per_icon + kPaddingBetweenIcons)) { | 1579 available_width < (width_per_icon + kPaddingBetweenIcons)) { |
| 1434 return 1; | 1580 return 1; |
| 1435 } | 1581 } |
| 1436 return available_width / (width_per_icon + kPaddingBetweenIcons); | 1582 return available_width / (width_per_icon + kPaddingBetweenIcons); |
| 1437 } | 1583 } |
| 1438 | 1584 |
| 1439 bool Tab::ShouldShowIcon() const { | 1585 bool Tab::ShouldShowIcon() const { |
| 1440 return chrome::ShouldTabShowFavicon( | 1586 return chrome::ShouldTabShowFavicon( |
| 1441 IconCapacity(), data().mini, IsActive(), data().show_icon, | 1587 IconCapacity(), data().mini, IsActive(), data().show_icon, |
| 1442 animating_media_state_); | 1588 media_indicator_button_ ? media_indicator_button_->showing_media_state() : |
| 1589 data_.media_state); | |
| 1443 } | 1590 } |
| 1444 | 1591 |
| 1445 bool Tab::ShouldShowMediaIndicator() const { | 1592 bool Tab::ShouldShowMediaIndicator() const { |
| 1446 return chrome::ShouldTabShowMediaIndicator( | 1593 return chrome::ShouldTabShowMediaIndicator( |
| 1447 IconCapacity(), data().mini, IsActive(), data().show_icon, | 1594 IconCapacity(), data().mini, IsActive(), data().show_icon, |
| 1448 animating_media_state_); | 1595 media_indicator_button_ ? media_indicator_button_->showing_media_state() : |
| 1596 data_.media_state); | |
| 1449 } | 1597 } |
| 1450 | 1598 |
| 1451 bool Tab::ShouldShowCloseBox() const { | 1599 bool Tab::ShouldShowCloseBox() const { |
| 1452 return chrome::ShouldTabShowCloseButton( | 1600 return chrome::ShouldTabShowCloseButton( |
| 1453 IconCapacity(), data().mini, IsActive()); | 1601 IconCapacity(), data().mini, IsActive()); |
| 1454 } | 1602 } |
| 1455 | 1603 |
| 1456 double Tab::GetThrobValue() { | 1604 double Tab::GetThrobValue() { |
| 1457 const bool is_selected = IsSelected(); | 1605 const bool is_selected = IsSelected(); |
| 1458 const double min = is_selected ? kSelectedTabOpacity : 0; | 1606 const double min = is_selected ? kSelectedTabOpacity : 0; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1493 | 1641 |
| 1494 void Tab::StartCrashAnimation() { | 1642 void Tab::StartCrashAnimation() { |
| 1495 crash_icon_animation_.reset(new FaviconCrashAnimation(this)); | 1643 crash_icon_animation_.reset(new FaviconCrashAnimation(this)); |
| 1496 crash_icon_animation_->Start(); | 1644 crash_icon_animation_->Start(); |
| 1497 } | 1645 } |
| 1498 | 1646 |
| 1499 bool Tab::IsPerformingCrashAnimation() const { | 1647 bool Tab::IsPerformingCrashAnimation() const { |
| 1500 return crash_icon_animation_.get() && data_.IsCrashed(); | 1648 return crash_icon_animation_.get() && data_.IsCrashed(); |
| 1501 } | 1649 } |
| 1502 | 1650 |
| 1503 void Tab::StartMediaIndicatorAnimation() { | |
| 1504 media_indicator_animation_ = | |
| 1505 chrome::CreateTabMediaIndicatorFadeAnimation(data_.media_state); | |
| 1506 media_indicator_animation_->set_delegate(this); | |
| 1507 media_indicator_animation_->Start(); | |
| 1508 } | |
| 1509 | |
| 1510 void Tab::ScheduleIconPaint() { | 1651 void Tab::ScheduleIconPaint() { |
| 1511 gfx::Rect bounds = favicon_bounds_; | 1652 gfx::Rect bounds = favicon_bounds_; |
| 1512 if (bounds.IsEmpty()) | 1653 if (bounds.IsEmpty()) |
| 1513 return; | 1654 return; |
| 1514 | 1655 |
| 1515 // Extends the area to the bottom when sad_favicon is animating. | 1656 // Extends the area to the bottom when sad_favicon is animating. |
| 1516 if (IsPerformingCrashAnimation()) | 1657 if (IsPerformingCrashAnimation()) |
| 1517 bounds.set_height(height() - bounds.y()); | 1658 bounds.set_height(height() - bounds.y()); |
| 1518 bounds.set_x(GetMirroredXForRect(bounds)); | 1659 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1519 SchedulePaintInRect(bounds); | 1660 SchedulePaintInRect(bounds); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 1539 *frame_id = 0; | 1680 *frame_id = 0; |
| 1540 } else if (data().incognito) { | 1681 } else if (data().incognito) { |
| 1541 *tab_id = IDR_THEME_TAB_BACKGROUND_INCOGNITO; | 1682 *tab_id = IDR_THEME_TAB_BACKGROUND_INCOGNITO; |
| 1542 *frame_id = IDR_THEME_FRAME_INCOGNITO; | 1683 *frame_id = IDR_THEME_FRAME_INCOGNITO; |
| 1543 } else { | 1684 } else { |
| 1544 *tab_id = IDR_THEME_TAB_BACKGROUND; | 1685 *tab_id = IDR_THEME_TAB_BACKGROUND; |
| 1545 *frame_id = IDR_THEME_FRAME; | 1686 *frame_id = IDR_THEME_FRAME; |
| 1546 } | 1687 } |
| 1547 } | 1688 } |
| 1548 | 1689 |
| 1690 Tab::MediaIndicatorButton* Tab::LazyGetMediaIndicatorButton() { | |
| 1691 if (!media_indicator_button_) { | |
| 1692 media_indicator_button_ = new MediaIndicatorButton(this); | |
| 1693 AddChildView(media_indicator_button_); // Takes ownership. | |
| 1694 } | |
| 1695 return media_indicator_button_; | |
| 1696 } | |
| 1697 | |
| 1549 //////////////////////////////////////////////////////////////////////////////// | 1698 //////////////////////////////////////////////////////////////////////////////// |
| 1550 // Tab, private static: | 1699 // Tab, private static: |
| 1551 | 1700 |
| 1552 // static | 1701 // static |
| 1553 void Tab::InitTabResources() { | 1702 void Tab::InitTabResources() { |
| 1554 static bool initialized = false; | 1703 static bool initialized = false; |
| 1555 if (initialized) | 1704 if (initialized) |
| 1556 return; | 1705 return; |
| 1557 | 1706 |
| 1558 initialized = true; | 1707 initialized = true; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1603 const gfx::ImageSkia& image) { | 1752 const gfx::ImageSkia& image) { |
| 1604 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); | 1753 DCHECK_NE(scale_factor, ui::SCALE_FACTOR_NONE); |
| 1605 ImageCacheEntry entry; | 1754 ImageCacheEntry entry; |
| 1606 entry.resource_id = resource_id; | 1755 entry.resource_id = resource_id; |
| 1607 entry.scale_factor = scale_factor; | 1756 entry.scale_factor = scale_factor; |
| 1608 entry.image = image; | 1757 entry.image = image; |
| 1609 image_cache_->push_front(entry); | 1758 image_cache_->push_front(entry); |
| 1610 if (image_cache_->size() > kMaxImageCacheSize) | 1759 if (image_cache_->size() > kMaxImageCacheSize) |
| 1611 image_cache_->pop_back(); | 1760 image_cache_->pop_back(); |
| 1612 } | 1761 } |
| OLD | NEW |