1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/tabs/media_indicator_button.h" 6 7 #include "chrome/browser/ui/views/tabs/tab.h" 8 #include "chrome/browser/ui/views/tabs/tab_controller.h" 9 #include "chrome/browser/ui/views/tabs/tab_renderer_data.h" 10 #include "content/public/browser/user_metrics.h" 11 #include "ui/gfx/animation/animation_delegate.h" 12 #include "ui/gfx/canvas.h" 13 #include "ui/gfx/image/image.h" 14 15 using base::UserMetricsAction; 16 17 const char MediaIndicatorButton::kViewClassName[] = "MediaIndicatorButton"; 18 19 class MediaIndicatorButton::FadeAnimationDelegate 20 : public gfx::AnimationDelegate { 21 public: 22 explicit FadeAnimationDelegate(MediaIndicatorButton* button) 23 : button_(button) {} 24 virtual ~FadeAnimationDelegate() {} 25 26 private: 27 // gfx::AnimationDelegate 28 virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE { 29 button_->SchedulePaint(); 30 } 31 32 virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE { 33 button_->showing_media_state_ = button_->media_state_; 34 button_->SchedulePaint(); 35 } 36 37 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE { 38 button_->showing_media_state_ = button_->media_state_; 39 button_->SchedulePaint(); 40 } 41 42 MediaIndicatorButton* const button_; 43 44 DISALLOW_COPY_AND_ASSIGN(FadeAnimationDelegate); 45 }; 46 47 MediaIndicatorButton::MediaIndicatorButton() 48 : views::ImageButton(NULL), 49 media_state_(TAB_MEDIA_STATE_NONE), 50 showing_media_state_(TAB_MEDIA_STATE_NONE) { 51 SetEventTargeter( 52 scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); 53 } 54 55 MediaIndicatorButton::~MediaIndicatorButton() {} 56 57 void MediaIndicatorButton::TransitionToMediaState(TabMediaState next_state) { 58 if (next_state == media_state_) 59 return; 60 61 if (next_state != TAB_MEDIA_STATE_NONE) { 62 const gfx::ImageSkia* const indicator_image = 63 chrome::GetTabMediaIndicatorImage(next_state).ToImageSkia(); 64 SetImage(views::CustomButton::STATE_NORMAL, indicator_image); 65 SetImage(views::CustomButton::STATE_DISABLED, indicator_image); 66 const gfx::ImageSkia* const affordance_image = 67 chrome::GetTabMediaIndicatorAffordanceImage(next_state).ToImageSkia(); 68 SetImage(views::CustomButton::STATE_HOVERED, affordance_image); 69 SetImage(views::CustomButton::STATE_PRESSED, affordance_image); 70 } 71 72 if ((media_state_ == TAB_MEDIA_STATE_AUDIO_PLAYING && 73 next_state == TAB_MEDIA_STATE_AUDIO_MUTING) || 74 (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING && 75 next_state == TAB_MEDIA_STATE_AUDIO_PLAYING) || 76 (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING && 77 next_state == TAB_MEDIA_STATE_NONE)) { 78 // Instant user feedback: No fade animation. 79 showing_media_state_ = next_state; 80 fade_animation_.reset(); 81 } else { 82 if (next_state == TAB_MEDIA_STATE_NONE) 83 showing_media_state_ = media_state_; // Fading-out indicator. 84 else 85 showing_media_state_ = next_state; // Fading-in to next indicator. 86 fade_animation_ = chrome::CreateTabMediaIndicatorFadeAnimation(next_state); 87 if (!fade_animation_delegate_) 88 fade_animation_delegate_.reset(new FadeAnimationDelegate(this)); 89 fade_animation_->set_delegate(fade_animation_delegate_.get()); 90 fade_animation_->Start(); 91 } 92 93 SetEnabled(chrome::IsTabAudioMutingFeatureEnabled() && 94 (next_state == TAB_MEDIA_STATE_AUDIO_PLAYING || 95 next_state == TAB_MEDIA_STATE_AUDIO_MUTING)); 96 97 // An indicator state change should be made visible immediately, instead of 98 // the user being surprised when their mouse leaves the button. 99 if (state() == views::CustomButton::STATE_HOVERED) { 100 SetState(enabled() ? views::CustomButton::STATE_NORMAL : 101 views::CustomButton::STATE_DISABLED); 102 } 103 104 media_state_ = next_state; 105 106 // Note: The calls to SetImage(), SetEnabled(), and SetState() above will call 107 // SchedulePaint() if necessary. 108 } 109 110 const char* MediaIndicatorButton::GetClassName() const { 111 return kViewClassName; 112 } 113 114 views::View* MediaIndicatorButton::GetTooltipHandlerForPoint( 115 const gfx::Point& point) { 116 return NULL; // Tab (the parent View) provides the tooltip. 117 } 118 119 bool MediaIndicatorButton::OnMouseDragged(const ui::MouseEvent& event) { 120 const ButtonState previous_state = state(); 121 const bool ret = ImageButton::OnMouseDragged(event); 122 if (previous_state != views::CustomButton::STATE_NORMAL && 123 state() == views::CustomButton::STATE_NORMAL) 124 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Dragged")); 125 return ret; 126 } 127 128 void MediaIndicatorButton::OnPaint(gfx::Canvas* canvas) { 129 double opaqueness = 130 fade_animation_ ? fade_animation_->GetCurrentValue() : 1.0; 131 if (media_state_ == TAB_MEDIA_STATE_NONE) 132 opaqueness = 1.0 - opaqueness; // Fading out, not in. 133 if (opaqueness < 1.0) 134 canvas->SaveLayerAlpha(opaqueness * SK_AlphaOPAQUE); 135 ImageButton::OnPaint(canvas); 136 if (opaqueness < 1.0) 137 canvas->Restore(); 138 } 139 140 bool MediaIndicatorButton::DoesIntersectRect(const views::View* target, 141 const gfx::Rect& rect) const { 142 // If this button is not enabled, Tab (the parent View) handles all mouse 143 // events. 144 return enabled() && 145 views::ViewTargeterDelegate::DoesIntersectRect(target, rect); 146 } 147 148 void MediaIndicatorButton::NotifyClick(const ui::Event& event) { 149 if (media_state_ == TAB_MEDIA_STATE_AUDIO_PLAYING) 150 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Mute")); 151 else if (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING) 152 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Unmute")); 153 else 154 NOTREACHED(); 155 156 DCHECK(parent() && !strcmp(parent()->GetClassName(), Tab::kViewClassName)); 157 Tab* const tab = static_cast<Tab*>(parent()); 158 tab->controller()->ToggleTabAudioMute(tab); 159 } 160