summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/cocoa
diff options
context:
space:
mode:
authormiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-16 02:10:06 +0000
committermiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-16 02:10:06 +0000
commit84f215956d399005923cc407b16655b16b95c2b7 (patch)
tree22beb9261311cd76ab04620b48660ed239b9ae73 /chrome/browser/ui/cocoa
parent0686352174278600afdcf80398848faa0b6e266b (diff)
downloadchromium_src-84f215956d399005923cc407b16655b16b95c2b7.zip
chromium_src-84f215956d399005923cc407b16655b16b95c2b7.tar.gz
chromium_src-84f215956d399005923cc407b16655b16b95c2b7.tar.bz2
New tab UI media indicator for recording, tab capture, and audio playback.
Implementation of new tab media indicator designed by the UX team. This replaces the existing "throbber animation over favicon" indicators with a simpler, less-distracting static icon (right-aligned). 1. Pulled out all existing recording/capture layout, animation, paint code, and graphics. 2. Modified existing audio playback indicator to become a multi-purpose media indicator. 3. Updated visibility precedence logic for media indicator, per discussion in bug. 4. Refactored duplicated code into tab_utils.h/.cc. Testing: Updated unit tests. Manual look-and-feel testing to confirm correct behavior and animation transition smoothness on all of: Aura, Win non-Aura, Mac, and GTK. BUG=290550 Review URL: https://codereview.chromium.org/26922003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228838 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/cocoa')
-rw-r--r--chrome/browser/ui/cocoa/tabs/media_indicator_view.h44
-rw-r--r--chrome/browser/ui/cocoa/tabs/media_indicator_view.mm100
-rw-r--r--chrome/browser/ui/cocoa/tabs/media_indicator_view_unittest.mm34
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_controller.h13
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_controller.mm121
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm176
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.h30
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.mm62
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_projecting_image_view_unittest.mm51
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_strip_controller.h5
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm126
-rw-r--r--chrome/browser/ui/cocoa/tabs/throbbing_image_view.h47
-rw-r--r--chrome/browser/ui/cocoa/tabs/throbbing_image_view.mm71
-rw-r--r--chrome/browser/ui/cocoa/tabs/throbbing_image_view_unittest.mm40
14 files changed, 336 insertions, 584 deletions
diff --git a/chrome/browser/ui/cocoa/tabs/media_indicator_view.h b/chrome/browser/ui/cocoa/tabs/media_indicator_view.h
new file mode 100644
index 0000000..176e56e
--- /dev/null
+++ b/chrome/browser/ui/cocoa/tabs/media_indicator_view.h
@@ -0,0 +1,44 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_TABS_MEDIA_INDICATOR_VIEW_H_
+#define CHROME_BROWSER_UI_COCOA_TABS_MEDIA_INDICATOR_VIEW_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/ui/tabs/tab_utils.h"
+
+class MediaIndicatorViewAnimationDelegate;
+
+namespace gfx {
+class Animation;
+} // namespace gfx
+
+@interface MediaIndicatorView : NSImageView {
+ @private
+ TabMediaState mediaState_;
+ scoped_ptr<MediaIndicatorViewAnimationDelegate> delegate_;
+ scoped_ptr<gfx::Animation> animation_;
+ TabMediaState animatingMediaState_;
+}
+
+@property(readonly, nonatomic) TabMediaState mediaState;
+@property(readonly, nonatomic) TabMediaState animatingMediaState;
+
+// Initialize a new MediaIndicatorView in TAB_MEDIA_STATE_NONE (i.e., a
+// non-active indicator).
+- (id)init;
+
+// Starts the animation to transition the indicator to the new |mediaState|.
+- (void)updateIndicator:(TabMediaState)mediaState;
+
+@end
+
+@interface MediaIndicatorView(TestingAPI)
+// Turns off animations for logic testing.
+- (void)disableAnimations;
+@end
+
+#endif // CHROME_BROWSER_UI_COCOA_TABS_MEDIA_INDICATOR_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/tabs/media_indicator_view.mm b/chrome/browser/ui/cocoa/tabs/media_indicator_view.mm
new file mode 100644
index 0000000..e3caf07
--- /dev/null
+++ b/chrome/browser/ui/cocoa/tabs/media_indicator_view.mm
@@ -0,0 +1,100 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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"
+
+#include "ui/gfx/animation/animation.h"
+#include "ui/gfx/animation/animation_delegate.h"
+#include "ui/gfx/image/image.h"
+
+class MediaIndicatorViewAnimationDelegate : public gfx::AnimationDelegate {
+ public:
+ MediaIndicatorViewAnimationDelegate(NSView* view,
+ TabMediaState* mediaState,
+ TabMediaState* animatingMediaState)
+ : view_(view), mediaState_(mediaState),
+ animatingMediaState_(animatingMediaState) {}
+ virtual ~MediaIndicatorViewAnimationDelegate() {}
+
+ virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
+ *animatingMediaState_ = *mediaState_;
+ [view_ setNeedsDisplay:YES];
+ }
+ virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
+ [view_ setNeedsDisplay:YES];
+ }
+ virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
+ *animatingMediaState_ = *mediaState_;
+ [view_ setNeedsDisplay:YES];
+ }
+
+ private:
+ NSView* const view_;
+ TabMediaState* const mediaState_;
+ TabMediaState* const animatingMediaState_;
+};
+
+@implementation MediaIndicatorView
+
+@synthesize mediaState = mediaState_;
+@synthesize animatingMediaState = animatingMediaState_;
+
+- (id)init {
+ if ((self = [super initWithFrame:NSZeroRect])) {
+ mediaState_ = animatingMediaState_ = TAB_MEDIA_STATE_NONE;
+ delegate_.reset(new MediaIndicatorViewAnimationDelegate(
+ self, &mediaState_, &animatingMediaState_));
+ }
+ return self;
+}
+
+- (void)updateIndicator:(TabMediaState)mediaState {
+ if (mediaState == 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 the animation delegate is missing, that means animations were disabled
+ // for testing; so, go directly to animating completion state.
+ if (!delegate_) {
+ animatingMediaState_ = mediaState_;
+ return;
+ }
+
+ animation_ = chrome::CreateTabMediaIndicatorFadeAnimation(mediaState_);
+ animation_->set_delegate(delegate_.get());
+ animation_->Start();
+}
+
+- (void)drawRect:(NSRect)rect {
+ if (!animation_)
+ return;
+
+ double opaqueness = animation_->GetCurrentValue();
+ if (mediaState_ == TAB_MEDIA_STATE_NONE)
+ opaqueness = 1.0 - opaqueness; // Fading out, not in.
+
+ [[self image] drawInRect:[self bounds]
+ fromRect:NSZeroRect
+ operation:NSCompositeSourceOver
+ fraction:opaqueness];
+}
+
+- (void)disableAnimations {
+ delegate_.reset();
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/tabs/media_indicator_view_unittest.mm b/chrome/browser/ui/cocoa/tabs/media_indicator_view_unittest.mm
new file mode 100644
index 0000000..f3eb132
--- /dev/null
+++ b/chrome/browser/ui/cocoa/tabs/media_indicator_view_unittest.mm
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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"
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/message_loop/message_loop.h"
+#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+class MediaIndicatorViewTest : public CocoaTest {
+ public:
+ MediaIndicatorViewTest() {
+ view_.reset([[MediaIndicatorView alloc] init]);
+ ASSERT_TRUE(!!view_);
+ EXPECT_EQ(TAB_MEDIA_STATE_NONE, [view_ animatingMediaState]);
+
+ [[test_window() contentView] addSubview:view_.get()];
+
+ [view_ updateIndicator:TAB_MEDIA_STATE_AUDIO_PLAYING];
+ EXPECT_EQ(TAB_MEDIA_STATE_AUDIO_PLAYING, [view_ animatingMediaState]);
+ }
+
+ base::scoped_nsobject<MediaIndicatorView> view_;
+ base::MessageLoopForUI message_loop_; // Needed for gfx::Animation.
+};
+
+TEST_VIEW(MediaIndicatorViewTest, view_)
+
+} // namespace
diff --git a/chrome/browser/ui/cocoa/tabs/tab_controller.h b/chrome/browser/ui/cocoa/tabs/tab_controller.h
index 2cfef89..ad0a6a1 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_controller.h
@@ -20,6 +20,7 @@ enum TabLoadingState {
kTabCrashed,
};
+@class MediaIndicatorView;
@class MenuController;
namespace TabControllerInternal {
class MenuDelegate;
@@ -42,7 +43,7 @@ class MenuDelegate;
@private
base::scoped_nsobject<NSView> iconView_;
base::scoped_nsobject<NSTextField> titleView_;
- base::scoped_nsobject<NSView> audioIndicatorView_;
+ base::scoped_nsobject<MediaIndicatorView> mediaIndicatorView_;
base::scoped_nsobject<HoverCloseButton> closeButton_;
NSRect originalIconFrame_; // frame of iconView_ as loaded from nib
@@ -51,12 +52,10 @@ class MenuDelegate;
BOOL app_;
BOOL mini_;
BOOL pinned_;
- BOOL projecting_;
BOOL active_;
BOOL selected_;
GURL url_;
TabLoadingState loadingState_;
- CGFloat iconTitleXOffset_; // between left edges of icon and title
id<TabControllerTarget> target_; // weak, where actions are sent
SEL action_; // selector sent when tab is selected by clicking
scoped_ptr<ui::SimpleMenuModel> contextMenuModel_;
@@ -70,10 +69,6 @@ class MenuDelegate;
@property(assign, nonatomic) BOOL app;
@property(assign, nonatomic) BOOL mini;
@property(assign, nonatomic) BOOL pinned;
-// A tab is called "projecting" when a video/audio stream of its contents is
-// being captured and perhaps streamed remotely. We add a favicon glow animation
-// in this state to notify the user.
-@property(assign, nonatomic) BOOL projecting;
// Note that |-selected| will return YES if the controller is |-active|, too.
// |-setSelected:| affects the selection, while |-setActive:| affects the key
// status/focus of the content.
@@ -83,7 +78,7 @@ class MenuDelegate;
@property(assign, nonatomic) GURL url;
@property(assign, nonatomic) NSView* iconView;
@property(readonly, nonatomic) NSTextField* titleView;
-@property(assign, nonatomic) NSView* audioIndicatorView;
+@property(assign, nonatomic) MediaIndicatorView* mediaIndicatorView;
@property(readonly, nonatomic) HoverCloseButton* closeButton;
// Minimum and maximum allowable tab width. The minimum width does not show
@@ -123,7 +118,7 @@ class MenuDelegate;
- (NSString*)toolTip;
- (int)iconCapacity;
- (BOOL)shouldShowIcon;
-- (BOOL)shouldShowAudioIndicator;
+- (BOOL)shouldShowMediaIndicator;
- (BOOL)shouldShowCloseButton;
@end // TabController(TestingAPI)
diff --git a/chrome/browser/ui/cocoa/tabs/tab_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_controller.mm
index 8185c7f..ff67592 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_controller.mm
@@ -4,12 +4,14 @@
#import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
+#include <algorithm>
#include <cmath>
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h"
#import "chrome/browser/themes/theme_properties.h"
#import "chrome/browser/themes/theme_service.h"
+#import "chrome/browser/ui/cocoa/tabs/media_indicator_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h"
#import "chrome/browser/ui/cocoa/tabs/tab_view.h"
#import "chrome/browser/ui/cocoa/themed_window.h"
@@ -26,7 +28,6 @@
@synthesize loadingState = loadingState_;
@synthesize mini = mini_;
@synthesize pinned = pinned_;
-@synthesize projecting = projecting_;
@synthesize target = target_;
@synthesize url = url_;
@@ -97,7 +98,6 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
// explicilty save the offset between the title and the close button since
// we can just get that value for the close button's frame.
NSRect titleFrame = NSMakeRect(35, 6, 92, 14);
- iconTitleXOffset_ = NSMinX(titleFrame) - NSMinX(originalIconFrame_);
// Label.
titleView_.reset([[NSTextField alloc] initWithFrame:titleFrame]);
@@ -230,24 +230,8 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
- (void)setIconView:(NSView*)iconView {
[iconView_ removeFromSuperview];
iconView_.reset([iconView retain]);
- if ([self projecting] && [self loadingState] == kTabDone) {
- // When projecting we have bigger iconView to accommodate the glow
- // animation, so this frame should be double the size of a favicon.
- NSRect iconFrame = [iconView frame];
- // Center the iconView given it's double regular size.
- if ([self app] || [self mini]) {
- const CGFloat tabWidth = [self app] ? [TabController appTabWidth]
- : [TabController miniTabWidth];
- iconFrame.origin.x = std::floor((tabWidth - NSWidth(iconFrame)) / 2.0);
- } else {
- iconFrame.origin.x = std::floor(originalIconFrame_.origin.x / 2.0);
- }
-
- iconFrame.origin.y = -std::ceil(originalIconFrame_.origin.y / 2.0);
-
- [iconView_ setFrame:iconFrame];
- } else if ([self app] || [self mini]) {
+ if ([self app] || [self mini]) {
NSRect appIconFrame = [iconView frame];
appIconFrame.origin = originalIconFrame_.origin;
@@ -273,18 +257,16 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
return titleView_;
}
-- (NSView*)audioIndicatorView {
- return audioIndicatorView_;
+- (MediaIndicatorView*)mediaIndicatorView {
+ return mediaIndicatorView_;
}
-- (void)setAudioIndicatorView:(NSView*)audioIndicatorView {
- if (audioIndicatorView == audioIndicatorView_)
- return;
- [audioIndicatorView_ removeFromSuperview];
- audioIndicatorView_.reset([audioIndicatorView retain]);
+- (void)setMediaIndicatorView:(MediaIndicatorView*)mediaIndicatorView {
+ [mediaIndicatorView_ removeFromSuperview];
+ mediaIndicatorView_.reset([mediaIndicatorView retain]);
[self updateVisibility];
- if (audioIndicatorView_)
- [[self view] addSubview:audioIndicatorView_];
+ if (mediaIndicatorView_)
+ [[self view] addSubview:mediaIndicatorView_];
}
- (HoverCloseButton*)closeButton {
@@ -299,58 +281,35 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
// tab. We never actually do this, but it's a helpful guide for determining
// how much space we have available.
- (int)iconCapacity {
- CGFloat width = NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_);
+ const CGFloat availableWidth = std::max<CGFloat>(
+ 0, NSMaxX([closeButton_ frame]) - NSMinX(originalIconFrame_));
+ const CGFloat widthPerIcon = NSWidth(originalIconFrame_);
const int kPaddingBetweenIcons = 2;
- CGFloat iconWidth = NSWidth(originalIconFrame_) + kPaddingBetweenIcons;
-
- return width / iconWidth;
+ if (availableWidth >= widthPerIcon &&
+ availableWidth < (widthPerIcon + kPaddingBetweenIcons)) {
+ return 1;
+ }
+ return availableWidth / (widthPerIcon + kPaddingBetweenIcons);
}
-// Returns YES if we should show the icon. When tabs get too small, we clip
-// the favicon before the close button for selected tabs, and prefer the
-// favicon for unselected tabs. Exception: We clip the favicon before the audio
-// indicator in all cases. The icon can also be suppressed more directly
-// by clearing iconView_.
- (BOOL)shouldShowIcon {
- if (!iconView_)
- return NO;
- const BOOL shouldShowAudioIndicator = [self shouldShowAudioIndicator];
- if ([self mini])
- return !shouldShowAudioIndicator;
- int required_capacity = shouldShowAudioIndicator ? 2 : 1;
- if ([self selected]) {
- // Active tabs give priority to the close button, then the audio indicator,
- // then the favicon.
- ++required_capacity;
- } else {
- // Non-selected tabs give priority to the audio indicator, then the favicon,
- // and finally the close button.
- }
- return [self iconCapacity] >= required_capacity;
+ return chrome::ShouldTabShowFavicon(
+ [self iconCapacity], [self mini], [self selected], iconView_ != nil,
+ !mediaIndicatorView_ ? TAB_MEDIA_STATE_NONE :
+ [mediaIndicatorView_ animatingMediaState]);
}
-// Returns YES if we should show the audio indicator. When tabs get too small,
-// we clip the audio indicator before the close button for selected tabs, and
-// prefer the audio indicator for unselected tabs.
-- (BOOL)shouldShowAudioIndicator {
- if (!audioIndicatorView_)
+- (BOOL)shouldShowMediaIndicator {
+ if (!mediaIndicatorView_)
return NO;
- if ([self mini])
- return YES;
- if ([self selected]) {
- // The active tab clips the audio indicator before the close button.
- return [self iconCapacity] >= 2;
- }
- // Non-selected tabs clip close button before the audio indicator.
- return [self iconCapacity] >= 1;
+ return chrome::ShouldTabShowMediaIndicator(
+ [self iconCapacity], [self mini], [self selected], iconView_ != nil,
+ [mediaIndicatorView_ animatingMediaState]);
}
-// Returns YES if we should be showing the close button. The selected tab
-// always shows the close button.
- (BOOL)shouldShowCloseButton {
- if ([self mini])
- return NO;
- return ([self selected] || [self iconCapacity] >= 3);
+ return chrome::ShouldTabShowCloseButton(
+ [self iconCapacity], [self mini], [self selected]);
}
- (void)updateVisibility {
@@ -369,32 +328,32 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
[closeButton_ setHidden:!newShowCloseButton];
- BOOL newShowAudioIndicator = [self shouldShowAudioIndicator];
+ BOOL newShowMediaIndicator = [self shouldShowMediaIndicator];
- if (audioIndicatorView_) {
- [audioIndicatorView_ setHidden:!newShowAudioIndicator];
+ [mediaIndicatorView_ setHidden:!newShowMediaIndicator];
- NSRect newFrame = [audioIndicatorView_ frame];
+ if (newShowMediaIndicator) {
+ NSRect newFrame = [mediaIndicatorView_ frame];
if ([self app] || [self mini]) {
- // Tab is pinned: Position the audio indicator in the center.
+ // Tab is pinned: Position the media indicator in the center.
const CGFloat tabWidth = [self app] ?
[TabController appTabWidth] : [TabController miniTabWidth];
newFrame.origin.x = std::floor((tabWidth - NSWidth(newFrame)) / 2);
newFrame.origin.y = NSMinY(originalIconFrame_) -
std::floor((NSHeight(newFrame) - NSHeight(originalIconFrame_)) / 2);
} else {
- // The Frame for the audioIndicatorView_ depends on whether iconView_
+ // The Frame for the mediaIndicatorView_ depends on whether iconView_
// and/or closeButton_ are visible, and where they have been positioned.
const NSRect closeButtonFrame = [closeButton_ frame];
newFrame.origin.x = NSMinX(closeButtonFrame);
// Position to the left of the close button when it is showing.
if (newShowCloseButton)
newFrame.origin.x -= NSWidth(newFrame);
- // Audio indicator is centered vertically, with respect to closeButton_.
+ // Media indicator is centered vertically, with respect to closeButton_.
newFrame.origin.y = NSMinY(closeButtonFrame) -
std::floor((NSHeight(newFrame) - NSHeight(closeButtonFrame)) / 2);
}
- [audioIndicatorView_ setFrame:newFrame];
+ [mediaIndicatorView_ setFrame:newFrame];
}
// Adjust the title view based on changes to the icon's and close button's
@@ -405,13 +364,13 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
newTitleFrame.origin.y = oldTitleFrame.origin.y;
if (newShowIcon) {
- newTitleFrame.origin.x = originalIconFrame_.origin.x + iconTitleXOffset_;
+ newTitleFrame.origin.x = NSMaxX([iconView_ frame]);
} else {
newTitleFrame.origin.x = originalIconFrame_.origin.x;
}
- if (newShowAudioIndicator) {
- newTitleFrame.size.width = NSMinX([audioIndicatorView_ frame]) -
+ if (newShowMediaIndicator) {
+ newTitleFrame.size.width = NSMinX([mediaIndicatorView_ frame]) -
newTitleFrame.origin.x;
} else if (newShowCloseButton) {
newTitleFrame.size.width = NSMinX([closeButton_ frame]) -
diff --git a/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm b/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm
index ab580340..b530b32 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm
@@ -7,6 +7,7 @@
#import "base/mac/scoped_nsobject.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
+#import "chrome/browser/ui/cocoa/tabs/media_indicator_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
#import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h"
@@ -117,12 +118,18 @@ class TabControllerTest : public CocoaTest {
const TabController* controller) {
// Check whether subviews should be visible when they are supposed to be,
// given Tab size and TabRendererData state.
+ const TabMediaState indicatorState =
+ [[controller mediaIndicatorView] mediaState];
if ([controller mini]) {
- if ([controller projecting])
+ EXPECT_EQ(1, [controller iconCapacity]);
+ if (indicatorState == TAB_MEDIA_STATE_CAPTURING ||
+ indicatorState == TAB_MEDIA_STATE_RECORDING) {
+ EXPECT_FALSE([controller shouldShowIcon]);
+ EXPECT_TRUE([controller shouldShowMediaIndicator]);
+ } else {
EXPECT_TRUE([controller shouldShowIcon]);
- else
- EXPECT_TRUE([controller shouldShowIcon] !=
- [controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
+ }
EXPECT_FALSE([controller shouldShowCloseButton]);
} else if ([controller selected]) {
EXPECT_TRUE([controller shouldShowCloseButton]);
@@ -130,23 +137,25 @@ class TabControllerTest : public CocoaTest {
case 0:
case 1:
EXPECT_FALSE([controller shouldShowIcon]);
- EXPECT_FALSE([controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
break;
case 2:
- if ([controller projecting])
+ if (indicatorState == TAB_MEDIA_STATE_CAPTURING ||
+ indicatorState == TAB_MEDIA_STATE_RECORDING) {
+ EXPECT_FALSE([controller shouldShowIcon]);
+ EXPECT_TRUE([controller shouldShowMediaIndicator]);
+ } else {
EXPECT_TRUE([controller shouldShowIcon]);
- else
- EXPECT_TRUE([controller shouldShowIcon] !=
- [controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
+ }
break;
default:
EXPECT_LE(3, [controller iconCapacity]);
EXPECT_TRUE([controller shouldShowIcon]);
- if ([controller projecting])
- EXPECT_FALSE([controller shouldShowAudioIndicator]);
+ if (indicatorState != TAB_MEDIA_STATE_NONE)
+ EXPECT_TRUE([controller shouldShowMediaIndicator]);
else
- EXPECT_TRUE(!![controller audioIndicatorView] ==
- [controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
break;
}
} else { // Tab not selected/active and not mini tab.
@@ -154,24 +163,26 @@ class TabControllerTest : public CocoaTest {
case 0:
EXPECT_FALSE([controller shouldShowCloseButton]);
EXPECT_FALSE([controller shouldShowIcon]);
- EXPECT_FALSE([controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
break;
case 1:
EXPECT_FALSE([controller shouldShowCloseButton]);
- if ([controller projecting])
+ if (indicatorState == TAB_MEDIA_STATE_CAPTURING ||
+ indicatorState == TAB_MEDIA_STATE_RECORDING) {
+ EXPECT_FALSE([controller shouldShowIcon]);
+ EXPECT_TRUE([controller shouldShowMediaIndicator]);
+ } else {
EXPECT_TRUE([controller shouldShowIcon]);
- else
- EXPECT_TRUE([controller shouldShowIcon] !=
- [controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
+ }
break;
default:
EXPECT_LE(2, [controller iconCapacity]);
EXPECT_TRUE([controller shouldShowIcon]);
- if ([controller projecting])
- EXPECT_FALSE([controller shouldShowAudioIndicator]);
+ if (indicatorState != TAB_MEDIA_STATE_NONE)
+ EXPECT_TRUE([controller shouldShowMediaIndicator]);
else
- EXPECT_TRUE(!![controller audioIndicatorView] ==
- [controller shouldShowAudioIndicator]);
+ EXPECT_FALSE([controller shouldShowMediaIndicator]);
break;
}
}
@@ -180,9 +191,8 @@ class TabControllerTest : public CocoaTest {
EXPECT_TRUE([controller shouldShowIcon] ==
(!![controller iconView] && ![[controller iconView] isHidden]));
EXPECT_TRUE([controller mini] == [[controller titleView] isHidden]);
- EXPECT_TRUE([controller shouldShowAudioIndicator] ==
- (!![controller audioIndicatorView] &&
- ![[controller audioIndicatorView] isHidden]));
+ EXPECT_TRUE([controller shouldShowMediaIndicator] ==
+ ![[controller mediaIndicatorView] isHidden]);
EXPECT_TRUE([controller shouldShowCloseButton] !=
[[controller closeButton] isHidden]);
@@ -198,22 +208,22 @@ class TabControllerTest : public CocoaTest {
EXPECT_LE(NSMinY(tabFrame), NSMinY(iconFrame));
EXPECT_LE(NSMaxY(iconFrame), NSMaxY(tabFrame));
}
- if ([controller shouldShowIcon] && [controller shouldShowAudioIndicator]) {
+ if ([controller shouldShowIcon] && [controller shouldShowMediaIndicator]) {
EXPECT_LE(NSMaxX([[controller iconView] frame]),
- NSMinX([[controller audioIndicatorView] frame]));
+ NSMinX([[controller mediaIndicatorView] frame]));
}
- if ([controller shouldShowAudioIndicator]) {
- const NSRect audioIndicatorFrame =
- [[controller audioIndicatorView] frame];
+ if ([controller shouldShowMediaIndicator]) {
+ const NSRect mediaIndicatorFrame =
+ [[controller mediaIndicatorView] frame];
if (NSWidth(titleFrame) > 0)
- EXPECT_LE(NSMaxX(titleFrame), NSMinX(audioIndicatorFrame));
- EXPECT_LE(NSMaxX(audioIndicatorFrame), NSMaxX(tabFrame));
- EXPECT_LE(NSMinY(tabFrame), NSMinY(audioIndicatorFrame));
- EXPECT_LE(NSMaxY(audioIndicatorFrame), NSMaxY(tabFrame));
+ EXPECT_LE(NSMaxX(titleFrame), NSMinX(mediaIndicatorFrame));
+ EXPECT_LE(NSMaxX(mediaIndicatorFrame), NSMaxX(tabFrame));
+ EXPECT_LE(NSMinY(tabFrame), NSMinY(mediaIndicatorFrame));
+ EXPECT_LE(NSMaxY(mediaIndicatorFrame), NSMaxY(tabFrame));
}
- if ([controller shouldShowAudioIndicator] &&
+ if ([controller shouldShowMediaIndicator] &&
[controller shouldShowCloseButton]) {
- EXPECT_LE(NSMaxX([[controller audioIndicatorView] frame]),
+ EXPECT_LE(NSMaxX([[controller mediaIndicatorView] frame]),
NSMinX([[controller closeButton] frame]));
}
if ([controller shouldShowCloseButton]) {
@@ -489,64 +499,64 @@ TEST_F(TabControllerTest, TitleViewLayout) {
// relevant combinations of tab state. This test overlaps with parts of the
// other tests above.
TEST_F(TabControllerTest, LayoutAndVisibilityOfSubviews) {
+ static const TabMediaState kMediaStatesToTest[] = {
+ TAB_MEDIA_STATE_NONE, TAB_MEDIA_STATE_CAPTURING,
+ TAB_MEDIA_STATE_AUDIO_PLAYING
+ };
+
NSWindow* const window = test_window();
// Create TabController instance and place its view into the test window.
base::scoped_nsobject<TabController> controller([[TabController alloc] init]);
[[window contentView] addSubview:[controller view]];
- // Create favicon and audio indicator icon views.
+ // Create favicon and media indicator views. Disable animation in the media
+ // indicator view so that TabController's "what should be shown" logic can be
+ // tested effectively. If animations were left enabled, the
+ // shouldShowMediaIndicator method would return true during fade-out
+ // transitions.
base::scoped_nsobject<NSImageView> faviconView(
CreateImageViewFromResourceBundle(IDR_DEFAULT_FAVICON));
- base::scoped_nsobject<NSImageView> audioIndicatorView(
- CreateImageViewFromResourceBundle(IDR_TAB_AUDIO_INDICATOR));
-
- [controller setIconView:faviconView];
+ base::scoped_nsobject<MediaIndicatorView> mediaIndicatorView(
+ [[MediaIndicatorView alloc] init]);
+ [mediaIndicatorView disableAnimations];
+ [controller setMediaIndicatorView:mediaIndicatorView];
// Perform layout over all possible combinations, checking for correct
// results.
- for (int is_mini_tab = 0; is_mini_tab < 2; ++is_mini_tab) {
- for (int is_active_tab = 0; is_active_tab < 2; ++is_active_tab) {
- for (int is_audio_playing = 0; is_audio_playing < 2; ++is_audio_playing) {
- for (int is_capturing = 0; is_capturing < 2; ++is_capturing) {
- SCOPED_TRACE(::testing::Message()
- << (is_active_tab ? "Active" : "Inactive") << ' '
- << (is_mini_tab ? "Mini " : "")
- << "Tab with is_audio_playing=" << !!is_audio_playing
- << " and is_capturing=" << !!is_capturing);
-
- // Simulate what tab_strip_controller would do to set up the
- // TabController state.
- [controller setMini:(is_mini_tab ? YES : NO)];
- [controller setActive:(is_active_tab ? YES : NO)];
- if (is_capturing) {
- [controller setProjecting:YES];
- [controller setAudioIndicatorView:nil];
- } else {
- [controller setProjecting:NO];
- if (is_audio_playing)
- [controller setAudioIndicatorView:audioIndicatorView];
- else
- [controller setAudioIndicatorView:nil];
- }
-
- // Test layout for every width from maximum to minimum.
- NSRect tabFrame = [[controller view] frame];
- int min_width;
- if (is_mini_tab) {
- tabFrame.size.width = min_width = [TabController miniTabWidth];
- } else {
- tabFrame.size.width = [TabController maxTabWidth];
- min_width = is_active_tab ? [TabController minSelectedTabWidth] :
- [TabController minTabWidth];
- }
- while (NSWidth(tabFrame) >= min_width) {
- SCOPED_TRACE(::testing::Message()
- << "width=" << tabFrame.size.width);
- [[controller view] setFrame:tabFrame];
- CheckForExpectedLayoutAndVisibilityOfSubviews(controller);
- --tabFrame.size.width;
- }
+ for (int isMiniTab = 0; isMiniTab < 2; ++isMiniTab) {
+ for (int isActiveTab = 0; isActiveTab < 2; ++isActiveTab) {
+ for (size_t mediaStateIndex = 0;
+ mediaStateIndex < arraysize(kMediaStatesToTest);
+ ++mediaStateIndex) {
+ const TabMediaState mediaState = kMediaStatesToTest[mediaStateIndex];
+ SCOPED_TRACE(::testing::Message()
+ << (isActiveTab ? "Active" : "Inactive") << ' '
+ << (isMiniTab ? "Mini " : "")
+ << "Tab with media indicator state " << mediaState);
+
+ // Simulate what tab_strip_controller would do to set up the
+ // TabController state.
+ [controller setMini:(isMiniTab ? YES : NO)];
+ [controller setActive:(isActiveTab ? YES : NO)];
+ [[controller mediaIndicatorView] updateIndicator:mediaState];
+ [controller setIconView:faviconView];
+
+ // Test layout for every width from maximum to minimum.
+ NSRect tabFrame = [[controller view] frame];
+ int minWidth;
+ if (isMiniTab) {
+ tabFrame.size.width = minWidth = [TabController miniTabWidth];
+ } else {
+ tabFrame.size.width = [TabController maxTabWidth];
+ minWidth = isActiveTab ? [TabController minSelectedTabWidth] :
+ [TabController minTabWidth];
+ }
+ while (NSWidth(tabFrame) >= minWidth) {
+ SCOPED_TRACE(::testing::Message() << "width=" << tabFrame.size.width);
+ [[controller view] setFrame:tabFrame];
+ CheckForExpectedLayoutAndVisibilityOfSubviews(controller);
+ --tabFrame.size.width;
}
}
}
diff --git a/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.h b/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.h
deleted file mode 100644
index ef55d15..0000000
--- a/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_TABS_TAB_PROJECTING_IMAGE_VIEW_H_
-#define CHROME_BROWSER_UI_COCOA_TABS_TAB_PROJECTING_IMAGE_VIEW_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/ui/cocoa/tabs/throbbing_image_view.h"
-
-// ImageView for when a tab is in "projecting" mode. The view is made up of
-// three images: background image (original favicon), projector sheet and an
-// animated glow. This view paints outside the favicon bounds due to the glow.
-@interface TabProjectingImageView : ThrobbingImageView {
- @private
- base::scoped_nsobject<NSImage> projectorImage_;
-}
-
-- (id)initWithFrame:(NSRect)rect
- backgroundImage:(NSImage*)backgroundImage
- projectorImage:(NSImage*)projectorImage
- throbImage:(NSImage*)throbImage
- animationContainer:(gfx::AnimationContainer*)animationContainer;
-
-@end
-
-#endif // CHROME_BROWSER_UI_COCOA_TABS_TAB_PROJECTING_IMAGE_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.mm b/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.mm
deleted file mode 100644
index 2f5d685..0000000
--- a/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.mm
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// 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/tab_projecting_image_view.h"
-
-#include "ui/gfx/animation/animation.h"
-
-@implementation TabProjectingImageView
-
-- (id)initWithFrame:(NSRect)rect
- backgroundImage:(NSImage*)backgroundImage
- projectorImage:(NSImage*)projectorImage
- throbImage:(NSImage*)throbImage
- animationContainer:(gfx::AnimationContainer*)animationContainer {
- if ((self = [super initWithFrame:rect
- backgroundImage:backgroundImage
- throbImage:throbImage
- throbPosition:kThrobPositionOverlay
- animationContainer:animationContainer])) {
- projectorImage_.reset([projectorImage retain]);
- }
- return self;
-}
-
-- (void)drawRect:(NSRect)rect {
- // For projecting mode, we need to draw 3 centered icons of different sizes:
- // - glow: 32x32
- // - projection sheet: 16x16
- // - favicon: 12x12 (0.75*16)
- // Our bounds should be set to 32x32.
- NSRect bounds = [self bounds];
-
- const int faviconWidthAndHeight = bounds.size.width / 2 * 0.75;
- const int faviconX = (bounds.size.width - faviconWidthAndHeight) / 2;
- // Adjustment in y direction because projector screen is thinner at top.
- const int faviconY = faviconX + 1;
- [backgroundImage_ drawInRect:NSMakeRect(faviconX,
- faviconY,
- faviconWidthAndHeight,
- faviconWidthAndHeight)
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:1];
-
- const int projectorWidthAndHeight = bounds.size.width / 2;
- const int projectorXY = (bounds.size.width - projectorWidthAndHeight) / 2;
- [projectorImage_ drawInRect:NSMakeRect(projectorXY,
- projectorXY,
- projectorWidthAndHeight,
- projectorWidthAndHeight)
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:1];
-
- [throbImage_ drawInRect:[self bounds]
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:throbAnimation_->GetCurrentValue()];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view_unittest.mm b/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view_unittest.mm
deleted file mode 100644
index caa49aa..0000000
--- a/chrome/browser/ui/cocoa/tabs/tab_projecting_image_view_unittest.mm
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// 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/tab_projecting_image_view.h"
-
-#include "base/message_loop/message_loop.h"
-#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-namespace {
-
-class TabProjectingImageViewTest : public CocoaTest {
- public:
- TabProjectingImageViewTest() {
- base::scoped_nsobject<NSImage> backgroundImage(
- [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]);
- [backgroundImage lockFocus];
- NSRectFill(NSMakeRect(0, 0, 16, 16));
- [backgroundImage unlockFocus];
-
- base::scoped_nsobject<NSImage> projectorImage(
- [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]);
- [projectorImage lockFocus];
- NSRectFill(NSMakeRect(0, 0, 16, 16));
- [projectorImage unlockFocus];
-
- base::scoped_nsobject<NSImage> throbImage(
- [[NSImage alloc] initWithSize:NSMakeSize(32, 32)]);
- [throbImage lockFocus];
- NSRectFill(NSMakeRect(0, 0, 32, 32));
- [throbImage unlockFocus];
-
- base::scoped_nsobject<TabProjectingImageView> view(
- [[TabProjectingImageView alloc] initWithFrame:NSMakeRect(0, 0, 32, 32)
- backgroundImage:backgroundImage
- projectorImage:projectorImage
- throbImage:throbImage
- animationContainer:NULL]);
- view_ = view.get();
- [[test_window() contentView] addSubview:view_];
- }
-
- base::MessageLoopForUI message_loop_; // Needed for gfx::ThrobAnimation.
- TabProjectingImageView* view_;
-};
-
-TEST_VIEW(TabProjectingImageViewTest, view_)
-
-} // namespace
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h
index a87c5a5..203502e 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h
@@ -27,9 +27,6 @@ class TabStripModel;
namespace content {
class WebContents;
}
-namespace gfx {
-class AnimationContainer;
-}
// The interface for the tab strip controller's delegate.
// Delegating TabStripModelObserverBridge's events (in lieu of directly
@@ -142,8 +139,6 @@ class AnimationContainer;
// Helper for performing tab selection as a result of dragging over a tab.
scoped_ptr<HoverTabSelector> hoverTabSelector_;
-
- scoped_refptr<gfx::AnimationContainer> animationContainer_;
}
@property(nonatomic) CGFloat leftIndentForControls;
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
index 911dd5d..364a353 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
@@ -36,14 +36,13 @@
#import "chrome/browser/ui/cocoa/new_tab_button.h"
#import "chrome/browser/ui/cocoa/tab_contents/favicon_util_mac.h"
#import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
+#import "chrome/browser/ui/cocoa/tabs/media_indicator_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
-#import "chrome/browser/ui/cocoa/tabs/tab_projecting_image_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_model_observer_bridge.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_view.h"
#import "chrome/browser/ui/cocoa/tabs/throbber_view.h"
-#import "chrome/browser/ui/cocoa/tabs/throbbing_image_view.h"
#include "chrome/browser/ui/find_bar/find_bar.h"
#include "chrome/browser/ui/find_bar/find_bar_controller.h"
#include "chrome/browser/ui/find_bar/find_tab_helper.h"
@@ -70,7 +69,6 @@
#include "ui/base/models/list_selection_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
-#include "ui/gfx/animation/animation_container.h"
#include "ui/gfx/image/image.h"
using content::OpenURLParams;
@@ -103,9 +101,6 @@ const CGFloat kNewTabButtonOffset = 8.0;
// Time (in seconds) in which tabs animate to their final position.
const NSTimeInterval kAnimationDuration = 0.125;
-// The width and height of the icon + glow for projecting mode.
-const CGFloat kProjectingIconWidthAndHeight = 32.0;
-
// Helper class for doing NSAnimationContext calls that takes a bool to disable
// all the work. Useful for code that wants to conditionally animate.
class ScopedNSAnimationContextGroup {
@@ -215,39 +210,6 @@ NSImage* ApplyMask(NSImage* image, NSImage* mask) {
}) autorelease];
}
-// Creates a modified favicon used for the recording case. The mask is used for
-// making part of the favicon transparent. (The part where the recording dot
-// later is drawn.)
-NSImage* CreateMaskedFaviconForRecording(NSImage* image,
- NSImage* mask,
- NSImage* recImage) {
- return [CreateImageWithSize([image size], ^(NSSize size) {
- CGFloat width = size.width;
- CGFloat height = size.height;
-
- [image drawAtPoint:NSZeroPoint
- fromRect:NSMakeRect(0, 0, width, height)
- operation:NSCompositeCopy
- fraction:1.0];
-
- NSSize maskSize = [mask size];
- NSSize recImageSize = [recImage size];
- CGFloat offsetFromRight = recImageSize.width +
- (maskSize.width - recImageSize.width) / 2;
- CGFloat offsetFromBottom = (maskSize.height - recImageSize.height) / 2;
-
- NSRect maskBounds;
- maskBounds.origin.x = width - offsetFromRight;
- maskBounds.origin.y = -offsetFromBottom;
- maskBounds.size = maskSize;
-
- [mask drawInRect:maskBounds
- fromRect:NSZeroRect
- operation:NSCompositeDestinationOut
- fraction:1.0];
- }) autorelease];
-}
-
// Paints |overlay| on top of |ground|.
NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
DCHECK_EQ([ground size].width, [overlay size].width);
@@ -274,8 +236,8 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
- (void)regenerateSubviewList;
- (NSInteger)indexForContentsView:(NSView*)view;
- (NSImageView*)iconImageViewForContents:(content::WebContents*)contents;
-- (void)updateFaviconForContents:(content::WebContents*)contents
- atIndex:(NSInteger)modelIndex;
+- (void)updateIconsForContents:(content::WebContents*)contents
+ atIndex:(NSInteger)modelIndex;
- (void)layoutTabsWithAnimation:(BOOL)animate
regenerateSubviews:(BOOL)doUpdate;
- (void)animationDidStopForController:(TabController*)controller
@@ -494,7 +456,6 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
[[TabStripDragController alloc] initWithTabStripController:self]);
tabContentsArray_.reset([[NSMutableArray alloc] init]);
tabArray_.reset([[NSMutableArray alloc] init]);
- animationContainer_ = new gfx::AnimationContainer;
NSWindow* browserWindow = [view window];
// Important note: any non-tab subviews not added to |permanentSubviews_|
@@ -1312,7 +1273,6 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
0, -[[self class] defaultTabHeight])];
[self setTabTitle:newController withContents:contents];
- [newController setProjecting:chrome::ShouldShowProjectingIndicator(contents)];
// If a tab is being inserted, we can again use the entire tab strip width
// for layout.
@@ -1330,7 +1290,7 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
// dragging a tab out into a new window, we have to put the tab's favicon
// into the right state up front as we won't be told to do it from anywhere
// else.
- [self updateFaviconForContents:contents atIndex:modelIndex];
+ [self updateIconsForContents:contents atIndex:modelIndex];
}
// Called before |contents| is deactivated.
@@ -1563,8 +1523,8 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
// Updates the current loading state, replacing the icon view with a favicon,
// a throbber, the default icon, or nothing at all.
-- (void)updateFaviconForContents:(content::WebContents*)contents
- atIndex:(NSInteger)modelIndex {
+- (void)updateIconsForContents:(content::WebContents*)contents
+ atIndex:(NSInteger)modelIndex {
if (!contents)
return;
@@ -1612,63 +1572,19 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
if (newState == kTabDone || oldState != newState ||
oldHasIcon != newHasIcon) {
NSView* iconView = nil;
- NSImageView* audioIndicatorView = nil;
if (newHasIcon) {
if (newState == kTabDone) {
- NSImageView* imageView = [self iconImageViewForContents:contents];
-
- ui::ThemeProvider* theme = [[tabStripView_ window] themeProvider];
- if (theme && [tabController projecting]) {
- NSImage* projectorGlow =
- theme->GetNSImageNamed(IDR_TAB_CAPTURE_GLOW);
- NSImage* projector = theme->GetNSImageNamed(IDR_TAB_CAPTURE);
-
- NSRect frame = NSMakeRect(0,
- 0,
- kProjectingIconWidthAndHeight,
- kProjectingIconWidthAndHeight);
- TabProjectingImageView* projectingView =
- [[[TabProjectingImageView alloc]
- initWithFrame:frame
- backgroundImage:[imageView image]
- projectorImage:projector
- throbImage:projectorGlow
- animationContainer:animationContainer_.get()] autorelease];
-
- iconView = projectingView;
- } else if (theme && chrome::ShouldShowRecordingIndicator(contents)) {
- // Create a masked favicon.
- NSImage* mask = theme->GetNSImageNamed(IDR_TAB_RECORDING_MASK);
- NSImage* recording = theme->GetNSImageNamed(IDR_TAB_RECORDING);
- NSImage* favIconMasked = CreateMaskedFaviconForRecording(
- [imageView image], mask, recording);
-
- NSRect frame =
- NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
- ThrobbingImageView* recordingView =
- [[[ThrobbingImageView alloc]
- initWithFrame:frame
- backgroundImage:favIconMasked
- throbImage:recording
- throbPosition:kThrobPositionBottomRight
- animationContainer:animationContainer_.get()] autorelease];
-
- iconView = recordingView;
- } else {
- iconView = imageView;
-
- if (theme && chrome::IsPlayingAudio(contents)) {
- NSImage* const image =
- theme->GetNSImageNamed(IDR_TAB_AUDIO_INDICATOR);
- if (image) {
- NSRect frame;
- frame.size = [image size];
- audioIndicatorView =
- [[[NSImageView alloc] initWithFrame:frame] autorelease];
- [audioIndicatorView setImage:image];
- }
- }
+ iconView = [self iconImageViewForContents:contents];
+ const TabMediaState mediaState =
+ chrome::GetTabMediaStateForContents(contents);
+ // Create MediaIndicatorView upon first use.
+ if (mediaState != TAB_MEDIA_STATE_NONE &&
+ ![tabController mediaIndicatorView]) {
+ MediaIndicatorView* const mediaIndicatorView =
+ [[[MediaIndicatorView alloc] init] autorelease];
+ [tabController setMediaIndicatorView:mediaIndicatorView];
}
+ [[tabController mediaIndicatorView] updateIndicator:mediaState];
} else if (newState == kTabCrashed) {
NSImage* oldImage = [[self iconImageViewForContents:contents] image];
NSRect frame =
@@ -1676,6 +1592,8 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
iconView = [ThrobberView toastThrobberViewWithFrame:frame
beforeImage:oldImage
afterImage:sadFaviconImage];
+ [[tabController mediaIndicatorView]
+ updateIndicator:TAB_MEDIA_STATE_NONE];
} else {
NSRect frame =
NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
@@ -1685,7 +1603,7 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
}
[tabController setIconView:iconView];
- if (iconView && ![tabController projecting]) {
+ if (iconView) {
// See the comment above kTabOverlap for why these DCHECKs exist.
DCHECK_GE(NSMinX([iconView frame]), kTabOverlap);
// TODO(thakis): Ideally, this would be true too, but it's not true in
@@ -1693,7 +1611,6 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
//DCHECK_LE(NSMaxX([iconView frame]),
// NSWidth([[tabController view] frame]) - kTabOverlap);
}
- [tabController setAudioIndicatorView:audioIndicatorView];
}
}
@@ -1719,9 +1636,8 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
if (change != TabStripModelObserver::LOADING_ONLY)
[self setTabTitle:tabController withContents:contents];
- [tabController setProjecting:chrome::ShouldShowProjectingIndicator(contents)];
- [self updateFaviconForContents:contents atIndex:modelIndex];
+ [self updateIconsForContents:contents atIndex:modelIndex];
TabContentsController* updatedController =
[tabContentsArray_ objectAtIndex:index];
@@ -1776,7 +1692,7 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) {
[tabController setPinned:tabStripModel_->IsTabPinned(modelIndex)];
[tabController setApp:tabStripModel_->IsAppTab(modelIndex)];
[tabController setUrl:contents->GetURL()];
- [self updateFaviconForContents:contents atIndex:modelIndex];
+ [self updateIconsForContents:contents atIndex:modelIndex];
// If the tab is being restored and it's pinned, the mini state is set after
// the tab has already been rendered, so re-layout the tabstrip. In all other
// cases, the state is set before the tab is rendered so this isn't needed.
diff --git a/chrome/browser/ui/cocoa/tabs/throbbing_image_view.h b/chrome/browser/ui/cocoa/tabs/throbbing_image_view.h
deleted file mode 100644
index 075d941..0000000
--- a/chrome/browser/ui/cocoa/tabs/throbbing_image_view.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_TABS_THROBBING_IMAGE_VIEW_H_
-#define CHROME_BROWSER_UI_COCOA_TABS_THROBBING_IMAGE_VIEW_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/memory/scoped_ptr.h"
-
-class ThrobbingImageViewAnimationDelegate;
-
-namespace gfx {
-class Animation;
-class AnimationContainer;
-} // namespace gfx
-
-// Where to position the throb image. For the overlay position, the throb image
-// will be drawn with the same size as the background image. For the bottom
-// right position, it will have its original size.
-enum ThrobPosition {
- kThrobPositionOverlay,
- kThrobPositionBottomRight
-};
-
-@interface ThrobbingImageView : NSView {
- @protected
- base::scoped_nsobject<NSImage> backgroundImage_;
- base::scoped_nsobject<NSImage> throbImage_;
- scoped_ptr<gfx::Animation> throbAnimation_;
-
- @private
- scoped_ptr<ThrobbingImageViewAnimationDelegate> delegate_;
- ThrobPosition throbPosition_;
-}
-
-- (id)initWithFrame:(NSRect)rect
- backgroundImage:(NSImage*)backgroundImage
- throbImage:(NSImage*)throbImage
- throbPosition:(ThrobPosition)throbPosition
- animationContainer:(gfx::AnimationContainer*)animationContainer;
-
-@end
-
-#endif // CHROME_BROWSER_UI_COCOA_TABS_THROBBING_IMAGE_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/tabs/throbbing_image_view.mm b/chrome/browser/ui/cocoa/tabs/throbbing_image_view.mm
deleted file mode 100644
index e5de04ac..0000000
--- a/chrome/browser/ui/cocoa/tabs/throbbing_image_view.mm
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// 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/throbbing_image_view.h"
-
-#include "chrome/browser/ui/tabs/tab_utils.h"
-#include "ui/gfx/animation/animation.h"
-#include "ui/gfx/animation/animation_delegate.h"
-
-class ThrobbingImageViewAnimationDelegate : public gfx::AnimationDelegate {
- public:
- ThrobbingImageViewAnimationDelegate(NSView* view) : view_(view) {}
- virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
- [view_ setNeedsDisplay:YES];
- }
- private:
- NSView* view_; // weak
-};
-
-@implementation ThrobbingImageView
-
-- (id)initWithFrame:(NSRect)rect
- backgroundImage:(NSImage*)backgroundImage
- throbImage:(NSImage*)throbImage
- throbPosition:(ThrobPosition)throbPosition
- animationContainer:(gfx::AnimationContainer*)animationContainer {
- if ((self = [super initWithFrame:rect])) {
- backgroundImage_.reset([backgroundImage retain]);
- throbImage_.reset([throbImage retain]);
-
- delegate_.reset(new ThrobbingImageViewAnimationDelegate(self));
-
- throbAnimation_ = chrome::CreateTabRecordingIndicatorAnimation();
- throbAnimation_->set_delegate(delegate_.get());
- throbAnimation_->SetContainer(animationContainer);
- throbAnimation_->Start();
-
- throbPosition_ = throbPosition;
- }
- return self;
-}
-
-- (void)dealloc {
- throbAnimation_->Stop();
- [super dealloc];
-}
-
-- (void)drawRect:(NSRect)rect {
- [backgroundImage_ drawInRect:[self bounds]
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:1];
-
- NSRect b = [self bounds];
- NSRect throbImageBounds;
- if (throbPosition_ == kThrobPositionBottomRight) {
- NSSize throbImageSize = [throbImage_ size];
- throbImageBounds.origin = b.origin;
- throbImageBounds.origin.x += NSWidth(b) - throbImageSize.width;
- throbImageBounds.size = throbImageSize;
- } else {
- throbImageBounds = b;
- }
- [throbImage_ drawInRect:throbImageBounds
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:throbAnimation_->GetCurrentValue()];
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/tabs/throbbing_image_view_unittest.mm b/chrome/browser/ui/cocoa/tabs/throbbing_image_view_unittest.mm
deleted file mode 100644
index 5ce2b13..0000000
--- a/chrome/browser/ui/cocoa/tabs/throbbing_image_view_unittest.mm
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// 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/throbbing_image_view.h"
-
-#include "base/message_loop/message_loop.h"
-#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-namespace {
-
-class ThrobbingImageViewTest : public CocoaTest {
- public:
- ThrobbingImageViewTest() {
- base::scoped_nsobject<NSImage> image(
- [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]);
- [image lockFocus];
- NSRectFill(NSMakeRect(0, 0, 16, 16));
- [image unlockFocus];
-
- base::scoped_nsobject<ThrobbingImageView> view(
- [[ThrobbingImageView alloc] initWithFrame:NSMakeRect(0, 0, 16, 16)
- backgroundImage:image
- throbImage:image
- throbPosition:kThrobPositionOverlay
- animationContainer:NULL]);
- view_ = view.get();
- [[test_window() contentView] addSubview:view_];
- }
-
- base::MessageLoopForUI message_loop_; // Needed for gfx::ThrobAnimation.
- ThrobbingImageView* view_;
-};
-
-TEST_VIEW(ThrobbingImageViewTest, view_)
-
-} // namespace
-