diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/tabs/tab_renderer.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_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.cc | 691 |
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; +} |