Index: chrome/browser/ui/cocoa/tabs/media_indicator_button.mm |
diff --git a/chrome/browser/ui/cocoa/tabs/media_indicator_view.mm b/chrome/browser/ui/cocoa/tabs/media_indicator_button.mm |
similarity index 14% |
rename from chrome/browser/ui/cocoa/tabs/media_indicator_view.mm |
rename to chrome/browser/ui/cocoa/tabs/media_indicator_button.mm |
index e3e71cdbcb1d09675e34b21602bb938ffe22a097..87ced8c075eda8dfe07e0a9d841219d81a14eb13 100644 |
--- a/chrome/browser/ui/cocoa/tabs/media_indicator_view.mm |
+++ b/chrome/browser/ui/cocoa/tabs/media_indicator_button.mm |
@@ -2,113 +2,174 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#import "chrome/browser/ui/cocoa/tabs/media_indicator_view.h" |
+#import "chrome/browser/ui/cocoa/tabs/media_indicator_button.h" |
+#include "base/logging.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "content/public/browser/user_metrics.h" |
#include "ui/gfx/animation/animation.h" |
#include "ui/gfx/animation/animation_delegate.h" |
#include "ui/gfx/image/image.h" |
-class MediaIndicatorViewAnimationDelegate : public gfx::AnimationDelegate { |
+@implementation MediaIndicatorButton |
+ |
+class FadeAnimationDelegate : public gfx::AnimationDelegate { |
public: |
- MediaIndicatorViewAnimationDelegate(NSView* view, |
- TabMediaState* mediaState, |
- TabMediaState* animatingMediaState) |
- : view_(view), mediaState_(mediaState), |
- animatingMediaState_(animatingMediaState), |
- doneCallbackObject_(nil), doneCallbackSelector_(nil) {} |
- ~MediaIndicatorViewAnimationDelegate() override {} |
- |
- void SetAnimationDoneCallback(id anObject, SEL selector) { |
- doneCallbackObject_ = anObject; |
- doneCallbackSelector_ = selector; |
- } |
+ explicit FadeAnimationDelegate(MediaIndicatorButton* button) |
+ : button_(button) {} |
+ ~FadeAnimationDelegate() override {} |
- void AnimationEnded(const gfx::Animation* animation) override { |
- *animatingMediaState_ = *mediaState_; |
- [view_ setNeedsDisplay:YES]; |
- [doneCallbackObject_ performSelector:doneCallbackSelector_]; |
- } |
+ private: |
+ // gfx::AnimationDelegate implementation. |
void AnimationProgressed(const gfx::Animation* animation) override { |
- [view_ setNeedsDisplay:YES]; |
+ [button_ setNeedsDisplay:YES]; |
} |
+ |
void AnimationCanceled(const gfx::Animation* animation) override { |
AnimationEnded(animation); |
} |
- private: |
- NSView* const view_; |
- TabMediaState* const mediaState_; |
- TabMediaState* const animatingMediaState_; |
+ void AnimationEnded(const gfx::Animation* animation) override { |
+ button_->showingMediaState_ = button_->mediaState_; |
+ [button_ setNeedsDisplay:YES]; |
+ [button_->animationDoneTarget_ |
+ performSelector:button_->animationDoneAction_]; |
+ } |
- id doneCallbackObject_; |
- SEL doneCallbackSelector_; |
-}; |
+ MediaIndicatorButton* const button_; |
-@implementation MediaIndicatorView |
+ DISALLOW_COPY_AND_ASSIGN(FadeAnimationDelegate); |
+}; |
-@synthesize mediaState = mediaState_; |
-@synthesize animatingMediaState = animatingMediaState_; |
+@synthesize showingMediaState = showingMediaState_; |
- (id)init { |
if ((self = [super initWithFrame:NSZeroRect])) { |
- mediaState_ = animatingMediaState_ = TAB_MEDIA_STATE_NONE; |
- delegate_.reset(new MediaIndicatorViewAnimationDelegate( |
- self, &mediaState_, &animatingMediaState_)); |
+ mediaState_ = TAB_MEDIA_STATE_NONE; |
+ showingMediaState_ = TAB_MEDIA_STATE_NONE; |
+ [self setEnabled:NO]; |
+ [super setTarget:self]; |
+ [super setAction:@selector(handleClick:)]; |
} |
return self; |
} |
-- (void)updateIndicator:(TabMediaState)mediaState { |
- if (mediaState == mediaState_) |
+- (void)removeFromSuperview { |
+ fadeAnimation_.reset(); |
+ [super removeFromSuperview]; |
+} |
+ |
+- (void)transitionToMediaState:(TabMediaState)nextState { |
+ if (nextState == mediaState_) |
return; |
- mediaState_ = mediaState; |
- animation_.reset(); |
- |
- // Prepare this view if the new TabMediaState is an active one. |
- if (mediaState_ != TAB_MEDIA_STATE_NONE) { |
- animatingMediaState_ = mediaState_; |
- NSImage* const image = |
- chrome::GetTabMediaIndicatorImage(mediaState_).ToNSImage(); |
- NSRect frame = [self frame]; |
- frame.size = [image size]; |
- [self setFrame:frame]; |
- [self setImage:image]; |
+ if (nextState != TAB_MEDIA_STATE_NONE) { |
+ [self setImage:chrome::GetTabMediaIndicatorImage(nextState).ToNSImage()]; |
+ affordanceImage_.reset( |
+ [chrome::GetTabMediaIndicatorAffordanceImage(nextState).ToNSImage() |
+ retain]); |
} |
- // If the animation delegate is missing, that means animations were disabled |
- // for testing; so, go directly to animating completion state. |
- if (!delegate_) { |
- animatingMediaState_ = mediaState_; |
- return; |
+ if ((mediaState_ == TAB_MEDIA_STATE_AUDIO_PLAYING && |
+ nextState == TAB_MEDIA_STATE_AUDIO_MUTING) || |
+ (mediaState_ == TAB_MEDIA_STATE_AUDIO_MUTING && |
+ nextState == TAB_MEDIA_STATE_AUDIO_PLAYING) || |
+ (mediaState_ == TAB_MEDIA_STATE_AUDIO_MUTING && |
+ nextState == TAB_MEDIA_STATE_NONE)) { |
Nico
2014/10/31 21:55:00
nit: this looks like model code that should be sha
miu
2014/11/01 00:43:38
Yep. Planning on some cleanup w.r.t. the animatio
|
+ // Instant user feedback: No fade animation. |
+ showingMediaState_ = nextState; |
+ fadeAnimation_.reset(); |
+ } else { |
+ if (nextState == TAB_MEDIA_STATE_NONE) |
+ showingMediaState_ = mediaState_; // Fading-out indicator. |
+ else |
+ showingMediaState_ = nextState; // Fading-in to next indicator. |
+ // gfx::Animation requires a task runner is available for the current |
+ // thread. Generally, only certain unit tests would not instantiate a task |
+ // runner. |
+ if (base::ThreadTaskRunnerHandle::IsSet()) { |
+ fadeAnimation_ = chrome::CreateTabMediaIndicatorFadeAnimation(nextState); |
+ if (!fadeAnimationDelegate_) |
+ fadeAnimationDelegate_.reset(new FadeAnimationDelegate(self)); |
+ fadeAnimation_->set_delegate(fadeAnimationDelegate_.get()); |
+ fadeAnimation_->Start(); |
+ } |
} |
- animation_ = chrome::CreateTabMediaIndicatorFadeAnimation(mediaState_); |
- animation_->set_delegate(delegate_.get()); |
- animation_->Start(); |
+ [self setEnabled:(chrome::IsTabAudioMutingFeatureEnabled() && |
+ (nextState == TAB_MEDIA_STATE_AUDIO_PLAYING || |
+ nextState == TAB_MEDIA_STATE_AUDIO_MUTING))]; |
+ |
+ // An indicator state change should be made visible immediately, instead of |
+ // the user being surprised when their mouse leaves the button. |
+ if ([self hoverState] == kHoverStateMouseOver) |
+ [self setHoverState:kHoverStateNone]; |
+ |
+ mediaState_ = nextState; |
+ |
+ [self setNeedsDisplay:YES]; |
} |
-- (void)setAnimationDoneCallbackObject:(id)anObject withSelector:(SEL)selector { |
- if (delegate_) |
- delegate_->SetAnimationDoneCallback(anObject, selector); |
+- (void)setTarget:(id)aTarget { |
+ NOTREACHED(); // See class-level comments. |
} |
-- (void)drawRect:(NSRect)rect { |
- if (!animation_) |
- return; |
+- (void)setAction:(SEL)anAction { |
+ NOTREACHED(); // See class-level comments. |
+} |
+ |
+- (void)setAnimationDoneTarget:(id)target withAction:(SEL)action { |
+ animationDoneTarget_ = target; |
+ animationDoneAction_ = action; |
+} |
- double opaqueness = animation_->GetCurrentValue(); |
+- (void)setClickTarget:(id)target withAction:(SEL)action { |
+ clickTarget_ = target; |
+ clickAction_ = action; |
+} |
+ |
+- (void)drawRect:(NSRect)dirtyRect { |
+ NSImage* image = ([self hoverState] == kHoverStateNone || ![self isEnabled]) ? |
+ [self image] : affordanceImage_.get(); |
+ if (!image) |
+ return; |
+ NSRect imageRect = NSZeroRect; |
+ imageRect.size = [image size]; |
+ NSRect destRect = [self bounds]; |
+ destRect.origin.y = |
+ floor((NSHeight(destRect) / 2) - (NSHeight(imageRect) / 2)); |
+ destRect.size = imageRect.size; |
+ double opaqueness = |
+ fadeAnimation_ ? fadeAnimation_->GetCurrentValue() : 1.0; |
if (mediaState_ == TAB_MEDIA_STATE_NONE) |
opaqueness = 1.0 - opaqueness; // Fading out, not in. |
+ [image drawInRect:destRect |
+ fromRect:imageRect |
+ operation:NSCompositeSourceOver |
+ fraction:opaqueness |
+ respectFlipped:YES |
+ hints:nil]; |
+} |
- [[self image] drawInRect:[self bounds] |
- fromRect:NSZeroRect |
- operation:NSCompositeSourceOver |
- fraction:opaqueness]; |
+// When disabled, the superview should receive all mouse events. |
+- (NSView*)hitTest:(NSPoint)aPoint { |
+ if ([self isEnabled] && ![self isHidden]) |
+ return [super hitTest:aPoint]; |
+ else |
+ return nil; |
} |
-- (void)disableAnimations { |
- delegate_.reset(); |
+- (void)handleClick:(id)sender { |
+ using base::UserMetricsAction; |
+ |
+ if (mediaState_ == TAB_MEDIA_STATE_AUDIO_PLAYING) |
+ content::RecordAction(UserMetricsAction("MediaIndicatorButton_Mute")); |
+ else if (mediaState_ == TAB_MEDIA_STATE_AUDIO_MUTING) |
+ content::RecordAction(UserMetricsAction("MediaIndicatorButton_Unmute")); |
+ else |
+ NOTREACHED(); |
+ |
+ [clickTarget_ performSelector:clickAction_ withObject:self]; |
} |
@end |