| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 #import "chrome/browser/ui/cocoa/tabs/media_indicator_button.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/thread_task_runner_handle.h" | |
| 9 #include "content/public/browser/user_metrics.h" | |
| 10 #include "ui/gfx/animation/animation.h" | |
| 11 #include "ui/gfx/animation/animation_delegate.h" | |
| 12 #include "ui/gfx/image/image.h" | |
| 13 | |
| 14 @implementation MediaIndicatorButton | |
| 15 | |
| 16 class FadeAnimationDelegate : public gfx::AnimationDelegate { | |
| 17 public: | |
| 18 explicit FadeAnimationDelegate(MediaIndicatorButton* button) | |
| 19 : button_(button) {} | |
| 20 ~FadeAnimationDelegate() override {} | |
| 21 | |
| 22 private: | |
| 23 // gfx::AnimationDelegate implementation. | |
| 24 void AnimationProgressed(const gfx::Animation* animation) override { | |
| 25 [button_ setNeedsDisplay:YES]; | |
| 26 } | |
| 27 | |
| 28 void AnimationCanceled(const gfx::Animation* animation) override { | |
| 29 AnimationEnded(animation); | |
| 30 } | |
| 31 | |
| 32 void AnimationEnded(const gfx::Animation* animation) override { | |
| 33 button_->showingMediaState_ = button_->mediaState_; | |
| 34 [button_ setNeedsDisplay:YES]; | |
| 35 [button_->animationDoneTarget_ | |
| 36 performSelector:button_->animationDoneAction_]; | |
| 37 } | |
| 38 | |
| 39 MediaIndicatorButton* const button_; | |
| 40 | |
| 41 DISALLOW_COPY_AND_ASSIGN(FadeAnimationDelegate); | |
| 42 }; | |
| 43 | |
| 44 @synthesize showingMediaState = showingMediaState_; | |
| 45 | |
| 46 - (id)init { | |
| 47 if ((self = [super initWithFrame:NSZeroRect])) { | |
| 48 mediaState_ = TAB_MEDIA_STATE_NONE; | |
| 49 showingMediaState_ = TAB_MEDIA_STATE_NONE; | |
| 50 [self setEnabled:NO]; | |
| 51 [super setTarget:self]; | |
| 52 [super setAction:@selector(handleClick:)]; | |
| 53 } | |
| 54 return self; | |
| 55 } | |
| 56 | |
| 57 - (void)removeFromSuperview { | |
| 58 fadeAnimation_.reset(); | |
| 59 [super removeFromSuperview]; | |
| 60 } | |
| 61 | |
| 62 - (void)transitionToMediaState:(TabMediaState)nextState { | |
| 63 if (nextState == mediaState_) | |
| 64 return; | |
| 65 | |
| 66 if (nextState != TAB_MEDIA_STATE_NONE) { | |
| 67 [self setImage:chrome::GetTabMediaIndicatorImage(nextState).ToNSImage()]; | |
| 68 affordanceImage_.reset( | |
| 69 [chrome::GetTabMediaIndicatorAffordanceImage(nextState).ToNSImage() | |
| 70 retain]); | |
| 71 } | |
| 72 | |
| 73 if ((mediaState_ == TAB_MEDIA_STATE_AUDIO_PLAYING && | |
| 74 nextState == TAB_MEDIA_STATE_AUDIO_MUTING) || | |
| 75 (mediaState_ == TAB_MEDIA_STATE_AUDIO_MUTING && | |
| 76 nextState == TAB_MEDIA_STATE_AUDIO_PLAYING) || | |
| 77 (mediaState_ == TAB_MEDIA_STATE_AUDIO_MUTING && | |
| 78 nextState == TAB_MEDIA_STATE_NONE)) { | |
| 79 // Instant user feedback: No fade animation. | |
| 80 showingMediaState_ = nextState; | |
| 81 fadeAnimation_.reset(); | |
| 82 } else { | |
| 83 if (nextState == TAB_MEDIA_STATE_NONE) | |
| 84 showingMediaState_ = mediaState_; // Fading-out indicator. | |
| 85 else | |
| 86 showingMediaState_ = nextState; // Fading-in to next indicator. | |
| 87 // gfx::Animation requires a task runner is available for the current | |
| 88 // thread. Generally, only certain unit tests would not instantiate a task | |
| 89 // runner. | |
| 90 if (base::ThreadTaskRunnerHandle::IsSet()) { | |
| 91 fadeAnimation_ = chrome::CreateTabMediaIndicatorFadeAnimation(nextState); | |
| 92 if (!fadeAnimationDelegate_) | |
| 93 fadeAnimationDelegate_.reset(new FadeAnimationDelegate(self)); | |
| 94 fadeAnimation_->set_delegate(fadeAnimationDelegate_.get()); | |
| 95 fadeAnimation_->Start(); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 [self setEnabled:(chrome::IsTabAudioMutingFeatureEnabled() && | |
| 100 (nextState == TAB_MEDIA_STATE_AUDIO_PLAYING || | |
| 101 nextState == TAB_MEDIA_STATE_AUDIO_MUTING))]; | |
| 102 | |
| 103 // An indicator state change should be made visible immediately, instead of | |
| 104 // the user being surprised when their mouse leaves the button. | |
| 105 if ([self hoverState] == kHoverStateMouseOver) | |
| 106 [self setHoverState:kHoverStateNone]; | |
| 107 | |
| 108 mediaState_ = nextState; | |
| 109 | |
| 110 [self setNeedsDisplay:YES]; | |
| 111 } | |
| 112 | |
| 113 - (void)setTarget:(id)aTarget { | |
| 114 NOTREACHED(); // See class-level comments. | |
| 115 } | |
| 116 | |
| 117 - (void)setAction:(SEL)anAction { | |
| 118 NOTREACHED(); // See class-level comments. | |
| 119 } | |
| 120 | |
| 121 - (void)setAnimationDoneTarget:(id)target withAction:(SEL)action { | |
| 122 animationDoneTarget_ = target; | |
| 123 animationDoneAction_ = action; | |
| 124 } | |
| 125 | |
| 126 - (void)setClickTarget:(id)target withAction:(SEL)action { | |
| 127 clickTarget_ = target; | |
| 128 clickAction_ = action; | |
| 129 } | |
| 130 | |
| 131 - (void)drawRect:(NSRect)dirtyRect { | |
| 132 NSImage* image = ([self hoverState] == kHoverStateNone || ![self isEnabled]) ? | |
| 133 [self image] : affordanceImage_.get(); | |
| 134 if (!image) | |
| 135 return; | |
| 136 NSRect imageRect = NSZeroRect; | |
| 137 imageRect.size = [image size]; | |
| 138 NSRect destRect = [self bounds]; | |
| 139 destRect.origin.y = | |
| 140 floor((NSHeight(destRect) / 2) - (NSHeight(imageRect) / 2)); | |
| 141 destRect.size = imageRect.size; | |
| 142 double opaqueness = | |
| 143 fadeAnimation_ ? fadeAnimation_->GetCurrentValue() : 1.0; | |
| 144 if (mediaState_ == TAB_MEDIA_STATE_NONE) | |
| 145 opaqueness = 1.0 - opaqueness; // Fading out, not in. | |
| 146 [image drawInRect:destRect | |
| 147 fromRect:imageRect | |
| 148 operation:NSCompositeSourceOver | |
| 149 fraction:opaqueness | |
| 150 respectFlipped:YES | |
| 151 hints:nil]; | |
| 152 } | |
| 153 | |
| 154 // When disabled, the superview should receive all mouse events. | |
| 155 - (NSView*)hitTest:(NSPoint)aPoint { | |
| 156 if ([self isEnabled] && ![self isHidden]) | |
| 157 return [super hitTest:aPoint]; | |
| 158 else | |
| 159 return nil; | |
| 160 } | |
| 161 | |
| 162 - (void)handleClick:(id)sender { | |
| 163 using base::UserMetricsAction; | |
| 164 | |
| 165 if (mediaState_ == TAB_MEDIA_STATE_AUDIO_PLAYING) | |
| 166 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Mute")); | |
| 167 else if (mediaState_ == TAB_MEDIA_STATE_AUDIO_MUTING) | |
| 168 content::RecordAction(UserMetricsAction("MediaIndicatorButton_Unmute")); | |
| 169 else | |
| 170 NOTREACHED(); | |
| 171 | |
| 172 [clickTarget_ performSelector:clickAction_ withObject:self]; | |
| 173 } | |
| 174 | |
| 175 @end | |
| OLD | NEW |