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

Side by Side Diff: chrome/browser/ui/cocoa/tabs/tab_controller.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, 1 month 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" 5 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/i18n/rtl.h" 10 #include "base/i18n/rtl.h"
11 #include "base/mac/bundle_locations.h" 11 #include "base/mac/bundle_locations.h"
12 #include "base/mac/mac_util.h" 12 #include "base/mac/mac_util.h"
13 #import "chrome/browser/themes/theme_properties.h" 13 #import "chrome/browser/themes/theme_properties.h"
14 #import "chrome/browser/themes/theme_service.h" 14 #import "chrome/browser/themes/theme_service.h"
15 #import "chrome/browser/ui/cocoa/sprite_view.h" 15 #import "chrome/browser/ui/cocoa/sprite_view.h"
16 #import "chrome/browser/ui/cocoa/tabs/media_indicator_view.h" 16 #import "chrome/browser/ui/cocoa/tabs/media_indicator_button.h"
17 #import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h" 17 #import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h"
18 #import "chrome/browser/ui/cocoa/tabs/tab_view.h" 18 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
19 #import "chrome/browser/ui/cocoa/themed_window.h" 19 #import "chrome/browser/ui/cocoa/themed_window.h"
20 #include "content/public/browser/user_metrics.h"
20 #import "extensions/common/extension.h" 21 #import "extensions/common/extension.h"
21 #import "ui/base/cocoa/menu_controller.h" 22 #import "ui/base/cocoa/menu_controller.h"
22 23
23 @implementation TabController 24 @implementation TabController
24 25
25 @synthesize action = action_; 26 @synthesize action = action_;
26 @synthesize app = app_; 27 @synthesize app = app_;
27 @synthesize loadingState = loadingState_; 28 @synthesize loadingState = loadingState_;
28 @synthesize mini = mini_; 29 @synthesize mini = mini_;
29 @synthesize pinned = pinned_; 30 @synthesize pinned = pinned_;
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 selector:@selector(themeChangedNotification:) 121 selector:@selector(themeChangedNotification:)
121 name:kBrowserThemeDidChangeNotification 122 name:kBrowserThemeDidChangeNotification
122 object:nil]; 123 object:nil];
123 124
124 [self internalSetSelected:selected_]; 125 [self internalSetSelected:selected_];
125 } 126 }
126 return self; 127 return self;
127 } 128 }
128 129
129 - (void)dealloc { 130 - (void)dealloc {
130 [mediaIndicatorView_ setAnimationDoneCallbackObject:nil withSelector:nil];
131 [[NSNotificationCenter defaultCenter] removeObserver:self]; 131 [[NSNotificationCenter defaultCenter] removeObserver:self];
132 [[self tabView] setController:nil]; 132 [[self tabView] setController:nil];
133 [super dealloc]; 133 [super dealloc];
134 } 134 }
135 135
136 // The internals of |-setSelected:| and |-setActive:| but doesn't set the 136 // The internals of |-setSelected:| and |-setActive:| but doesn't set the
137 // backing variables. This updates the drawing state and marks self as needing 137 // backing variables. This updates the drawing state and marks self as needing
138 // a re-draw. 138 // a re-draw.
139 - (void)internalSetSelected:(BOOL)selected { 139 - (void)internalSetSelected:(BOOL)selected {
140 TabView* tabView = [self tabView]; 140 TabView* tabView = [self tabView];
(...skipping 15 matching lines...) Expand all
156 new TabControllerInternal::MenuDelegate(target_, self)); 156 new TabControllerInternal::MenuDelegate(target_, self));
157 contextMenuModel_.reset( 157 contextMenuModel_.reset(
158 [target_ contextMenuModelForController:self 158 [target_ contextMenuModelForController:self
159 menuDelegate:contextMenuDelegate_.get()]); 159 menuDelegate:contextMenuDelegate_.get()]);
160 contextMenuController_.reset( 160 contextMenuController_.reset(
161 [[MenuController alloc] initWithModel:contextMenuModel_.get() 161 [[MenuController alloc] initWithModel:contextMenuModel_.get()
162 useWithPopUpButtonCell:NO]); 162 useWithPopUpButtonCell:NO]);
163 return [contextMenuController_ menu]; 163 return [contextMenuController_ menu];
164 } 164 }
165 165
166 - (void)toggleMute:(id)sender {
167 if ([[self target] respondsToSelector:@selector(toggleMute:)]) {
168 [[self target] performSelector:@selector(toggleMute:)
169 withObject:[self view]];
170 }
171 }
172
166 - (void)closeTab:(id)sender { 173 - (void)closeTab:(id)sender {
174 using base::UserMetricsAction;
175
176 if (mediaIndicatorButton_ && ![mediaIndicatorButton_ isHidden]) {
177 if ([mediaIndicatorButton_ isEnabled]) {
178 content::RecordAction(UserMetricsAction("CloseTab_MuteToggleAvailable"));
179 } else if ([mediaIndicatorButton_ showingMediaState] ==
180 TAB_MEDIA_STATE_AUDIO_PLAYING) {
181 content::RecordAction(UserMetricsAction("CloseTab_AudioIndicator"));
182 } else {
183 content::RecordAction(UserMetricsAction("CloseTab_RecordingIndicator"));
184 }
185 } else {
186 content::RecordAction(UserMetricsAction("CloseTab_NoMediaIndicator"));
187 }
188
167 if ([[self target] respondsToSelector:@selector(closeTab:)]) { 189 if ([[self target] respondsToSelector:@selector(closeTab:)]) {
168 [[self target] performSelector:@selector(closeTab:) 190 [[self target] performSelector:@selector(closeTab:)
169 withObject:[self view]]; 191 withObject:[self view]];
170 } 192 }
171 } 193 }
172 194
173 - (void)selectTab:(id)sender { 195 - (void)selectTab:(id)sender {
174 if ([[self tabView] isClosing]) 196 if ([[self tabView] isClosing])
175 return; 197 return;
176 if ([[self target] respondsToSelector:[self action]]) { 198 if ([[self target] respondsToSelector:[self action]]) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 } 245 }
224 246
225 - (void)setIconView:(SpriteView*)iconView { 247 - (void)setIconView:(SpriteView*)iconView {
226 [iconView_ removeFromSuperview]; 248 [iconView_ removeFromSuperview];
227 iconView_.reset([iconView retain]); 249 iconView_.reset([iconView retain]);
228 250
229 if (iconView_) 251 if (iconView_)
230 [[self view] addSubview:iconView_]; 252 [[self view] addSubview:iconView_];
231 } 253 }
232 254
233 - (MediaIndicatorView*)mediaIndicatorView { 255 - (MediaIndicatorButton*)mediaIndicatorButton {
234 return mediaIndicatorView_; 256 return mediaIndicatorButton_;
235 } 257 }
236 258
237 - (void)setMediaIndicatorView:(MediaIndicatorView*)mediaIndicatorView { 259 - (void)setMediaState:(TabMediaState)mediaState {
238 [mediaIndicatorView_ removeFromSuperview]; 260 if (!mediaIndicatorButton_ && mediaState != TAB_MEDIA_STATE_NONE) {
239 mediaIndicatorView_.reset([mediaIndicatorView retain]); 261 mediaIndicatorButton_.reset([[MediaIndicatorButton alloc] init]);
240 [self updateVisibility]; 262 [self updateVisibility]; // Do layout and visibility before adding subview.
241 if (mediaIndicatorView_) { 263 [[self view] addSubview:mediaIndicatorButton_];
242 [[self view] addSubview:mediaIndicatorView_]; 264 [mediaIndicatorButton_ setAnimationDoneTarget:self
243 [mediaIndicatorView_ 265 withAction:@selector(updateVisibility)];
244 setAnimationDoneCallbackObject:self 266 [mediaIndicatorButton_ setClickTarget:self
245 withSelector:@selector(updateVisibility)]; 267 withAction:@selector(toggleMute:)];
246
247 } 268 }
269 [mediaIndicatorButton_ transitionToMediaState:mediaState];
248 } 270 }
249 271
250 - (HoverCloseButton*)closeButton { 272 - (HoverCloseButton*)closeButton {
251 return closeButton_; 273 return closeButton_;
252 } 274 }
253 275
254 - (NSString*)toolTip { 276 - (NSString*)toolTip {
255 return [[self tabView] toolTipText]; 277 return [[self tabView] toolTipText];
256 } 278 }
257 279
258 // Return a rough approximation of the number of icons we could fit in the 280 // Return a rough approximation of the number of icons we could fit in the
259 // tab. We never actually do this, but it's a helpful guide for determining 281 // tab. We never actually do this, but it's a helpful guide for determining
260 // how much space we have available. 282 // how much space we have available.
261 - (int)iconCapacity { 283 - (int)iconCapacity {
262 const CGFloat availableWidth = std::max<CGFloat>( 284 const CGFloat availableWidth = std::max<CGFloat>(
263 0, NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_)); 285 0, NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_));
264 const CGFloat widthPerIcon = NSWidth(originalIconFrame_); 286 const CGFloat widthPerIcon = NSWidth(originalIconFrame_);
265 const int kPaddingBetweenIcons = 2; 287 const int kPaddingBetweenIcons = 2;
266 if (availableWidth >= widthPerIcon && 288 if (availableWidth >= widthPerIcon &&
267 availableWidth < (widthPerIcon + kPaddingBetweenIcons)) { 289 availableWidth < (widthPerIcon + kPaddingBetweenIcons)) {
268 return 1; 290 return 1;
269 } 291 }
270 return availableWidth / (widthPerIcon + kPaddingBetweenIcons); 292 return availableWidth / (widthPerIcon + kPaddingBetweenIcons);
271 } 293 }
272 294
273 - (BOOL)shouldShowIcon { 295 - (BOOL)shouldShowIcon {
274 return chrome::ShouldTabShowFavicon( 296 return chrome::ShouldTabShowFavicon(
275 [self iconCapacity], [self mini], [self active], iconView_ != nil, 297 [self iconCapacity], [self mini], [self active], iconView_ != nil,
276 !mediaIndicatorView_ ? TAB_MEDIA_STATE_NONE : 298 !mediaIndicatorButton_ ? TAB_MEDIA_STATE_NONE :
277 [mediaIndicatorView_ animatingMediaState]); 299 [mediaIndicatorButton_ showingMediaState]);
278 } 300 }
279 301
280 - (BOOL)shouldShowMediaIndicator { 302 - (BOOL)shouldShowMediaIndicator {
281 if (!mediaIndicatorView_)
282 return NO;
283 return chrome::ShouldTabShowMediaIndicator( 303 return chrome::ShouldTabShowMediaIndicator(
284 [self iconCapacity], [self mini], [self active], iconView_ != nil, 304 [self iconCapacity], [self mini], [self active], iconView_ != nil,
285 [mediaIndicatorView_ animatingMediaState]); 305 !mediaIndicatorButton_ ? TAB_MEDIA_STATE_NONE :
306 [mediaIndicatorButton_ showingMediaState]);
286 } 307 }
287 308
288 - (BOOL)shouldShowCloseButton { 309 - (BOOL)shouldShowCloseButton {
289 return chrome::ShouldTabShowCloseButton( 310 return chrome::ShouldTabShowCloseButton(
290 [self iconCapacity], [self mini], [self active]); 311 [self iconCapacity], [self mini], [self active]);
291 } 312 }
292 313
293 - (void)setIconImage:(NSImage*)image { 314 - (void)setIconImage:(NSImage*)image {
294 [self setIconImage:image withToastAnimation:NO]; 315 [self setIconImage:image withToastAnimation:NO];
295 } 316 }
(...skipping 18 matching lines...) Expand all
314 : [TabController miniTabWidth]; 335 : [TabController miniTabWidth];
315 336
316 // Center the icon. 337 // Center the icon.
317 appIconFrame.origin.x = 338 appIconFrame.origin.x =
318 std::floor((tabWidth - NSWidth(appIconFrame)) / 2.0); 339 std::floor((tabWidth - NSWidth(appIconFrame)) / 2.0);
319 [iconView_ setFrame:appIconFrame]; 340 [iconView_ setFrame:appIconFrame];
320 } else { 341 } else {
321 [iconView_ setFrame:originalIconFrame_]; 342 [iconView_ setFrame:originalIconFrame_];
322 } 343 }
323 } 344 }
324 // Ensure that the icon is suppressed if no icon is set or if the tab is too
325 // narrow to display one.
326 [self updateVisibility];
327 } 345 }
328 346
329 - (void)updateVisibility { 347 - (void)updateVisibility {
330 // iconView_ may have been replaced or it may be nil, so [iconView_ isHidden] 348 // iconView_ may have been replaced or it may be nil, so [iconView_ isHidden]
331 // won't work. Instead, the state of the icon is tracked separately in 349 // won't work. Instead, the state of the icon is tracked separately in
332 // isIconShowing_. 350 // isIconShowing_.
333 BOOL newShowIcon = [self shouldShowIcon]; 351 BOOL newShowIcon = [self shouldShowIcon];
334 352
335 [iconView_ setHidden:!newShowIcon]; 353 [iconView_ setHidden:!newShowIcon];
336 isIconShowing_ = newShowIcon; 354 isIconShowing_ = newShowIcon;
337 355
338 // If the tab is a mini-tab, hide the title. 356 // If the tab is a mini-tab, hide the title.
339 TabView* tabView = [self tabView]; 357 TabView* tabView = [self tabView];
340 [tabView setTitleHidden:[self mini]]; 358 [tabView setTitleHidden:[self mini]];
341 359
342 BOOL newShowCloseButton = [self shouldShowCloseButton]; 360 BOOL newShowCloseButton = [self shouldShowCloseButton];
343 361
344 [closeButton_ setHidden:!newShowCloseButton]; 362 [closeButton_ setHidden:!newShowCloseButton];
345 363
346 BOOL newShowMediaIndicator = [self shouldShowMediaIndicator]; 364 BOOL newShowMediaIndicator = [self shouldShowMediaIndicator];
347 365
348 [mediaIndicatorView_ setHidden:!newShowMediaIndicator]; 366 [mediaIndicatorButton_ setHidden:!newShowMediaIndicator];
349 367
350 if (newShowMediaIndicator) { 368 if (newShowMediaIndicator) {
351 NSRect newFrame = [mediaIndicatorView_ frame]; 369 NSRect newFrame = [mediaIndicatorButton_ frame];
370 newFrame.size = [[mediaIndicatorButton_ image] size];
352 if ([self app] || [self mini]) { 371 if ([self app] || [self mini]) {
353 // Tab is pinned: Position the media indicator in the center. 372 // Tab is pinned: Position the media indicator in the center.
354 const CGFloat tabWidth = [self app] ? 373 const CGFloat tabWidth = [self app] ?
355 [TabController appTabWidth] : [TabController miniTabWidth]; 374 [TabController appTabWidth] : [TabController miniTabWidth];
356 newFrame.origin.x = std::floor((tabWidth - NSWidth(newFrame)) / 2); 375 newFrame.origin.x = std::floor((tabWidth - NSWidth(newFrame)) / 2);
357 newFrame.origin.y = NSMinY(originalIconFrame_) - 376 newFrame.origin.y = NSMinY(originalIconFrame_) -
358 std::floor((NSHeight(newFrame) - NSHeight(originalIconFrame_)) / 2); 377 std::floor((NSHeight(newFrame) - NSHeight(originalIconFrame_)) / 2);
359 } else { 378 } else {
360 // The Frame for the mediaIndicatorView_ depends on whether iconView_ 379 // The Frame for the mediaIndicatorButton_ depends on whether iconView_
361 // and/or closeButton_ are visible, and where they have been positioned. 380 // and/or closeButton_ are visible, and where they have been positioned.
362 const NSRect closeButtonFrame = [closeButton_ frame]; 381 const NSRect closeButtonFrame = [closeButton_ frame];
363 newFrame.origin.x = NSMinX(closeButtonFrame); 382 newFrame.origin.x = NSMinX(closeButtonFrame);
364 // Position to the left of the close button when it is showing. 383 // Position to the left of the close button when it is showing.
365 if (newShowCloseButton) 384 if (newShowCloseButton)
366 newFrame.origin.x -= NSWidth(newFrame); 385 newFrame.origin.x -= NSWidth(newFrame);
367 // Media indicator is centered vertically, with respect to closeButton_. 386 // Media indicator is centered vertically, with respect to closeButton_.
368 newFrame.origin.y = NSMinY(closeButtonFrame) - 387 newFrame.origin.y = NSMinY(closeButtonFrame) -
369 std::floor((NSHeight(newFrame) - NSHeight(closeButtonFrame)) / 2); 388 std::floor((NSHeight(newFrame) - NSHeight(closeButtonFrame)) / 2);
370 } 389 }
371 [mediaIndicatorView_ setFrame:newFrame]; 390 [mediaIndicatorButton_ setFrame:newFrame];
372 } 391 }
373 392
374 // Adjust the title view based on changes to the icon's and close button's 393 // Adjust the title view based on changes to the icon's and close button's
375 // visibility. 394 // visibility.
376 NSRect oldTitleFrame = [tabView titleFrame]; 395 NSRect oldTitleFrame = [tabView titleFrame];
377 NSRect newTitleFrame; 396 NSRect newTitleFrame;
378 newTitleFrame.size.height = oldTitleFrame.size.height; 397 newTitleFrame.size.height = oldTitleFrame.size.height;
379 newTitleFrame.origin.y = oldTitleFrame.origin.y; 398 newTitleFrame.origin.y = oldTitleFrame.origin.y;
380 399
381 if (newShowIcon) { 400 if (newShowIcon) {
382 newTitleFrame.origin.x = NSMaxX([iconView_ frame]); 401 newTitleFrame.origin.x = NSMaxX([iconView_ frame]);
383 } else { 402 } else {
384 newTitleFrame.origin.x = originalIconFrame_.origin.x; 403 newTitleFrame.origin.x = originalIconFrame_.origin.x;
385 } 404 }
386 405
387 if (newShowMediaIndicator) { 406 if (newShowMediaIndicator) {
388 newTitleFrame.size.width = NSMinX([mediaIndicatorView_ frame]) - 407 newTitleFrame.size.width = NSMinX([mediaIndicatorButton_ frame]) -
389 newTitleFrame.origin.x; 408 newTitleFrame.origin.x;
390 } else if (newShowCloseButton) { 409 } else if (newShowCloseButton) {
391 newTitleFrame.size.width = NSMinX([closeButton_ frame]) - 410 newTitleFrame.size.width = NSMinX([closeButton_ frame]) -
392 newTitleFrame.origin.x; 411 newTitleFrame.origin.x;
393 } else { 412 } else {
394 newTitleFrame.size.width = NSMaxX([closeButton_ frame]) - 413 newTitleFrame.size.width = NSMaxX([closeButton_ frame]) -
395 newTitleFrame.origin.x; 414 newTitleFrame.origin.x;
396 } 415 }
397 416
398 [tabView setTitleFrame:newTitleFrame]; 417 [tabView setTitleFrame:newTitleFrame];
(...skipping 27 matching lines...) Expand all
426 // TabStripDragController. 445 // TabStripDragController.
427 - (BOOL)tabCanBeDragged:(TabController*)controller { 446 - (BOOL)tabCanBeDragged:(TabController*)controller {
428 return [[target_ dragController] tabCanBeDragged:controller]; 447 return [[target_ dragController] tabCanBeDragged:controller];
429 } 448 }
430 449
431 - (void)maybeStartDrag:(NSEvent*)event forTab:(TabController*)tab { 450 - (void)maybeStartDrag:(NSEvent*)event forTab:(TabController*)tab {
432 [[target_ dragController] maybeStartDrag:event forTab:tab]; 451 [[target_ dragController] maybeStartDrag:event forTab:tab];
433 } 452 }
434 453
435 @end 454 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698