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.
5 #include "chrome/browser/ui/views/tabs/media_indicator_button.h"
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"
15 using base::UserMetricsAction
;
17 const char MediaIndicatorButton::kViewClassName
[] = "MediaIndicatorButton";
19 class MediaIndicatorButton::FadeAnimationDelegate
20 : public gfx::AnimationDelegate
{
22 explicit FadeAnimationDelegate(MediaIndicatorButton
* button
)
24 ~FadeAnimationDelegate() override
{}
27 // gfx::AnimationDelegate
28 void AnimationProgressed(const gfx::Animation
* animation
) override
{
29 button_
->SchedulePaint();
32 void AnimationCanceled(const gfx::Animation
* animation
) override
{
33 button_
->showing_media_state_
= button_
->media_state_
;
34 button_
->SchedulePaint();
37 void AnimationEnded(const gfx::Animation
* animation
) override
{
38 button_
->showing_media_state_
= button_
->media_state_
;
39 button_
->SchedulePaint();
42 MediaIndicatorButton
* const button_
;
44 DISALLOW_COPY_AND_ASSIGN(FadeAnimationDelegate
);
47 MediaIndicatorButton::MediaIndicatorButton()
48 : views::ImageButton(NULL
),
49 media_state_(TAB_MEDIA_STATE_NONE
),
50 showing_media_state_(TAB_MEDIA_STATE_NONE
) {
52 scoped_ptr
<views::ViewTargeter
>(new views::ViewTargeter(this)));
55 MediaIndicatorButton::~MediaIndicatorButton() {}
57 void MediaIndicatorButton::TransitionToMediaState(TabMediaState next_state
) {
58 if (next_state
== media_state_
)
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
);
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();
82 if (next_state
== TAB_MEDIA_STATE_NONE
)
83 showing_media_state_
= media_state_
; // Fading-out indicator.
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();
93 SetEnabled(chrome::IsTabAudioMutingFeatureEnabled() &&
94 (next_state
== TAB_MEDIA_STATE_AUDIO_PLAYING
||
95 next_state
== TAB_MEDIA_STATE_AUDIO_MUTING
));
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
);
104 media_state_
= next_state
;
106 // Note: The calls to SetImage(), SetEnabled(), and SetState() above will call
107 // SchedulePaint() if necessary.
110 const char* MediaIndicatorButton::GetClassName() const {
111 return kViewClassName
;
114 views::View
* MediaIndicatorButton::GetTooltipHandlerForPoint(
115 const gfx::Point
& point
) {
116 return NULL
; // Tab (the parent View) provides the tooltip.
119 bool MediaIndicatorButton::OnMousePressed(const ui::MouseEvent
& event
) {
120 const bool handled
= ImageButton::OnMousePressed(event
);
121 // Explicitly mark midle-mouse clicks as non-handled to ensure the tab sees
123 return !event
.IsMiddleMouseButton() && handled
;
126 bool MediaIndicatorButton::OnMouseDragged(const ui::MouseEvent
& event
) {
127 const ButtonState previous_state
= state();
128 const bool ret
= ImageButton::OnMouseDragged(event
);
129 if (previous_state
!= views::CustomButton::STATE_NORMAL
&&
130 state() == views::CustomButton::STATE_NORMAL
)
131 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Dragged"));
135 void MediaIndicatorButton::OnPaint(gfx::Canvas
* canvas
) {
137 fade_animation_
? fade_animation_
->GetCurrentValue() : 1.0;
138 if (media_state_
== TAB_MEDIA_STATE_NONE
)
139 opaqueness
= 1.0 - opaqueness
; // Fading out, not in.
140 if (opaqueness
< 1.0)
141 canvas
->SaveLayerAlpha(opaqueness
* SK_AlphaOPAQUE
);
142 ImageButton::OnPaint(canvas
);
143 if (opaqueness
< 1.0)
147 bool MediaIndicatorButton::DoesIntersectRect(const views::View
* target
,
148 const gfx::Rect
& rect
) const {
149 // If this button is not enabled, Tab (the parent View) handles all mouse
152 views::ViewTargeterDelegate::DoesIntersectRect(target
, rect
);
155 void MediaIndicatorButton::NotifyClick(const ui::Event
& event
) {
156 if (media_state_
== TAB_MEDIA_STATE_AUDIO_PLAYING
)
157 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Mute"));
158 else if (media_state_
== TAB_MEDIA_STATE_AUDIO_MUTING
)
159 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Unmute"));
163 DCHECK(parent() && !strcmp(parent()->GetClassName(), Tab::kViewClassName
));
164 Tab
* const tab
= static_cast<Tab
*>(parent());
165 tab
->controller()->ToggleTabAudioMute(tab
);