Index: chrome/browser/ui/views/tabs/tab.cc |
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc |
index b7871eb591b2508780431457ce9aa04408b20ebd..2492f4ebdff81f02c01645b8c17e45e0b66413f5 100644 |
--- a/chrome/browser/ui/views/tabs/tab.cc |
+++ b/chrome/browser/ui/views/tabs/tab.cc |
@@ -465,6 +465,7 @@ Tab::Tab(TabController* controller) |
tab_activated_with_last_gesture_begin_(false), |
hover_controller_(this), |
showing_icon_(false), |
+ showing_audio_indicator_(false), |
showing_close_button_(false), |
close_button_color_(0) { |
InitTabResources(); |
@@ -492,8 +493,6 @@ Tab::Tab(TabController* controller) |
AddChildView(close_button_); |
set_context_menu_controller(this); |
- |
- tab_audio_indicator_.reset(new TabAudioIndicator(this)); |
} |
Tab::~Tab() { |
@@ -502,7 +501,6 @@ Tab::~Tab() { |
void Tab::set_animation_container(gfx::AnimationContainer* container) { |
animation_container_ = container; |
hover_controller_.SetAnimationContainer(container); |
- tab_audio_indicator_->SetAnimationContainer(container); |
} |
bool Tab::IsActive() const { |
@@ -556,8 +554,6 @@ void Tab::SetData(const TabRendererData& data) { |
} |
} |
- tab_audio_indicator_->SetIsPlayingAudio(data_.AudioActive()); |
- |
DataChanged(old); |
Layout(); |
@@ -679,16 +675,6 @@ int Tab::GetImmersiveHeight() { |
} |
//////////////////////////////////////////////////////////////////////////////// |
-// Tab, TabAudioIndicator::Delegate overrides: |
- |
-void Tab::ScheduleAudioIndicatorPaint() { |
- // No need to schedule a paint if another animation is active. The other |
- // animation will do its own scheduling. |
- if (!icon_animation_) |
- ScheduleIconPaint(); |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
// Tab, AnimationDelegate overrides: |
void Tab::AnimationProgressed(const gfx::Animation* animation) { |
@@ -781,20 +767,7 @@ void Tab::Layout() { |
int favicon_left = lb.x(); |
favicon_bounds_.SetRect(favicon_left, favicon_top, |
tab_icon_size(), tab_icon_size()); |
- if (data().mini && width() < kMiniTabRendererAsNormalTabWidth) { |
- // Adjust the location of the favicon when transitioning from a normal |
- // tab to a mini-tab. |
- int mini_delta = kMiniTabRendererAsNormalTabWidth - GetMiniWidth(); |
- int ideal_delta = width() - GetMiniWidth(); |
- if (ideal_delta < mini_delta) { |
- int ideal_x = (GetMiniWidth() - tab_icon_size()) / 2; |
- int x = favicon_bounds_.x() + static_cast<int>( |
- (1 - static_cast<float>(ideal_delta) / |
- static_cast<float>(mini_delta)) * |
- (ideal_x - favicon_bounds_.x())); |
- favicon_bounds_.set_x(x); |
- } |
- } |
+ MaybeAdjustLeftForMiniTab(&favicon_bounds_); |
} else { |
favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0); |
} |
@@ -831,6 +804,23 @@ void Tab::Layout() { |
close_button_->SetVisible(false); |
} |
+ showing_audio_indicator_ = ShouldShowAudioIndicator(); |
+ if (showing_audio_indicator_) { |
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
+ gfx::ImageSkia audio_indicator_image( |
+ *rb.GetImageSkiaNamed(IDR_TAB_AUDIO_INDICATOR)); |
+ const int top = |
+ top_padding() + (content_height - audio_indicator_image.height()) / 2; |
+ const int right = showing_close_button_ ? |
+ close_button_->x() + close_button_->GetInsets().left() : lb.right(); |
+ const int left = std::max(lb.x(), right - audio_indicator_image.width()); |
+ audio_indicator_bounds_.SetRect(left, top, |
+ tab_icon_size(), tab_icon_size()); |
+ MaybeAdjustLeftForMiniTab(&audio_indicator_bounds_); |
+ } else { |
+ audio_indicator_bounds_.SetRect(lb.x(), lb.y(), 0, 0); |
+ } |
+ |
const int title_text_offset = is_host_desktop_type_ash ? |
kTitleTextOffsetYAsh : kTitleTextOffsetY; |
int title_left = favicon_bounds_.right() + kFaviconTitleSpacing; |
@@ -847,16 +837,19 @@ void Tab::Layout() { |
title_top -= (text_height - minimum_size.height()) / 2; |
int title_width; |
- if (close_button_->visible()) { |
+ if (showing_audio_indicator_) { |
+ title_width = audio_indicator_bounds_.x() - kTitleCloseButtonSpacing - |
+ title_left; |
+ } else if (close_button_->visible()) { |
// The close button has an empty border with some padding (see details |
// above where the close-button's bounds is set). Allow the title to |
// overlap the empty padding. |
- title_width = std::max(close_button_->x() + |
- close_button_->GetInsets().left() - |
- kTitleCloseButtonSpacing - title_left, 0); |
+ title_width = close_button_->x() + close_button_->GetInsets().left() - |
+ kTitleCloseButtonSpacing - title_left; |
} else { |
- title_width = std::max(lb.width() - title_left, 0); |
+ title_width = lb.width() - title_left; |
} |
+ title_width = std::max(title_width, 0); |
title_bounds_.SetRect(title_left, title_top, title_width, font_height_); |
} else { |
title_bounds_.SetRect(title_left, title_top, 0, 0); |
@@ -1077,6 +1070,17 @@ const gfx::Rect& Tab::GetIconBounds() const { |
return favicon_bounds_; |
} |
+void Tab::MaybeAdjustLeftForMiniTab(gfx::Rect* bounds) const { |
+ if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) |
+ return; |
+ const int mini_delta = kMiniTabRendererAsNormalTabWidth - GetMiniWidth(); |
+ const int ideal_delta = width() - GetMiniWidth(); |
+ const int ideal_x = (GetMiniWidth() - bounds->width()) / 2; |
+ bounds->set_x(bounds->x() + static_cast<int>( |
+ (1 - static_cast<float>(ideal_delta) / static_cast<float>(mini_delta)) * |
+ (ideal_x - bounds->x()))); |
+} |
+ |
void Tab::DataChanged(const TabRendererData& old) { |
if (data().blocked == old.blocked) |
return; |
@@ -1090,9 +1094,13 @@ void Tab::DataChanged(const TabRendererData& old) { |
void Tab::PaintTab(gfx::Canvas* canvas) { |
// See if the model changes whether the icons should be painted. |
const bool show_icon = ShouldShowIcon(); |
+ const bool show_audio_indicator = ShouldShowAudioIndicator(); |
const bool show_close_button = ShouldShowCloseBox(); |
- if (show_icon != showing_icon_ || show_close_button != showing_close_button_) |
+ if (show_icon != showing_icon_ || |
+ show_audio_indicator != showing_audio_indicator_ || |
+ show_close_button != showing_close_button_) { |
Layout(); |
+ } |
PaintTabBackground(canvas); |
@@ -1107,6 +1115,9 @@ void Tab::PaintTab(gfx::Canvas* canvas) { |
if (show_icon) |
PaintIcon(canvas); |
+ if (show_audio_indicator) |
+ PaintAudioIndicator(canvas); |
+ |
// If the close button color has changed, generate a new one. |
if (!close_button_color_ || title_color != close_button_color_) { |
close_button_color_ = title_color; |
@@ -1457,13 +1468,6 @@ void Tab::PaintIcon(gfx::Canvas* canvas) { |
data().favicon.width(), |
data().favicon.height(), |
bounds, true, SkPaint()); |
- } else if (!icon_animation_ && tab_audio_indicator_->IsAnimating()) { |
- // Draw the audio indicator UI only if no other icon animation is |
- // active. |
- if (!icon_animation_ && tab_audio_indicator_->IsAnimating()) { |
- tab_audio_indicator_->set_favicon(data().favicon); |
- tab_audio_indicator_->Paint(canvas, bounds); |
- } |
} else { |
DrawIconCenter(canvas, data().favicon, 0, |
data().favicon.width(), |
@@ -1573,6 +1577,21 @@ void Tab::PaintCaptureState(gfx::Canvas* canvas, gfx::Rect bounds) { |
} |
} |
+void Tab::PaintAudioIndicator(gfx::Canvas* canvas) { |
+ if (audio_indicator_bounds_.IsEmpty()) |
+ return; |
+ |
+ gfx::Rect bounds = audio_indicator_bounds_; |
+ bounds.set_x(GetMirroredXForRect(bounds)); |
+ |
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
+ const gfx::ImageSkia audio_indicator_image( |
+ *rb.GetImageSkiaNamed(IDR_TAB_AUDIO_INDICATOR)); |
+ DrawIconAtLocation(canvas, audio_indicator_image, 0, |
+ bounds.x(), bounds.y(), audio_indicator_image.width(), |
+ audio_indicator_image.height(), true, SkPaint()); |
+} |
+ |
void Tab::PaintTitle(gfx::Canvas* canvas, SkColor title_color) { |
// Paint the Title. |
const gfx::Rect& title_bounds = GetTitleBounds(); |
@@ -1652,19 +1671,44 @@ void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, |
int Tab::IconCapacity() const { |
if (height() < GetMinimumUnselectedSize().height()) |
return 0; |
- return (width() - left_padding() - right_padding()) / tab_icon_size(); |
+ const int kPaddingBetweenIcons = 2; |
+ return (width() - left_padding() - right_padding()) / |
+ (tab_icon_size() + kPaddingBetweenIcons); |
} |
bool Tab::ShouldShowIcon() const { |
+ if (!data().show_icon) |
+ return false; |
+ const bool should_show_audio_indicator = ShouldShowAudioIndicator(); |
+ if (data().mini && height() >= GetMinimumUnselectedSize().height()) { |
+ // Audio indicator always takes precendence over the favicon for mini tabs. |
+ return !should_show_audio_indicator; |
+ } |
+ int required_capacity = should_show_audio_indicator ? 2 : 1; |
+ if (IsActive()) { |
+ // Active tabs give priority to the close button, then the audio indicator, |
+ // then the favicon. |
+ ++required_capacity; |
+ } else { |
+ // Non-active tabs give priority to the audio indicator, then the favicon, |
+ // and finally the close button. |
+ } |
+ return IconCapacity() >= required_capacity; |
+} |
+ |
+bool Tab::ShouldShowAudioIndicator() const { |
+ // Note: If the capture indicator is active, then do not show the audio |
+ // indicator. This allows the favicon "throbber" animation to be shown in |
+ // small-width situations. |
+ if (!data().AudioActive() || data().CaptureActive()) |
+ return false; |
if (data().mini && height() >= GetMinimumUnselectedSize().height()) |
return true; |
- if (!data().show_icon) { |
- return false; |
- } else if (IsActive()) { |
- // The active tab clips favicon before close button. |
+ if (IsActive()) { |
+ // The active tab clips the audio indicator before the close button. |
return IconCapacity() >= 2; |
} |
- // Non-selected tabs clip close button before favicon. |
+ // Non-active tabs clip close button before the audio indicator. |
return IconCapacity() >= 1; |
} |