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 |