Home | History | Annotate | Download | only in tabs
      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