summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tabs/tab_renderer.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/tabs/tab_renderer.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/tabs/tab_renderer.cc')
-rw-r--r--chrome/browser/tabs/tab_renderer.cc691
1 files changed, 691 insertions, 0 deletions
diff --git a/chrome/browser/tabs/tab_renderer.cc b/chrome/browser/tabs/tab_renderer.cc
new file mode 100644
index 0000000..5bfbcf0
--- /dev/null
+++ b/chrome/browser/tabs/tab_renderer.cc
@@ -0,0 +1,691 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/tabs/tab_renderer.h"
+
+#include "base/gfx/image_operations.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "generated_resources.h"
+
+static const int kLeftPadding = 16;
+static const int kTopPadding = 6;
+static const int kRightPadding = 15;
+static const int kBottomPadding = 5;
+static const int kFavIconTitleSpacing = 4;
+static const int kTitleCloseButtonSpacing = 5;
+static const int kStandardTitleWidth = 175;
+static const int kCloseButtonVertFuzz = 0;
+static const int kCloseButtonHorzFuzz = 5;
+static const int kFaviconSize = 16;
+static const int kSelectedTitleColor = SK_ColorBLACK;
+static const int kUnselectedTitleColor = SkColorSetRGB(64, 64, 64);
+
+// How long the hover state takes.
+static const int kHoverDurationMs = 90;
+
+// How opaque to make the hover state (out of 1).
+static const double kHoverOpacity = 0.33;
+static const double kHoverOpacityVista = 0.7;
+
+// TODO(beng): (Cleanup) This stuff should move onto the class.
+static ChromeFont title_font;
+static int title_font_height = 0;
+static SkBitmap* close_button_n = NULL;
+static SkBitmap* close_button_h = NULL;
+static SkBitmap* close_button_p = NULL;
+static int close_button_height = 0;
+static int close_button_width = 0;
+static SkBitmap* tab_active_l = NULL;
+static SkBitmap* tab_active_c = NULL;
+static SkBitmap* tab_active_r = NULL;
+static int tab_active_l_width = 0;
+static int tab_active_r_width = 0;
+static SkBitmap* tab_inactive_l = NULL;
+static SkBitmap* tab_inactive_c = NULL;
+static SkBitmap* tab_inactive_r = NULL;
+static SkBitmap* tab_inactive_otr_l = NULL;
+static SkBitmap* tab_inactive_otr_c = NULL;
+static SkBitmap* tab_inactive_otr_r = NULL;
+static SkBitmap* tab_hover_l = NULL;
+static SkBitmap* tab_hover_c = NULL;
+static SkBitmap* tab_hover_r = NULL;
+static int tab_inactive_l_width = 0;
+static int tab_inactive_r_width = 0;
+static SkBitmap* waiting_animation_frames = NULL;
+static SkBitmap* loading_animation_frames = NULL;
+static SkBitmap* crashed_fav_icon = NULL;
+static int loading_animation_frame_count = 0;
+static int waiting_animation_frame_count = 0;
+static int waiting_to_loading_frame_count_ratio = 0;
+static SkBitmap* download_icon = NULL;
+static int download_icon_width = 0;
+static int download_icon_height = 0;
+
+namespace {
+
+void InitResources() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ title_font = rb.GetFont(ResourceBundle::BaseFont);
+ title_font_height = title_font.height();
+
+ close_button_n = rb.GetBitmapNamed(IDR_TAB_CLOSE);
+ close_button_h = rb.GetBitmapNamed(IDR_TAB_CLOSE_H);
+ close_button_p = rb.GetBitmapNamed(IDR_TAB_CLOSE_P);
+ close_button_width = close_button_n->width();
+ close_button_height = close_button_n->height();
+
+ tab_active_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT);
+ tab_active_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER);
+ tab_active_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT);
+ tab_active_l_width = tab_active_l->width();
+ tab_active_r_width = tab_active_r->width();
+
+ if (win_util::ShouldUseVistaFrame()) {
+ tab_inactive_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT_V);
+ tab_inactive_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER_V);
+ tab_inactive_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT_V);
+
+ // Our Vista frame doesn't change background color to show OTR,
+ // so we continue to use the existing background tabs.
+ tab_inactive_otr_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT_V);
+ tab_inactive_otr_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER_V);
+ tab_inactive_otr_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT_V);
+ } else {
+ tab_inactive_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT);
+ tab_inactive_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER);
+ tab_inactive_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT);
+
+ tab_inactive_otr_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT_OTR);
+ tab_inactive_otr_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER_OTR);
+ tab_inactive_otr_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT_OTR);
+ }
+
+ tab_hover_l = rb.GetBitmapNamed(IDR_TAB_HOVER_LEFT);
+ tab_hover_c = rb.GetBitmapNamed(IDR_TAB_HOVER_CENTER);
+ tab_hover_r = rb.GetBitmapNamed(IDR_TAB_HOVER_RIGHT);
+
+ tab_inactive_l_width = tab_inactive_l->width();
+ tab_inactive_r_width = tab_inactive_r->width();
+
+ // The loading animation image is a strip of states. Each state must be
+ // square, so the height must divide the width evenly.
+ loading_animation_frames = rb.GetBitmapNamed(IDR_THROBBER);
+ DCHECK(loading_animation_frames);
+ DCHECK(loading_animation_frames->width() %
+ loading_animation_frames->height() == 0);
+ loading_animation_frame_count =
+ loading_animation_frames->width() / loading_animation_frames->height();
+
+ waiting_animation_frames = rb.GetBitmapNamed(IDR_THROBBER_WAITING);
+ DCHECK(waiting_animation_frames);
+ DCHECK(waiting_animation_frames->width() %
+ waiting_animation_frames->height() == 0);
+ waiting_animation_frame_count =
+ waiting_animation_frames->width() / waiting_animation_frames->height();
+
+ waiting_to_loading_frame_count_ratio =
+ waiting_animation_frame_count / loading_animation_frame_count;
+
+ crashed_fav_icon = rb.GetBitmapNamed(IDR_SAD_FAVICON);
+
+ download_icon = rb.GetBitmapNamed(IDR_DOWNLOAD_ICON);
+ download_icon_width = download_icon->width();
+ download_icon_height = download_icon->height();
+
+ initialized = true;
+ }
+}
+
+int GetContentHeight() {
+ // The height of the content of the Tab is the largest of the favicon,
+ // the title text and the close button graphic.
+ int content_height = std::max(kFaviconSize, title_font_height);
+ return std::max(content_height, close_button_height);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabCloseButton
+//
+// This is a Button subclass that causes middle clicks to be forwarded to the
+// parent View by explicitly not handling them in OnMousePressed.
+class TabCloseButton : public ChromeViews::Button {
+ public:
+ TabCloseButton() : Button() {}
+ virtual ~TabCloseButton() {}
+
+ virtual bool OnMousePressed(const ChromeViews::MouseEvent& event) {
+ return !event.IsOnlyMiddleMouseButton();
+ }
+
+ // We need to let the parent know about mouse state so that it
+ // can highlight itself appropriately. Note that Exit events
+ // fire before Enter events, so this works.
+ virtual void OnMouseEntered(const ChromeViews::MouseEvent& event) {
+ BaseButton::OnMouseEntered(event);
+ GetParent()->OnMouseEntered(event);
+ }
+
+ virtual void OnMouseExited(const ChromeViews::MouseEvent& event) {
+ BaseButton::OnMouseExited(event);
+ GetParent()->OnMouseExited(event);
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TabCloseButton);
+};
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// FaviconCrashAnimation
+//
+// A custom animation subclass to manage the favicon crash animation.
+class TabRenderer::FavIconCrashAnimation : public Animation,
+ public AnimationDelegate {
+ public:
+ explicit FavIconCrashAnimation(TabRenderer* target)
+ : Animation(1000, 25, this),
+ target_(target) {
+ }
+ virtual ~FavIconCrashAnimation() {}
+
+ // Animation overrides:
+ virtual void AnimateToState(double state) {
+ const double kHidingOffset = 27;
+
+ if (state < .5) {
+ target_->SetFavIconHidingOffset(
+ static_cast<int>(floor(kHidingOffset * 2.0 * state)));
+ } else {
+ target_->DisplayCrashedFavIcon();
+ target_->SetFavIconHidingOffset(
+ static_cast<int>(
+ floor(kHidingOffset - ((state - .5) * 2.0 * kHidingOffset))));
+ }
+ }
+
+ // AnimationDelegate overrides:
+ virtual void AnimationCanceled(const Animation* animation) {
+ target_->SetFavIconHidingOffset(0);
+ }
+
+ private:
+ TabRenderer* target_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FavIconCrashAnimation);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// TabRenderer, public:
+
+TabRenderer::TabRenderer()
+ : animation_state_(ANIMATION_NONE),
+ animation_frame_(0),
+ showing_icon_(false),
+ showing_download_icon_(false),
+ showing_close_button_(false),
+ crash_animation_(NULL),
+ fav_icon_hiding_offset_(0),
+ should_display_crashed_favicon_(false) {
+ InitResources();
+
+ // Add the Close Button.
+ close_button_ = new TabCloseButton;
+ close_button_->SetImage(ChromeViews::Button::BS_NORMAL, close_button_n);
+ close_button_->SetImage(ChromeViews::Button::BS_HOT, close_button_h);
+ close_button_->SetImage(ChromeViews::Button::BS_PUSHED, close_button_p);
+ AddChildView(close_button_);
+
+ hover_animation_.reset(new SlideAnimation(this));
+ hover_animation_->SetSlideDuration(kHoverDurationMs);
+}
+
+TabRenderer::~TabRenderer() {
+ delete crash_animation_;
+}
+
+void TabRenderer::UpdateData(TabContents* contents) {
+ DCHECK(contents);
+ data_.favicon = contents->GetFavIcon();
+ data_.title = contents->GetTitle();
+ data_.loading = contents->is_loading();
+ data_.off_the_record = contents->profile()->IsOffTheRecord();
+ data_.show_icon = contents->ShouldDisplayFavIcon();
+ data_.show_download_icon = contents->IsDownloadShelfVisible();
+ data_.crashed = contents->IsCrashed();
+}
+
+void TabRenderer::UpdateFromModel() {
+ // Force a layout, since the tab may have grown a favicon.
+ Layout();
+ SchedulePaint();
+
+ if (data_.crashed) {
+ if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation())
+ StartCrashAnimation();
+ } else {
+ if (IsPerformingCrashAnimation())
+ StopCrashAnimation();
+ ResetCrashedFavIcon();
+ }
+}
+
+bool TabRenderer::IsSelected() const {
+ return true;
+}
+
+void TabRenderer::ValidateLoadingAnimation(AnimationState animation_state) {
+ if (animation_state_ != animation_state) {
+ // The waiting animation is the reverse of the loading animation, but at a
+ // different rate - the following reverses and scales the animation_frame_
+ // so that the frame is at an equivalent position when going from one
+ // animation to the other.
+ if (animation_state_ == ANIMATION_WAITING &&
+ animation_state == ANIMATION_LOADING) {
+ animation_frame_ = loading_animation_frame_count -
+ (animation_frame_ / waiting_to_loading_frame_count_ratio);
+ }
+ animation_state_ = animation_state;
+ }
+
+ if (animation_state_ != ANIMATION_NONE) {
+ animation_frame_ = ++animation_frame_ %
+ ((animation_state_ == ANIMATION_WAITING) ?
+ waiting_animation_frame_count :
+ loading_animation_frame_count);
+ } else {
+ animation_frame_ = 0;
+ }
+
+ SchedulePaint();
+}
+
+void TabRenderer::AnimationProgressed(const Animation* animation) {
+ if (animation == hover_animation_.get()) {
+ SchedulePaint();
+ }
+}
+
+// static
+gfx::Size TabRenderer::GetMinimumSize() {
+ InitResources();
+
+ gfx::Size minimum_size;
+ minimum_size.set_width(kLeftPadding + kRightPadding);
+ // Since we use bitmap images, the real minimum height of the image is
+ // defined most accurately by the height of the end cap images.
+ minimum_size.set_height(tab_active_l->height());
+ return minimum_size;
+}
+
+// static
+gfx::Size TabRenderer::GetMinimumSelectedSize() {
+ gfx::Size minimum_size = GetMinimumSize();
+ minimum_size.set_width(kLeftPadding + kFaviconSize + kRightPadding);
+ return minimum_size;
+}
+
+// static
+gfx::Size TabRenderer::GetStandardSize() {
+ gfx::Size standard_size = GetMinimumSize();
+ standard_size.set_width(
+ standard_size.width() + kFavIconTitleSpacing + kStandardTitleWidth);
+ return standard_size;
+}
+
+// static
+void TabRenderer::FormatTitleForDisplay(std::wstring* title) {
+ size_t current_index = 0;
+ size_t match_index;
+ while ((match_index = title->find(L'\n', current_index)) !=
+ std::wstring::npos) {
+ title->replace(match_index, 1, L"");
+ current_index = match_index;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabRenderer, protected:
+
+std::wstring TabRenderer::GetTitle() const {
+ return data_.title;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabRenderer, ChromeViews::View overrides:
+
+void TabRenderer::Paint(ChromeCanvas* canvas) {
+ // Don't paint if we're narrower than we can render correctly. (This should
+ // only happen during animations).
+ if (GetWidth() < GetMinimumSize().width())
+ return;
+
+ // See if the model changes whether the icons should be painted.
+ const bool show_icon = ShouldShowIcon();
+ const bool show_download_icon = data_.show_download_icon;
+ const bool show_close_button = ShouldShowCloseBox();
+ if (show_icon != showing_icon_ ||
+ show_download_icon != showing_download_icon_ ||
+ show_close_button != showing_close_button_)
+ Layout();
+
+ if (IsSelected()) {
+ // Sometimes detaching a tab quickly can result in the model reporting it
+ // as not being selected, so is_drag_clone_ ensures that we always paint
+ // the active representation for the dragged tab.
+ PaintActiveTabBackground(canvas);
+ } else {
+ // Draw our hover state.
+ if (hover_animation_->GetCurrentValue() > 0) {
+ PaintHoverTabBackground(canvas, hover_animation_->GetCurrentValue() *
+ (win_util::ShouldUseVistaFrame() ?
+ kHoverOpacityVista : kHoverOpacity));
+ } else {
+ PaintInactiveTabBackground(canvas);
+ }
+ }
+
+ // Paint the loading animation if the page is currently loading, otherwise
+ // show the page's favicon.
+ if (show_icon) {
+ if (animation_state_ != ANIMATION_NONE) {
+ PaintLoadingAnimation(canvas);
+ } else {
+ canvas->save();
+ canvas->ClipRectInt(0, 0, GetWidth(), GetHeight() - 4);
+ if (should_display_crashed_favicon_) {
+ canvas->DrawBitmapInt(*crashed_fav_icon, 0, 0,
+ crashed_fav_icon->width(),
+ crashed_fav_icon->height(),
+ favicon_bounds_.x(),
+ favicon_bounds_.y() + fav_icon_hiding_offset_,
+ kFaviconSize, kFaviconSize,
+ true);
+ } else {
+ if (!data_.favicon.isNull()) {
+ canvas->DrawBitmapInt(data_.favicon, 0, 0,
+ data_.favicon.width(),
+ data_.favicon.height(),
+ favicon_bounds_.x(),
+ favicon_bounds_.y() + fav_icon_hiding_offset_,
+ kFaviconSize, kFaviconSize,
+ true);
+ }
+ }
+ canvas->restore();
+ }
+ }
+
+ if (show_download_icon) {
+ canvas->DrawBitmapInt(*download_icon,
+ download_icon_bounds_.x(), download_icon_bounds_.y());
+ }
+
+ // Paint the Title.
+ std::wstring title = data_.title;
+ if (title.empty()) {
+ if (data_.loading) {
+ title = l10n_util::GetString(IDS_TAB_LOADING_TITLE);
+ } else {
+ title = l10n_util::GetString(IDS_TAB_UNTITLED_TITLE);
+ }
+ } else {
+ FormatTitleForDisplay(&title);
+ }
+
+ SkColor title_color = IsSelected() ? kSelectedTitleColor
+ : kUnselectedTitleColor;
+ canvas->DrawStringInt(title, title_font, title_color, title_bounds_.x(),
+ title_bounds_.y(), title_bounds_.width(),
+ title_bounds_.height());
+}
+
+void TabRenderer::Layout() {
+ CRect lb;
+ GetLocalBounds(&lb, false);
+ if (lb.IsRectEmpty())
+ return;
+
+ lb.left += kLeftPadding;
+ lb.top += kTopPadding;
+ lb.bottom -= kBottomPadding;
+ lb.right -= kRightPadding;
+
+ // First of all, figure out who is tallest.
+ int content_height = GetContentHeight();
+
+ // Size the Favicon.
+ showing_icon_ = ShouldShowIcon();
+ if (showing_icon_) {
+ int favicon_top = kTopPadding + (content_height - kFaviconSize) / 2;
+ favicon_bounds_.SetRect(lb.left, favicon_top, kFaviconSize, kFaviconSize);
+ } else {
+ favicon_bounds_.SetRect(lb.left, lb.top, 0, 0);
+ }
+
+ // Size the download icon.
+ showing_download_icon_ = data_.show_download_icon;
+ if (showing_download_icon_) {
+ int icon_top = kTopPadding + (content_height - download_icon_height) / 2;
+ download_icon_bounds_.SetRect(lb.Width() - download_icon_width, icon_top,
+ download_icon_width, download_icon_height);
+ }
+
+ // Size the Close button.
+ showing_close_button_ = ShouldShowCloseBox();
+ if (showing_close_button_) {
+ int close_button_top =
+ kTopPadding + kCloseButtonVertFuzz +
+ (content_height - close_button_height) / 2;
+ // If the ratio of the close button size to tab width exceeds the maximum.
+ close_button_->SetBounds(lb.Width() + kCloseButtonHorzFuzz,
+ close_button_top, close_button_width,
+ close_button_height);
+ close_button_->SetVisible(true);
+ } else {
+ close_button_->SetBounds(0, 0, 0, 0);
+ close_button_->SetVisible(false);
+ }
+
+ // Size the Title text to fill the remaining space.
+ int title_left = favicon_bounds_.right() + kFavIconTitleSpacing;
+ int title_top = kTopPadding + (content_height - title_font_height) / 2;
+
+ // If the user has big fonts, the title will appear rendered too far down on
+ // the y-axis if we use the regular top padding, so we need to adjust it so
+ // that the text appears centered.
+ gfx::Size minimum_size = GetMinimumSize();
+ int text_height = title_top + title_font_height + kBottomPadding;
+ if (text_height > minimum_size.height())
+ title_top -= (text_height - minimum_size.height()) / 2;
+
+ int title_width;
+ if (close_button_->IsVisible()) {
+ title_width = std::max(close_button_->GetX() -
+ kTitleCloseButtonSpacing - title_left, 0);
+ } else {
+ title_width = std::max(lb.Width() - title_left, 0);
+ }
+ if (data_.show_download_icon)
+ title_width = std::max(title_width - download_icon_width, 0);
+ title_bounds_.SetRect(title_left, title_top, title_width, title_font_height);
+
+ // Certain UI elements within the Tab (the favicon, the download icon, etc.)
+ // are not represented as child Views (which is the preferred method).
+ // Instead, these UI elements are drawn directly on the canvas from within
+ // Tab::Paint(). The Tab's child Views (for example, the Tab's close button
+ // which is a ChromeViews::Button instance) are automatically mirrored by the
+ // mirroring infrastructure in ChromeViews. The elements Tab draws directly
+ // on the canvas need to be manually mirrored if the View's layout is
+ // right-to-left.
+ favicon_bounds_.set_x(MirroredLeftPointForRect(favicon_bounds_));
+ title_bounds_.set_x(MirroredLeftPointForRect(title_bounds_));
+ download_icon_bounds_.set_x(MirroredLeftPointForRect(download_icon_bounds_));
+}
+
+void TabRenderer::DidChangeBounds(const CRect& previous,
+ const CRect& current) {
+ Layout();
+}
+
+
+void TabRenderer::OnMouseEntered(const ChromeViews::MouseEvent& e) {
+ hover_animation_->SetTweenType(SlideAnimation::EASE_OUT);
+ hover_animation_->Show();
+}
+
+void TabRenderer::OnMouseExited(const ChromeViews::MouseEvent& e) {
+ hover_animation_->SetTweenType(SlideAnimation::EASE_IN);
+ hover_animation_->Hide();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabRenderer, private
+
+void TabRenderer::PaintInactiveTabBackground(ChromeCanvas* canvas) {
+ bool is_otr = data_.off_the_record;
+ canvas->DrawBitmapInt(is_otr ? *tab_inactive_otr_l : *tab_inactive_l, 0, 0);
+ canvas->TileImageInt(is_otr ? *tab_inactive_otr_c : *tab_inactive_c,
+ tab_inactive_l_width, 0,
+ GetWidth() - tab_inactive_l_width - tab_inactive_r_width,
+ GetHeight());
+ canvas->DrawBitmapInt(is_otr ? *tab_inactive_otr_r : *tab_inactive_r,
+ GetWidth() - tab_inactive_r_width, 0);
+}
+
+void TabRenderer::PaintActiveTabBackground(ChromeCanvas* canvas) {
+ canvas->DrawBitmapInt(*tab_active_l, 0, 0);
+ canvas->TileImageInt(*tab_active_c, tab_active_l_width, 0,
+ GetWidth() - tab_active_l_width - tab_active_r_width, GetHeight());
+ canvas->DrawBitmapInt(*tab_active_r, GetWidth() - tab_active_r_width, 0);
+}
+
+void TabRenderer::PaintHoverTabBackground(ChromeCanvas* canvas,
+ double opacity) {
+ bool is_otr = data_.off_the_record;
+ SkBitmap left = gfx::ImageOperations::CreateBlendedBitmap(
+ (is_otr ? *tab_inactive_otr_l : *tab_inactive_l),
+ *tab_hover_l, opacity);
+ SkBitmap center = gfx::ImageOperations::CreateBlendedBitmap(
+ (is_otr ? *tab_inactive_otr_c : *tab_inactive_c),
+ *tab_hover_c, opacity);
+ SkBitmap right = gfx::ImageOperations::CreateBlendedBitmap(
+ (is_otr ? *tab_inactive_otr_r : *tab_inactive_r),
+ *tab_hover_r, opacity);
+
+ canvas->DrawBitmapInt(left, 0, 0);
+ canvas->TileImageInt(center, tab_active_l_width, 0,
+ GetWidth() - tab_active_l_width - tab_active_r_width, GetHeight());
+ canvas->DrawBitmapInt(right, GetWidth() - tab_active_r_width, 0);
+}
+
+void TabRenderer::PaintLoadingAnimation(ChromeCanvas* canvas) {
+ SkBitmap* frames = (animation_state_ == ANIMATION_WAITING) ?
+ waiting_animation_frames : loading_animation_frames;
+ int image_size = frames->height();
+ int image_offset = animation_frame_ * image_size;
+ int dst_y = (GetHeight() - image_size) / 2;
+
+ // Just like with the Tab's title and favicon, the position for the page
+ // loading animation also needs to be mirrored if the View's UI layout is
+ // right-to-left.
+ int dst_x;
+ if (UILayoutIsRightToLeft()) {
+ dst_x = GetWidth() - kLeftPadding - image_size;
+ } else {
+ dst_x = kLeftPadding;
+ }
+ canvas->DrawBitmapInt(*frames, image_offset, 0, image_size,
+ image_size, dst_x, dst_y, image_size, image_size,
+ false);
+}
+
+int TabRenderer::IconCapacity() const {
+ if (GetHeight() < GetMinimumSize().height()) {
+ return 0;
+ }
+ return (GetWidth() - kLeftPadding - kRightPadding) / kFaviconSize;
+}
+
+bool TabRenderer::ShouldShowIcon() const {
+ if (!data_.show_icon) {
+ return false;
+ } else if (IsSelected()) {
+ // The selected tab clips favicon before close button.
+ return IconCapacity() >= 2;
+ }
+ // Non-selected tabs clip close button before favicon.
+ return IconCapacity() >= 1;
+}
+
+bool TabRenderer::ShouldShowCloseBox() const {
+ // The selected tab never clips close button.
+ return IsSelected() || IconCapacity() >= 3;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabRenderer, private:
+
+void TabRenderer::StartCrashAnimation() {
+ if (!crash_animation_)
+ crash_animation_ = new FavIconCrashAnimation(this);
+ crash_animation_->Reset();
+ crash_animation_->Start();
+}
+
+void TabRenderer::StopCrashAnimation() {
+ if (!crash_animation_)
+ return;
+ crash_animation_->Stop();
+}
+
+bool TabRenderer::IsPerformingCrashAnimation() const {
+ return crash_animation_ && crash_animation_->IsAnimating();
+}
+
+void TabRenderer::SetFavIconHidingOffset(int offset) {
+ fav_icon_hiding_offset_ = offset;
+ SchedulePaint();
+}
+
+void TabRenderer::DisplayCrashedFavIcon() {
+ should_display_crashed_favicon_ = true;
+}
+
+void TabRenderer::ResetCrashedFavIcon() {
+ should_display_crashed_favicon_ = false;
+}