Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2952)

Unified Diff: chrome/browser/ui/cocoa/tabs/media_indicator_button.mm

Issue 688523002: [Cocoa] Tab audio mute control, behind a switch (off by default). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Prevent TabStripController from unconditionally causing creation of MediaIndicatorButton. Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « chrome/browser/ui/cocoa/tabs/media_indicator_button.h ('k') | chrome/browser/ui/cocoa/tabs/media_indicator_button_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698