| 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)) {
|
| + // 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
|
|
|